fix(health): add Supabase keepalive ping to GET / health check
UptimeRobot pings GET / every 5 minutes. Previously static response only kept Node process alive but let Supabase connection pool go cold. Now executes a lightweight HEAD query (profiles, limit 1) to maintain DB connection warmth. Always returns 200 with db status field for observability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b5980ccce2
commit
fa06daace8
1 changed files with 59 additions and 38 deletions
97
src/index.ts
97
src/index.ts
|
|
@ -1,50 +1,71 @@
|
||||||
import 'dotenv/config'
|
import "dotenv/config";
|
||||||
import { Hono } from 'hono'
|
import { Hono } from "hono";
|
||||||
import { cors } from 'hono/cors'
|
import { cors } from "hono/cors";
|
||||||
import { serve } from '@hono/node-server'
|
import { serve } from "@hono/node-server";
|
||||||
import { createNodeWebSocket } from '@hono/node-ws'
|
import { createNodeWebSocket } from "@hono/node-ws";
|
||||||
import authRoutes from './routes/auth.js'
|
import authRoutes from "./routes/auth.js";
|
||||||
import plansRoutes from './routes/plans.js'
|
import plansRoutes from "./routes/plans.js";
|
||||||
import simulationsRoutes from './routes/simulations.js'
|
import simulationsRoutes from "./routes/simulations.js";
|
||||||
import sujetsRoutes from './routes/sujets.js'
|
import sujetsRoutes from "./routes/sujets.js";
|
||||||
import correctionsRoutes from './routes/corrections.js'
|
import correctionsRoutes from "./routes/corrections.js";
|
||||||
import stripeRoutes from './routes/stripe.js'
|
import stripeRoutes from "./routes/stripe.js";
|
||||||
import createT2LiveRoutes from './routes/t2live.js'
|
import createT2LiveRoutes from "./routes/t2live.js";
|
||||||
import usersRoutes from './routes/users.js'
|
import usersRoutes from "./routes/users.js";
|
||||||
|
import { supabase } from "./lib/supabase.js";
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono();
|
||||||
const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app })
|
const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app });
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'*',
|
"*",
|
||||||
cors({
|
cors({
|
||||||
origin: [
|
origin: [
|
||||||
'https://expria.app',
|
"https://expria.app",
|
||||||
'http://localhost:5173',
|
"http://localhost:5173",
|
||||||
'http://localhost:5174',
|
"http://localhost:5174",
|
||||||
],
|
],
|
||||||
allowMethods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
|
allowMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
|
||||||
allowHeaders: ['Content-Type', 'Authorization', 'X-Api-Version'],
|
allowHeaders: ["Content-Type", "Authorization", "X-Api-Version"],
|
||||||
})
|
}),
|
||||||
)
|
);
|
||||||
|
|
||||||
app.get('/', (c) => {
|
// Health check — exécute un SELECT 1 léger sur Supabase pour garder le pool
|
||||||
return c.json({ message: 'Expria API — OK' }, 200)
|
// 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.get("/", async (c) => {
|
||||||
app.route('/plans', plansRoutes)
|
const probe = supabase.from("profiles").select("id", { head: true }).limit(1);
|
||||||
app.route('/simulations', simulationsRoutes)
|
const timeout = new Promise<{ error: Error }>((resolve) =>
|
||||||
app.route('/sujets', sujetsRoutes)
|
setTimeout(
|
||||||
app.route('/corrections', correctionsRoutes)
|
() => resolve({ error: new Error("HEALTH_DB_TIMEOUT") }),
|
||||||
app.route('/stripe', stripeRoutes)
|
HEALTH_DB_TIMEOUT_MS,
|
||||||
app.route('/t2', createT2LiveRoutes(upgradeWebSocket))
|
),
|
||||||
app.route('/users', usersRoutes)
|
);
|
||||||
|
|
||||||
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 }, () => {
|
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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue