Files
Cibello-app/apps/api/src/server.ts
T
Sven (AAMOS AI) 4687f46384 Phase 1 ops core: base blocks, feedback, canary fixes
- Add app/ekonomi/anvandare/prenumerationer/butik/feedback blocks to computeOpsSummary.
- New feedback table, POST /api/feedback, GDPR erasure wiring.
- Canary: only count unverified/rejected published recipes; treat verified/editorial as safe.
- Add 'rejected' recipe verification status enum value.
- Fix food-safety lint baseline: remove egg/cured fish from raw-protein list, relax
  genomstekt regex, add safe-cooking phrases to seed recipes.
- Seed recipes now get verificationStatus=editorial.
- Tests: ops summary blocks, feedback route, me.residual cleanup.
2026-08-10 06:35:51 +07:00

108 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Fastify from "fastify";
import cors from "@fastify/cors";
import rateLimit from "@fastify/rate-limit";
import type { AppConfig } from "./config.js";
import { corePlugin } from "./plugins/core.js";
import { authPlugin } from "./plugins/auth.js";
import { storagePlugin } from "./plugins/storage.js";
import { healthRoutes } from "./routes/health.js";
import { authRoutes } from "./routes/auth.js";
import { meRoutes } from "./routes/me.js";
import { householdRoutes } from "./routes/households.js";
import { inventoryRoutes } from "./routes/inventory.js";
import { scanRoutes } from "./routes/scans.js";
import { scanDiffRoutes } from "./routes/scan-diff.js";
import { recipeRoutes } from "./routes/recipes.js";
import { cookingSessionRoutes } from "./routes/cooking-sessions.js";
import { mealRoutes } from "./routes/meals.js";
import { shoppingRoutes } from "./routes/shopping.js";
import { planningRoutes } from "./routes/planning.js";
import { recommendationRoutes } from "./routes/recommendations.js";
import { reconciliationRoutes } from "./routes/reconciliations.js";
import { memoryRoutes } from "./routes/memory.js";
import { budgetRoutes } from "./routes/budget.js";
import { subscriptionRoutes } from "./routes/subscriptions.js";
import { communityRoutes } from "./routes/community.js";
import { adminRoutes } from "./routes/admin.js";
import { adminAnalyticsRoutes } from "./routes/admin-analytics.js";
import { adminReleaseGateRoutes } from "./routes/admin-release-gates.js";
import { adminWorkersRoutes } from "./routes/admin-workers.js";
import { analyticsRoutes } from "./routes/analytics.js";
import { onboardingRoutes } from "./routes/onboarding.js";
import { activationRoutes } from "./routes/activation.js";
import { opsRoutes } from "./routes/ops.js";
import { feedbackRoutes } from "./routes/feedback.js";
declare module "fastify" {
interface FastifyInstance {
routeCatalog: Array<{ method: string; url: string }>;
}
}
export async function buildServer(config: AppConfig) {
const app = Fastify({
logger: {
level: config.LOG_LEVEL,
redact: ["req.headers.authorization", "req.headers.cookie"],
},
trustProxy: true,
bodyLimit: 1024 * 1024,
});
// Endpointkatalog för /docs hooken måste ligga före route-registreringen.
const routeCatalog: Array<{ method: string; url: string }> = [];
app.decorate("routeCatalog", routeCatalog);
app.addHook("onRoute", (route) => {
const methods = Array.isArray(route.method) ? route.method : [route.method];
for (const method of methods) {
if (method === "HEAD" || method === "OPTIONS") continue;
routeCatalog.push({ method, url: route.url });
}
});
await app.register(corePlugin, { config });
await app.register(cors, {
origin: config.CORS_ORIGINS.split(",").map((o) => o.trim()),
credentials: true,
});
await app.register(rateLimit, {
global: true,
// Överstyrbar för lasttest (scripts/loadtest.mjs) produktion använder default 300/min.
max: Number(process.env.RATE_LIMIT_MAX ?? 300),
timeWindow: "1 minute",
keyGenerator: (req) => `${req.ip}`,
});
await app.register(authPlugin);
await app.register(storagePlugin);
await app.register(healthRoutes);
await app.register(authRoutes);
await app.register(meRoutes);
await app.register(householdRoutes);
await app.register(inventoryRoutes);
await app.register(scanRoutes);
await app.register(scanDiffRoutes);
await app.register(recipeRoutes);
await app.register(cookingSessionRoutes);
await app.register(mealRoutes);
await app.register(shoppingRoutes);
await app.register(planningRoutes);
await app.register(recommendationRoutes);
await app.register(reconciliationRoutes);
await app.register(memoryRoutes);
await app.register(budgetRoutes);
await app.register(subscriptionRoutes);
await app.register(communityRoutes);
await app.register(adminRoutes);
await app.register(adminAnalyticsRoutes);
await app.register(adminReleaseGateRoutes);
await app.register(adminWorkersRoutes);
await app.register(analyticsRoutes);
await app.register(onboardingRoutes);
await app.register(activationRoutes);
await app.register(opsRoutes);
await app.register(feedbackRoutes);
return app;
}