diff --git a/src/index.ts b/src/index.ts index 2acbffb..0d4e5ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,50 +1,71 @@ -import 'dotenv/config' -import { Hono } from 'hono' -import { cors } from 'hono/cors' -import { serve } from '@hono/node-server' -import { createNodeWebSocket } from '@hono/node-ws' -import authRoutes from './routes/auth.js' -import plansRoutes from './routes/plans.js' -import simulationsRoutes from './routes/simulations.js' -import sujetsRoutes from './routes/sujets.js' -import correctionsRoutes from './routes/corrections.js' -import stripeRoutes from './routes/stripe.js' -import createT2LiveRoutes from './routes/t2live.js' -import usersRoutes from './routes/users.js' +import "dotenv/config"; +import { Hono } from "hono"; +import { cors } from "hono/cors"; +import { serve } from "@hono/node-server"; +import { createNodeWebSocket } from "@hono/node-ws"; +import authRoutes from "./routes/auth.js"; +import plansRoutes from "./routes/plans.js"; +import simulationsRoutes from "./routes/simulations.js"; +import sujetsRoutes from "./routes/sujets.js"; +import correctionsRoutes from "./routes/corrections.js"; +import stripeRoutes from "./routes/stripe.js"; +import createT2LiveRoutes from "./routes/t2live.js"; +import usersRoutes from "./routes/users.js"; +import { supabase } from "./lib/supabase.js"; -const app = new Hono() -const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app }) +const app = new Hono(); +const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app }); app.use( - '*', + "*", cors({ origin: [ - 'https://expria.app', - 'http://localhost:5173', - 'http://localhost:5174', + "https://expria.app", + "http://localhost:5173", + "http://localhost:5174", ], - allowMethods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'], - allowHeaders: ['Content-Type', 'Authorization', 'X-Api-Version'], - }) -) + allowMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"], + allowHeaders: ["Content-Type", "Authorization", "X-Api-Version"], + }), +); -app.get('/', (c) => { - return c.json({ message: 'Expria API — OK' }, 200) -}) +// Health check — exécute un SELECT 1 léger sur Supabase pour garder le pool +// de connexions DB actif (ping UptimeRobot toutes les 5 min sur Render Starter). +// Le endpoint retourne toujours 200 (liveness) ; le champ `db` reporte l'état +// réel de la connexion pour observabilité. +const HEALTH_DB_TIMEOUT_MS = 5000; -app.route('/auth', authRoutes) -app.route('/plans', plansRoutes) -app.route('/simulations', simulationsRoutes) -app.route('/sujets', sujetsRoutes) -app.route('/corrections', correctionsRoutes) -app.route('/stripe', stripeRoutes) -app.route('/t2', createT2LiveRoutes(upgradeWebSocket)) -app.route('/users', usersRoutes) +app.get("/", async (c) => { + const probe = supabase.from("profiles").select("id", { head: true }).limit(1); + const timeout = new Promise<{ error: Error }>((resolve) => + setTimeout( + () => resolve({ error: new Error("HEALTH_DB_TIMEOUT") }), + HEALTH_DB_TIMEOUT_MS, + ), + ); -const port = Number(process.env.PORT) || 3000 + try { + const result = await Promise.race([probe, timeout]); + const db = "error" in result && result.error ? "error" : "connected"; + return c.json({ message: "Expria API — OK", db }, 200); + } catch { + return c.json({ message: "Expria API — OK", db: "error" }, 200); + } +}); + +app.route("/auth", authRoutes); +app.route("/plans", plansRoutes); +app.route("/simulations", simulationsRoutes); +app.route("/sujets", sujetsRoutes); +app.route("/corrections", correctionsRoutes); +app.route("/stripe", stripeRoutes); +app.route("/t2", createT2LiveRoutes(upgradeWebSocket)); +app.route("/users", usersRoutes); + +const port = Number(process.env.PORT) || 3000; const server = serve({ fetch: app.fetch, port }, () => { - console.log(`Expria API listening on port ${port}`) -}) + console.log(`Expria API listening on port ${port}`); +}); -injectWebSocket(server) +injectWebSocket(server);