Files
Tiktok-Rezepte/backend/db.js
2026-08-26 16:17:54 +02:00

222 lines
6.6 KiB
JavaScript

import { readFileSync, writeFileSync, existsSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const DB_FILE = process.env.DB_PATH || join(__dirname, 'data.json');
function loadDB() {
if (existsSync(DB_FILE)) {
try {
return JSON.parse(readFileSync(DB_FILE, 'utf-8'));
} catch (e) {
console.error('DB Datei korrupt, Backup erstellt:', e.message);
try { writeFileSync(DB_FILE + '.bak', readFileSync(DB_FILE, 'utf-8')); } catch {}
}
}
return { users: [], recipes: [], invite_tokens: [], _counters: { users: 0, recipes: 0, invite_tokens: 0 } };
}
function saveDB(data) {
try {
writeFileSync(DB_FILE, JSON.stringify(data, null, 2));
} catch (e) {
console.error('DB Schreibfehler:', e.message);
}
}
let db = loadDB();
function nextId(table) {
db._counters[table] = (db._counters[table] || 0) + 1;
return db._counters[table];
}
function now() {
return new Date().toISOString().replace('T', ' ').slice(0, 19);
}
export function getDb() {
return db;
}
export function getAllRecipes(search, category, userId) {
let results = [...db.recipes];
if (search) {
const term = search.toLowerCase();
results = results.filter(r =>
(r.name && r.name.toLowerCase().includes(term)) ||
(r.description && r.description.toLowerCase().includes(term)) ||
(r.ingredients && r.ingredients.toLowerCase().includes(term))
);
}
if (category && category !== 'Alle') {
results = results.filter(r => r.category === category);
}
if (userId) {
results = results.filter(r => r.user_id === Number(userId));
}
results.sort((a, b) => (b.created_at || '').localeCompare(a.created_at || ''));
return results;
}
export function getRecipeById(id) {
return db.recipes.find(r => r.id === Number(id)) || undefined;
}
export function createRecipe(recipe) {
const id = nextId('recipes');
const nowStr = now();
const row = {
id,
name: recipe.name,
description: recipe.description || '',
ingredients: JSON.stringify(recipe.ingredients || []),
steps: JSON.stringify(recipe.steps || []),
category: recipe.category || 'Sonstiges',
source: recipe.source || 'tiktok',
source_url: recipe.source_url || recipe.tiktok_url || null,
source_author: recipe.source_author || recipe.tiktok_author || null,
tiktok_url: recipe.tiktok_url || recipe.source_url || null,
tiktok_author: recipe.tiktok_author || recipe.source_author || null,
thumbnail_url: recipe.thumbnail_url || null,
user_id: recipe.user_id || null,
created_at: nowStr,
updated_at: nowStr,
};
db.recipes.push(row);
saveDB(db);
return row;
}
export function updateRecipe(id, recipe) {
const idx = db.recipes.findIndex(r => r.id === Number(id));
if (idx === -1) return undefined;
db.recipes[idx] = {
...db.recipes[idx],
name: recipe.name,
description: recipe.description || '',
ingredients: JSON.stringify(recipe.ingredients || []),
steps: JSON.stringify(recipe.steps || []),
category: recipe.category || 'Sonstiges',
source: recipe.source || db.recipes[idx].source || 'tiktok',
source_url: recipe.source_url || recipe.tiktok_url || db.recipes[idx].source_url || null,
source_author: recipe.source_author || recipe.tiktok_author || db.recipes[idx].source_author || null,
tiktok_url: recipe.tiktok_url || recipe.source_url || db.recipes[idx].tiktok_url || null,
tiktok_author: recipe.tiktok_author || recipe.source_author || db.recipes[idx].tiktok_author || null,
thumbnail_url: recipe.thumbnail_url || null,
updated_at: now(),
};
saveDB(db);
return db.recipes[idx];
}
export function deleteRecipe(id) {
const idx = db.recipes.findIndex(r => r.id === Number(id));
if (idx === -1) return;
db.recipes.splice(idx, 1);
saveDB(db);
}
export function getCategories() {
const counts = {};
for (const r of db.recipes) {
counts[r.category] = (counts[r.category] || 0) + 1;
}
return Object.entries(counts)
.map(([category, count]) => ({ category, count }))
.sort((a, b) => b.count - a.count);
}
export function getUserByUsername(username) {
return db.users.find(u => u.username === username) || undefined;
}
export function getUserById(id) {
return db.users.find(u => u.id === Number(id)) || undefined;
}
export function createUser(username, hashedPassword, isAdmin = false) {
const id = nextId('users');
const user = {
id,
username,
password: hashedPassword,
is_admin: isAdmin ? 1 : 0,
created_at: now(),
};
db.users.push(user);
saveDB(db);
return { id, username, is_admin: user.is_admin, created_at: user.created_at };
}
export function updateUserCredentials(userId, { username, password }) {
const user = db.users.find(u => u.id === Number(userId));
if (!user) return null;
if (username) user.username = username;
if (password) user.password = password;
saveDB(db);
return { id: user.id, username: user.username, is_admin: user.is_admin, created_at: user.created_at };
}
export function createInviteToken(token, createdBy) {
const id = nextId('invite_tokens');
db.invite_tokens.push({
id,
token,
created_by: createdBy,
used_by: null,
created_at: now(),
used_at: null,
});
saveDB(db);
}
export function getInviteToken(token) {
return db.invite_tokens.find(i => i.token === token) || undefined;
}
export function useInviteToken(token, userId) {
const inv = db.invite_tokens.find(i => i.token === token);
if (inv) {
inv.used_by = userId;
inv.used_at = now();
saveDB(db);
}
}
export function getInvitesByUser(userId) {
return db.invite_tokens
.filter(i => i.created_by === Number(userId))
.sort((a, b) => (b.created_at || '').localeCompare(a.created_at || ''))
.map(i => {
const usedByUser = i.used_by ? db.users.find(u => u.id === i.used_by) : null;
return { ...i, used_by_username: usedByUser ? usedByUser.username : null };
});
}
export function getAllUsers() {
return db.users
.map(u => ({ id: u.id, username: u.username, is_admin: u.is_admin, created_at: u.created_at }))
.sort((a, b) => (b.created_at || '').localeCompare(a.created_at || ''));
}
export function deleteUser(id) {
const idx = db.users.findIndex(u => u.id === Number(id));
if (idx === -1) return false;
db.users.splice(idx, 1);
db.invite_tokens = db.invite_tokens.filter(i => i.created_by !== Number(id));
saveDB(db);
return true;
}
export function getInviteStats() {
const total = db.invite_tokens.length;
const used = db.invite_tokens.filter(i => i.used_by !== null).length;
return { total, used, available: total - used };
}