Files
Tiktok-Rezepte/backend/routes/recipes.ts
2026-11-01 20:14:25 +01:00

118 lines
4.0 KiB
TypeScript

/// <reference path="../types.d.ts" />
import { Router, Request, Response } from 'express';
import {
getAllRecipes, getRecipeById, createRecipe,
updateRecipe, deleteRecipe, getCategories, getDb
} from '../db.js';
import { getTikTokData } from '../services/tiktok.js';
import { isAiAvailable, extractRecipe } from '../services/ai.js';
import { trackEvent } from '../analytics-db.js';
const router = Router();
router.get('/', (req: Request, res: Response) => {
const { search, category, user_id } = req.query as { search?: string; category?: string; user_id?: string };
const recipes = getAllRecipes(search, category, user_id);
res.json(recipes.map(r => ({
...r,
ingredients: JSON.parse(r.ingredients || '[]'),
steps: JSON.parse(r.steps || '[]'),
})));
});
router.get('/categories', (req: Request, res: Response) => {
res.json(getCategories());
});
router.get('/authors', (req: Request, res: Response) => {
const db = getDb();
const recipes = db.prepare('SELECT DISTINCT user_id FROM recipes WHERE user_id IS NOT NULL').all() as { user_id: number }[];
const authorIds = recipes.map(r => r.user_id);
const authors = authorIds.map(id => {
const user = db.prepare('SELECT id, username FROM users WHERE id = ?').get(id) as { id: number; username: string } | undefined;
return { id, username: user ? user.username : `User #${id}` };
}).sort((a, b) => a.username.localeCompare(b.username));
res.json(authors);
});
router.get('/ai-status', async (req: Request, res: Response) => {
const available = await isAiAvailable();
res.json({ ai: available });
});
router.get('/:id', (req: Request, res: Response) => {
const recipe = getRecipeById(req.params.id as string);
if (!recipe) return res.status(404).json({ error: 'Rezept nicht gefunden' });
res.json({
...recipe,
ingredients: JSON.parse(recipe.ingredients || '[]'),
steps: JSON.parse(recipe.steps || '[]'),
});
});
router.post('/', (req: Request, res: Response) => {
const recipe = createRecipe(req.body);
const userId = req.body.user_id || null;
trackEvent('recipe_created', userId, { recipe_id: recipe.id, recipe_name: recipe.name });
res.status(201).json(recipe);
});
router.put('/:id', (req: Request, res: Response) => {
const existing = getRecipeById(req.params.id as string);
if (!existing) return res.status(404).json({ error: 'Rezept nicht gefunden' });
const updated = updateRecipe(req.params.id as string, req.body);
trackEvent('recipe_updated', req.body.user_id || null, { recipe_id: Number(req.params.id), recipe_name: updated?.name });
res.json(updated);
});
router.delete('/:id', (req: Request, res: Response) => {
const existing = getRecipeById(req.params.id as string);
if (!existing) return res.status(404).json({ error: 'Rezept nicht gefunden' });
deleteRecipe(req.params.id as string);
trackEvent('recipe_deleted', null, { recipe_id: Number(req.params.id) });
res.json({ success: true });
});
router.post('/from-tiktok', async (req: Request, res: Response) => {
try {
const { url } = req.body;
if (!url) return res.status(400).json({ error: 'URL erforderlich' });
const tiktokData = await getTikTokData(url);
const aiAvailable = await isAiAvailable();
let extracted = null;
let aiStatus = aiAvailable ? 'available' : 'unavailable';
let aiMessage = '';
if (aiAvailable && tiktokData.title) {
try {
extracted = await extractRecipe(tiktokData.title);
aiStatus = 'success';
} catch (e: any) {
console.error('KI Extraktion fehlgeschlagen:', e.message);
aiStatus = 'error';
aiMessage = e.message;
}
} else if (!aiAvailable) {
aiMessage = 'KI-Service nicht erreichbar';
} else if (!tiktokData.title) {
aiMessage = 'Kein Titel im Video gefunden';
}
res.json({
tiktok: tiktokData,
ai: aiAvailable,
aiStatus,
aiMessage,
extracted,
});
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
export default router;