import path from "node:path"; import fs from "node:fs"; import Fastify, { type FastifyInstance } from "fastify"; import cookie from "@fastify/cookie"; import multipart from "@fastify/multipart"; import fastifyStatic from "@fastify/static"; import type { AppConfig } from "./config.js"; import { rootDir } from "./config.js"; import { openDb, type Db } from "./db.js"; import { registerRoutes } from "./routes.js"; import { startWorker } from "./worker.js"; import { createRunComfyProvider } from "./ai/runcomfy.js"; import { createHttpBaseAdapter } from "./integration/baseAdapter.js"; import { getAppSetting } from "./domain/settings.js"; import { seedIfEmpty } from "./seed/oskar.js"; import type { ImageProvider } from "./ai/provider.js"; export interface BuiltApp { app: FastifyInstance; db: Db; worker: { stop: () => Promise }; } export async function buildApp(cfg: AppConfig): Promise { for (const dir of [cfg.dataDir, cfg.originalsDir, cfg.derivedDir]) { fs.mkdirSync(dir, { recursive: true }); } const db = openDb(cfg.dbPath); const seed = await seedIfEmpty(db, cfg); if (seed.seeded) console.log("[vilmal] seed:", JSON.stringify(seed.detail)); const provider: ImageProvider | null = cfg.runcomfyApiKey ? createRunComfyProvider({ apiKey: cfg.runcomfyApiKey, baseUrl: cfg.runcomfyBaseUrl, modelId: cfg.runcomfyModelId, }) : null; const app = Fastify({ logger: cfg.env !== "test", bodyLimit: cfg.maxUploadBytes }); await app.register(cookie, cfg.sessionSecret ? { secret: cfg.sessionSecret } : {}); await app.register(multipart, { limits: { fileSize: cfg.maxUploadBytes, files: 30 }, }); // Token z panelu (app_settings) ma pierwszeństwo przed env — zapis // w Ustawieniach działa bez restartu; brak tokenu → ExportBlocked. const baseAdapter = createHttpBaseAdapter(cfg, () => getAppSetting(db, "baselinker.api_token")); registerRoutes(app, db, cfg, { baseAdapter }); // produkcja: serwuj zbudowany frontend (web/dist względem katalogu projektu, // nie względem dataDir — dirname(dataDir) + ".." wyłaziło poza repo) const webDist = path.resolve(rootDir, "web", "dist"); if (cfg.env === "production" && fs.existsSync(webDist)) { await app.register(fastifyStatic, { root: webDist }); app.setNotFoundHandler((req, reply) => { if (req.url.startsWith("/api/")) return reply.code(404).send({ error: "Not found" }); return reply.sendFile("index.html"); }); } const worker = startWorker(db, cfg, provider, undefined, baseAdapter); app.addHook("onClose", async () => worker.stop()); return { app, db, worker }; }