Updates
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Router } from 'express';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import {
|
||||
getOverview, getPageviewsByDay, getTopUsers,
|
||||
getTopRecipes, getRecentActivity, getEvents
|
||||
@@ -7,16 +7,16 @@ import { getUserById } from '../db.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/overview', (req, res) => {
|
||||
router.get('/overview', (req: Request, res: Response) => {
|
||||
res.json(getOverview());
|
||||
});
|
||||
|
||||
router.get('/pageviews', (req, res) => {
|
||||
const { from, to } = req.query;
|
||||
router.get('/pageviews', (req: Request, res: Response) => {
|
||||
const { from, to } = req.query as { from?: string; to?: string };
|
||||
res.json(getPageviewsByDay(from, to));
|
||||
});
|
||||
|
||||
router.get('/users', (req, res) => {
|
||||
router.get('/users', (req: Request, res: Response) => {
|
||||
const topUsers = getTopUsers(20);
|
||||
const enriched = topUsers.map(u => {
|
||||
const user = getUserById(u.user_id);
|
||||
@@ -25,12 +25,12 @@ router.get('/users', (req, res) => {
|
||||
res.json(enriched);
|
||||
});
|
||||
|
||||
router.get('/recipes', (req, res) => {
|
||||
router.get('/recipes', (req: Request, res: Response) => {
|
||||
res.json(getTopRecipes(20));
|
||||
});
|
||||
|
||||
router.get('/activity', (req, res) => {
|
||||
const { user_id, limit } = req.query;
|
||||
router.get('/activity', (req: Request, res: Response) => {
|
||||
const { user_id, limit } = req.query as { user_id?: string; limit?: string };
|
||||
const events = getEvents({ user_id, limit: limit ? Number(limit) : 50 });
|
||||
const enriched = events.map(e => {
|
||||
const user = e.user_id ? getUserById(e.user_id) : null;
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Router } from 'express';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import crypto from 'crypto';
|
||||
import {
|
||||
getUserByUsername, getUserById, createUser, updateUserCredentials, deleteUser,
|
||||
createInviteToken, getInviteToken, useInviteToken,
|
||||
getInvitesByUser, getAllUsers, getInviteStats
|
||||
getInvitesByUser, getAllUsers, getInviteStats, User
|
||||
} from '../db.js';
|
||||
import { authMiddleware, adminMiddleware } from '../middleware.js';
|
||||
import { authMiddleware, adminMiddleware, AuthenticatedRequest } from '../middleware.js';
|
||||
import { trackEvent } from '../analytics-db.js';
|
||||
|
||||
const router = Router();
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'tiktok-rezepte-secret-key-change-in-production';
|
||||
const JWT_EXPIRES_IN = '7d';
|
||||
|
||||
router.post('/register', async (req, res) => {
|
||||
router.post('/register', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { username, password, inviteToken } = req.body;
|
||||
|
||||
@@ -57,7 +57,7 @@ router.post('/register', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/login', async (req, res) => {
|
||||
router.post('/login', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
|
||||
@@ -70,7 +70,7 @@ router.post('/login', async (req, res) => {
|
||||
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
|
||||
}
|
||||
|
||||
const valid = await bcrypt.compare(password, user.password);
|
||||
const valid = await bcrypt.compare(password, user.password!);
|
||||
if (!valid) {
|
||||
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
|
||||
}
|
||||
@@ -88,14 +88,14 @@ router.post('/login', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/me', (req, res) => {
|
||||
router.get('/me', (req: Request, res: Response) => {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ error: 'Nicht authentifiziert' });
|
||||
}
|
||||
try {
|
||||
const token = authHeader.split(' ')[1];
|
||||
const decoded = jwt.verify(token, JWT_SECRET);
|
||||
const decoded = jwt.verify(token, JWT_SECRET) as { username: string };
|
||||
const user = getUserByUsername(decoded.username);
|
||||
if (!user) return res.status(401).json({ error: 'Benutzer nicht gefunden' });
|
||||
res.json({ id: user.id, username: user.username, is_admin: !!user.is_admin, created_at: user.created_at });
|
||||
@@ -104,15 +104,15 @@ router.get('/me', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/invite', authMiddleware, adminMiddleware, (req, res) => {
|
||||
router.post('/invite', authMiddleware, adminMiddleware, (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { count = 1 } = req.body;
|
||||
const amount = Math.min(Math.max(parseInt(count) || 1, 1), 20);
|
||||
const tokens = [];
|
||||
const amount = Math.min(Math.max(parseInt(count as string) || 1, 1), 20);
|
||||
const tokens: string[] = [];
|
||||
|
||||
for (let i = 0; i < amount; i++) {
|
||||
const token = crypto.randomBytes(16).toString('hex');
|
||||
createInviteToken(token, req.user.id);
|
||||
createInviteToken(token, req.user!.id);
|
||||
tokens.push(token);
|
||||
}
|
||||
|
||||
@@ -122,9 +122,9 @@ router.post('/invite', authMiddleware, adminMiddleware, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/invites', authMiddleware, adminMiddleware, (req, res) => {
|
||||
router.get('/invites', authMiddleware, adminMiddleware, (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const invites = getInvitesByUser(req.user.id);
|
||||
const invites = getInvitesByUser(req.user!.id);
|
||||
const stats = getInviteStats();
|
||||
res.json({ invites, stats });
|
||||
} catch (error) {
|
||||
@@ -132,7 +132,7 @@ router.get('/invites', authMiddleware, adminMiddleware, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/users', authMiddleware, adminMiddleware, (req, res) => {
|
||||
router.get('/users', authMiddleware, adminMiddleware, (req: Request, res: Response) => {
|
||||
try {
|
||||
const users = getAllUsers();
|
||||
res.json(users);
|
||||
@@ -141,10 +141,10 @@ router.get('/users', authMiddleware, adminMiddleware, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/users/:id', authMiddleware, adminMiddleware, (req, res) => {
|
||||
router.delete('/users/:id', authMiddleware, adminMiddleware, (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const userId = Number(req.params.id);
|
||||
if (userId === req.user.id) {
|
||||
if (userId === req.user!.id) {
|
||||
return res.status(400).json({ error: 'Du kannst dich nicht selbst löschen' });
|
||||
}
|
||||
const user = getUserById(userId);
|
||||
@@ -156,15 +156,15 @@ router.delete('/users/:id', authMiddleware, adminMiddleware, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/credentials', authMiddleware, async (req, res) => {
|
||||
router.put('/credentials', authMiddleware, async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { currentPassword, newUsername, newPassword } = req.body;
|
||||
|
||||
const user = getUserByUsername(req.user.username);
|
||||
const user = getUserByUsername(req.user!.username);
|
||||
if (!user) return res.status(404).json({ error: 'Benutzer nicht gefunden' });
|
||||
|
||||
if (currentPassword) {
|
||||
const valid = await bcrypt.compare(currentPassword, user.password);
|
||||
const valid = await bcrypt.compare(currentPassword, user.password!);
|
||||
if (!valid) return res.status(401).json({ error: 'Aktuelles Passwort ist falsch' });
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ router.put('/credentials', authMiddleware, async (req, res) => {
|
||||
if (exists) return res.status(409).json({ error: 'Benutzername ist bereits vergeben' });
|
||||
}
|
||||
|
||||
const updates = {};
|
||||
const updates: { username?: string; password?: string } = {};
|
||||
if (newUsername) updates.username = newUsername;
|
||||
if (newPassword) {
|
||||
if (newPassword.length < 6) return res.status(400).json({ error: 'Neues Passwort muss mindestens 6 Zeichen lang sein' });
|
||||
@@ -185,9 +185,11 @@ router.put('/credentials', authMiddleware, async (req, res) => {
|
||||
}
|
||||
|
||||
const updated = updateUserCredentials(user.id, updates);
|
||||
if (!updated) return res.status(500).json({ error: 'Aktualisierung fehlgeschlagen' });
|
||||
|
||||
const token = jwt.sign({ id: updated.id, username: updated.username }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
|
||||
|
||||
res.json({ user: updated, token });
|
||||
res.json({ user: { id: updated.id, username: updated.username, is_admin: !!updated.is_admin, created_at: updated.created_at }, token });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Fehler beim Aktualisieren' });
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Router } from 'express';
|
||||
/// <reference path="../types.d.ts" />
|
||||
import { Router, Request, Response } from 'express';
|
||||
import {
|
||||
getAllRecipes, getRecipeById, createRecipe,
|
||||
updateRecipe, deleteRecipe, getCategories, getDb
|
||||
@@ -9,69 +10,72 @@ import { trackEvent } from '../analytics-db.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
const { search, category, user_id } = req.query;
|
||||
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),
|
||||
ingredients: JSON.parse(r.ingredients || '[]'),
|
||||
steps: JSON.parse(r.steps || '[]'),
|
||||
})));
|
||||
});
|
||||
|
||||
router.get('/categories', (req, res) => {
|
||||
router.get('/categories', (req: Request, res: Response) => {
|
||||
res.json(getCategories());
|
||||
});
|
||||
|
||||
router.get('/authors', (req, res) => {
|
||||
router.get('/authors', (req: Request, res: Response) => {
|
||||
const db = getDb();
|
||||
const authorIds = [...new Set(db.recipes.filter(r => r.user_id).map(r => r.user_id))];
|
||||
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.users.find(u => u.id === 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, res) => {
|
||||
router.get('/ai-status', async (req: Request, res: Response) => {
|
||||
const available = await isAiAvailable();
|
||||
res.json({ ai: available });
|
||||
});
|
||||
|
||||
router.get('/:id', (req, res) => {
|
||||
const recipe = getRecipeById(req.params.id);
|
||||
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),
|
||||
ingredients: JSON.parse(recipe.ingredients || '[]'),
|
||||
steps: JSON.parse(recipe.steps || '[]'),
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/', (req, res) => {
|
||||
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, res) => {
|
||||
const existing = getRecipeById(req.params.id);
|
||||
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, req.body);
|
||||
trackEvent('recipe_updated', req.body.user_id || null, { recipe_id: Number(req.params.id), recipe_name: updated.name });
|
||||
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, res) => {
|
||||
const existing = getRecipeById(req.params.id);
|
||||
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);
|
||||
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, res) => {
|
||||
router.post('/from-tiktok', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { url } = req.body;
|
||||
if (!url) return res.status(400).json({ error: 'URL erforderlich' });
|
||||
@@ -87,7 +91,7 @@ router.post('/from-tiktok', async (req, res) => {
|
||||
try {
|
||||
extracted = await extractRecipe(tiktokData.title);
|
||||
aiStatus = 'success';
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
console.error('KI Extraktion fehlgeschlagen:', e.message);
|
||||
aiStatus = 'error';
|
||||
aiMessage = e.message;
|
||||
@@ -105,7 +109,7 @@ router.post('/from-tiktok', async (req, res) => {
|
||||
aiMessage,
|
||||
extracted,
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user