Sieht gut aus und geht (noch keine Bildeingabe)

This commit is contained in:
rxf
2025-09-22 09:41:01 +02:00
parent 6f93db4a12
commit 6d04ab93c0
79 changed files with 16233 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=health.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA8BxB,eAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const router = (0, express_1.Router)();
router.get('/', (req, res) => {
res.json({
success: true,
message: 'Rezepte Klaus API is running!',
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV,
});
});
router.get('/db', async (req, res) => {
try {
res.json({
success: true,
message: 'Database connection is healthy',
timestamp: new Date().toISOString(),
});
}
catch (error) {
res.status(500).json({
success: false,
message: 'Database connection failed',
error: error instanceof Error ? error.message : 'Unknown error',
});
}
});
exports.default = router;
//# sourceMappingURL=health.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AAEpD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAGxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC9C,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,+BAA+B;QACxC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ;KAClC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAGH,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IACtD,IAAI,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC;YACP,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,gCAAgC;YACzC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,4BAA4B;YACrC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SAChE,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=images.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/routes/images.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAuGxB,eAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,95 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const client_1 = require("@prisma/client");
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const router = (0, express_1.Router)();
const prisma = new client_1.PrismaClient();
router.get('/recipe/:recipeId', async (req, res, next) => {
try {
const { recipeId } = req.params;
if (!recipeId) {
return res.status(400).json({
success: false,
message: 'Recipe ID is required',
});
}
const images = await prisma.recipeImage.findMany({
where: { recipeId: parseInt(recipeId) },
orderBy: { id: 'asc' }
});
return res.json({
success: true,
data: images,
});
}
catch (error) {
next(error);
}
});
router.get('/serve/:imagePath(*)', (req, res, next) => {
try {
const imagePath = req.params.imagePath;
if (!imagePath) {
return res.status(400).json({
success: false,
message: 'Image path is required',
});
}
const cleanPath = imagePath.replace(/^uploads\//, '');
const fullPath = path_1.default.join(process.cwd(), '../../uploads', cleanPath);
console.log(`Serving image: ${imagePath} -> ${fullPath}`);
if (!fs_1.default.existsSync(fullPath)) {
console.log(`Image not found: ${fullPath}`);
return res.status(404).json({
success: false,
message: 'Image not found',
requestedPath: imagePath,
resolvedPath: fullPath
});
}
res.set({
'Access-Control-Allow-Origin': 'http://localhost:5173',
'Access-Control-Allow-Credentials': 'true',
'Cache-Control': 'public, max-age=31536000',
});
return res.sendFile(path_1.default.resolve(fullPath));
}
catch (error) {
console.error('Error serving image:', error);
next(error);
}
});
router.get('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Image ID is required',
});
}
const image = await prisma.recipeImage.findUnique({
where: { id: parseInt(id) }
});
if (!image) {
return res.status(404).json({
success: false,
message: 'Image not found',
});
}
return res.json({
success: true,
data: image,
});
}
catch (error) {
next(error);
}
});
exports.default = router;
//# sourceMappingURL=images.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"images.js","sourceRoot":"","sources":["../../src/routes/images.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=ingredients.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ingredients.d.ts","sourceRoot":"","sources":["../../src/routes/ingredients.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA0LxB,eAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,159 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const client_1 = require("@prisma/client");
const joi_1 = __importDefault(require("joi"));
const router = (0, express_1.Router)();
const prisma = new client_1.PrismaClient();
const ingredientSchema = joi_1.default.object({
recipeNumber: joi_1.default.string().required().max(20),
ingredients: joi_1.default.string().required(),
});
const updateIngredientSchema = ingredientSchema.fork(['recipeNumber'], (schema) => schema.optional());
router.get('/', async (req, res, next) => {
try {
const { search = '', category = '', page = '1', limit = '10', sortBy = 'recipeNumber', sortOrder = 'asc' } = req.query;
const pageNum = parseInt(page);
const limitNum = parseInt(limit);
const skip = (pageNum - 1) * limitNum;
const where = {};
if (search) {
where.OR = [
{ recipeNumber: { contains: search } },
{ ingredients: { contains: search } },
];
}
if (category) {
where.recipeNumber = { contains: category };
}
const [ingredients, total] = await Promise.all([
prisma.ingredient.findMany({
where,
orderBy: { [sortBy]: sortOrder },
skip,
take: limitNum,
}),
prisma.ingredient.count({ where })
]);
return res.json({
success: true,
data: ingredients,
pagination: {
page: pageNum,
limit: limitNum,
total,
pages: Math.ceil(total / limitNum),
},
});
}
catch (error) {
next(error);
}
});
router.get('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Ingredient ID is required',
});
}
const ingredient = await prisma.ingredient.findUnique({
where: { id: parseInt(id) }
});
if (!ingredient) {
return res.status(404).json({
success: false,
message: 'Ingredient not found',
});
}
return res.json({
success: true,
data: ingredient,
});
}
catch (error) {
next(error);
}
});
router.post('/', async (req, res, next) => {
try {
const { error, value } = ingredientSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: 'Validation error',
details: error.details,
});
}
const ingredient = await prisma.ingredient.create({
data: value
});
return res.status(201).json({
success: true,
data: ingredient,
message: 'Ingredient created successfully',
});
}
catch (error) {
next(error);
}
});
router.put('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Ingredient ID is required',
});
}
const { error, value } = updateIngredientSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: 'Validation error',
details: error.details,
});
}
const ingredient = await prisma.ingredient.update({
where: { id: parseInt(id) },
data: value
});
return res.json({
success: true,
data: ingredient,
message: 'Ingredient updated successfully',
});
}
catch (error) {
next(error);
}
});
router.delete('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Ingredient ID is required',
});
}
await prisma.ingredient.delete({
where: { id: parseInt(id) }
});
return res.json({
success: true,
message: 'Ingredient deleted successfully',
});
}
catch (error) {
next(error);
}
});
exports.default = router;
//# sourceMappingURL=ingredients.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ingredients.js","sourceRoot":"","sources":["../../src/routes/ingredients.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=recipes.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../../src/routes/recipes.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA4PxB,eAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,212 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const client_1 = require("@prisma/client");
const joi_1 = __importDefault(require("joi"));
const router = (0, express_1.Router)();
const prisma = new client_1.PrismaClient();
const recipeSchema = joi_1.default.object({
recipeNumber: joi_1.default.string().optional().allow(''),
title: joi_1.default.string().required().min(1).max(255),
description: joi_1.default.string().optional().allow(''),
category: joi_1.default.string().optional().allow(''),
preparation: joi_1.default.string().optional().allow(''),
servings: joi_1.default.number().integer().min(1).default(1),
ingredients: joi_1.default.string().optional().allow(''),
instructions: joi_1.default.string().optional().allow(''),
comment: joi_1.default.string().optional().allow(''),
});
const updateRecipeSchema = recipeSchema;
router.get('/', async (req, res, next) => {
try {
const { search = '', category = '', page = '1', limit = '10', sortBy = 'title', sortOrder = 'asc' } = req.query;
const pageNum = parseInt(page);
const limitNum = parseInt(limit);
const skip = (pageNum - 1) * limitNum;
const where = {};
if (search) {
where.OR = [
{ title: { contains: search } },
{ description: { contains: search } },
{ ingredients: { contains: search } },
];
}
if (category) {
where.category = { contains: category };
}
const [recipes, total] = await Promise.all([
prisma.recipe.findMany({
where,
include: {
images: true,
ingredientsList: true,
},
orderBy: { [sortBy]: sortOrder },
skip,
take: limitNum,
}),
prisma.recipe.count({ where })
]);
return res.json({
success: true,
data: recipes,
pagination: {
page: pageNum,
limit: limitNum,
total,
pages: Math.ceil(total / limitNum),
},
});
}
catch (error) {
next(error);
}
});
router.get('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Recipe ID is required',
});
}
const recipe = await prisma.recipe.findUnique({
where: { id: parseInt(id) },
include: {
images: true,
ingredientsList: true,
}
});
if (!recipe) {
return res.status(404).json({
success: false,
message: 'Recipe not found',
});
}
if ((!recipe.ingredients || recipe.ingredients.trim() === '' || recipe.ingredients.length < 10) && recipe.recipeNumber) {
try {
const paddedNumber = recipe.recipeNumber.padStart(3, '0');
const recipeNumberWithR = `R${paddedNumber}`;
const separateIngredients = await prisma.ingredient.findFirst({
where: { recipeNumber: recipeNumberWithR }
});
if (separateIngredients && separateIngredients.ingredients) {
recipe.ingredients = separateIngredients.ingredients;
}
}
catch (ingredientError) {
console.log(`Could not load separate ingredients for recipe ${recipe.recipeNumber}:`, ingredientError);
}
}
return res.json({
success: true,
data: recipe,
});
}
catch (error) {
next(error);
}
});
router.post('/', async (req, res, next) => {
try {
const { error, value } = recipeSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: 'Validation error',
details: error.details,
});
}
if (!value.recipeNumber || value.recipeNumber.trim() === '') {
const lastRecipe = await prisma.recipe.findFirst({
orderBy: { id: 'desc' },
select: { recipeNumber: true }
});
let nextNumber = 1;
if (lastRecipe?.recipeNumber) {
const match = lastRecipe.recipeNumber.match(/\d+/);
if (match) {
nextNumber = parseInt(match[0]) + 1;
}
}
value.recipeNumber = `R${nextNumber.toString().padStart(3, '0')}`;
}
const recipe = await prisma.recipe.create({
data: value,
include: {
images: true,
ingredientsList: true,
}
});
return res.status(201).json({
success: true,
data: recipe,
message: 'Recipe created successfully',
});
}
catch (error) {
next(error);
}
});
router.put('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Recipe ID is required',
});
}
const { error, value } = updateRecipeSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: 'Validation error',
details: error.details,
});
}
const recipe = await prisma.recipe.update({
where: { id: parseInt(id) },
data: value,
include: {
images: true,
ingredientsList: true,
}
});
return res.json({
success: true,
data: recipe,
message: 'Recipe updated successfully',
});
}
catch (error) {
next(error);
}
});
router.delete('/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id) {
return res.status(400).json({
success: false,
message: 'Recipe ID is required',
});
}
await prisma.recipe.delete({
where: { id: parseInt(id) }
});
return res.json({
success: true,
message: 'Recipe deleted successfully',
});
}
catch (error) {
next(error);
}
});
exports.default = router;
//# sourceMappingURL=recipes.js.map

File diff suppressed because one or more lines are too long