diff --git a/jus-ia-start-kit/.gitignore b/jus-ia-start-kit/.gitignore index a71028314..7c4623f2a 100644 --- a/jus-ia-start-kit/.gitignore +++ b/jus-ia-start-kit/.gitignore @@ -2,4 +2,5 @@ node_modules/ dist/ .env src/public/css/app.css +/public/ *.tsbuildinfo diff --git a/jus-ia-start-kit/api/index.ts b/jus-ia-start-kit/api/index.ts new file mode 100644 index 000000000..4942cef40 --- /dev/null +++ b/jus-ia-start-kit/api/index.ts @@ -0,0 +1,22 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; +import { buildApp } from "../src/app.js"; + +let appReady: ReturnType | null = null; + +function getApp() { + if (!appReady) { + appReady = buildApp().then(async (app) => { + await app.ready(); + return app; + }); + } + return appReady; +} + +export default async function handler( + req: IncomingMessage, + res: ServerResponse, +) { + const app = await getApp(); + app.server.emit("request", req, res); +} diff --git a/jus-ia-start-kit/package.json b/jus-ia-start-kit/package.json index 4a76a4959..4c2e1d6bc 100644 --- a/jus-ia-start-kit/package.json +++ b/jus-ia-start-kit/package.json @@ -7,9 +7,10 @@ "dev": "concurrently \"npm run dev:server\" \"npm run dev:css\"", "dev:server": "tsx watch --env-file=.env src/server.ts", "dev:css": "npx @tailwindcss/cli -i src/styles/input.css -o src/public/css/app.css --watch", - "build": "npm run build:css && npm run build:server", + "build": "npm run build:css && npm run build:server && npm run build:assets", "build:server": "tsc", "build:css": "npx @tailwindcss/cli -i src/styles/input.css -o src/public/css/app.css --minify", + "build:assets": "cp -r src/public/* public/ 2>/dev/null; cp -r src/templates dist/templates", "start": "node --env-file=.env dist/server.js", "test": "vitest run", "test:watch": "vitest" diff --git a/jus-ia-start-kit/src/app.ts b/jus-ia-start-kit/src/app.ts new file mode 100644 index 000000000..a1b0d8409 --- /dev/null +++ b/jus-ia-start-kit/src/app.ts @@ -0,0 +1,56 @@ +import Fastify from "fastify"; +import fastifyView from "@fastify/view"; +import fastifyStatic from "@fastify/static"; +import fastifyFormbody from "@fastify/formbody"; +import nunjucks from "nunjucks"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { config } from "./config/index.js"; +import { homeRoutes } from "./routes/home.js"; +import { flowRoutes } from "./routes/flow.js"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export async function buildApp() { + const app = Fastify({ + logger: { + level: config.nodeEnv === "production" ? "info" : "debug", + transport: + config.nodeEnv === "development" + ? { target: "pino-pretty", options: { translateTime: "HH:MM:ss" } } + : undefined, + }, + }); + + // Parse form body (application/x-www-form-urlencoded) + await app.register(fastifyFormbody); + + // Serve static assets (local dev only — Vercel serves from public/) + await app.register(fastifyStatic, { + root: join(__dirname, "public"), + prefix: "/", + }); + + // Nunjucks template engine + nunjucks.configure(join(__dirname, "templates"), { + autoescape: true, + noCache: config.nodeEnv === "development", + }); + + await app.register(fastifyView, { + engine: { nunjucks }, + templates: join(__dirname, "templates"), + options: { + onConfigure: (_env: nunjucks.Environment) => { + // Add any custom filters here + }, + }, + }); + + // Register routes + await app.register(homeRoutes); + await app.register(flowRoutes); + + return app; +} diff --git a/jus-ia-start-kit/src/server.ts b/jus-ia-start-kit/src/server.ts index 0b6f3b5cd..d6dbd27e7 100644 --- a/jus-ia-start-kit/src/server.ts +++ b/jus-ia-start-kit/src/server.ts @@ -1,57 +1,8 @@ -import Fastify from "fastify"; -import fastifyView from "@fastify/view"; -import fastifyStatic from "@fastify/static"; -import fastifyFormbody from "@fastify/formbody"; -import nunjucks from "nunjucks"; -import { fileURLToPath } from "node:url"; -import { dirname, join } from "node:path"; import { config } from "./config/index.js"; -import { homeRoutes } from "./routes/home.js"; -import { flowRoutes } from "./routes/flow.js"; +import { buildApp } from "./app.js"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const app = await buildApp(); -const app = Fastify({ - logger: { - level: config.nodeEnv === "production" ? "info" : "debug", - transport: - config.nodeEnv === "development" - ? { target: "pino-pretty", options: { translateTime: "HH:MM:ss" } } - : undefined, - }, -}); - -// Parse form body (application/x-www-form-urlencoded) -await app.register(fastifyFormbody); - -// Serve static assets -await app.register(fastifyStatic, { - root: join(__dirname, "public"), - prefix: "/", -}); - -// Nunjucks template engine -const nunjucksEnv = nunjucks.configure(join(__dirname, "templates"), { - autoescape: true, - noCache: config.nodeEnv === "development", -}); - -await app.register(fastifyView, { - engine: { nunjucks }, - templates: join(__dirname, "templates"), - options: { - onConfigure: (env: nunjucks.Environment) => { - // Add any custom filters here - }, - }, -}); - -// Register routes -await app.register(homeRoutes); -await app.register(flowRoutes); - -// Start server try { const address = await app.listen({ port: config.port, host: config.host }); app.log.info(`Jus IA Start Kit running at ${address}`); diff --git a/jus-ia-start-kit/vercel.json b/jus-ia-start-kit/vercel.json new file mode 100644 index 000000000..697601f22 --- /dev/null +++ b/jus-ia-start-kit/vercel.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "buildCommand": "npm run build", + "outputDirectory": "public", + "functions": { + "api/index.ts": { + "includeFiles": "src/templates/**" + } + }, + "rewrites": [ + { "source": "/css/(.*)", "destination": "/css/$1" }, + { "source": "/js/(.*)", "destination": "/js/$1" }, + { "source": "/images/(.*)", "destination": "/images/$1" }, + { "source": "/favicon.ico", "destination": "/favicon.ico" }, + { "source": "/(.*)", "destination": "/api/index" } + ] +}