import cors from "@fastify/cors"; import Fastify from "fastify"; import { env } from "./env.js"; import { aiImportsRoutes } from "./routes/ai-imports.js"; import { analyticsRoutes } from "./routes/analytics.js"; import { domainsRoutes } from "./routes/domains.js"; import { menuCategoriesRoutes } from "./routes/menu-categories.js"; import { menuItemsRoutes } from "./routes/menu-items.js"; import { menusRoutes } from "./routes/menus.js"; import { meRoutes } from "./routes/me.js"; import { qrRoutes } from "./routes/qr.js"; import { qrTemplatesRoutes } from "./routes/qr-templates.js"; import { restaurantsRoutes } from "./routes/restaurants.js"; import { subscriptionRoutes } from "./routes/subscription.js"; // Default Fastify bodyLimit is 1 MiB — a base64-encoded menu photo from a // phone camera routinely exceeds that, so AI import uploads need real headroom. const app = Fastify({ logger: true, bodyLimit: 20 * 1024 * 1024 }); await app.register(cors); // Clients (mobile fetch/axios in particular) commonly send // `Content-Type: application/json` on bodyless POSTs — treat an empty body // as `{}` instead of the default 400. app.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => { if (!body) { done(null, {}); return; } try { done(null, JSON.parse(body as string)); } catch (err) { done(err as Error, undefined); } }); app.get("/", async () => ({ name: "menul.io API", status: "ok", version: "0.0.1" })); app.get("/health", async () => ({ status: "ok" })); await app.register(meRoutes); await app.register(restaurantsRoutes); await app.register(menusRoutes); await app.register(menuCategoriesRoutes); await app.register(menuItemsRoutes); await app.register(qrRoutes); await app.register(qrTemplatesRoutes); await app.register(aiImportsRoutes); await app.register(domainsRoutes); await app.register(analyticsRoutes); await app.register(subscriptionRoutes); app .listen({ port: env.PORT, host: "0.0.0.0" }) .catch((err) => { app.log.error(err); process.exit(1); });