Wire jobs-worker health probes to Discord/email alerts with cooldown, optional mysqldump, rate-limit /api/health, mark-all-read alerts, ConfirmDialog on destructive admin actions, and raise coverage floors. Co-authored-by: Cursor <[email protected]>
66 lines
1.7 KiB
TypeScript
66 lines
1.7 KiB
TypeScript
import { sql } from "drizzle-orm";
|
|
import { NextResponse } from "next/server";
|
|
import { env } from "@/env";
|
|
import { apiJson } from "@/lib/api";
|
|
import { db } from "@/lib/db";
|
|
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
|
import { redis } from "@/lib/redis";
|
|
import { rcon } from "@/lib/services/rcon";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/**
|
|
* Ops health probe: database reachability, Redis (when configured), emulator
|
|
* RCON, SMTP (when configured), and runtime info. Returns HTTP 200 always
|
|
* (read the `status`/`database` fields), so it's safe for uptime monitors that
|
|
* only care about reachability. Rate-limited per client IP.
|
|
*/
|
|
export async function GET() {
|
|
const ip = await clientIp();
|
|
const limit = await rateLimit(`health:${ip}`, 60, 60_000);
|
|
if (!limit.ok) {
|
|
return NextResponse.json(
|
|
{ status: "rate_limited", retryAfter: limit.retryAfter },
|
|
{
|
|
status: 429,
|
|
headers: { "Retry-After": String(limit.retryAfter) },
|
|
},
|
|
);
|
|
}
|
|
|
|
const database = await db
|
|
.execute(sql`SELECT 1`)
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
|
|
let redisOk: boolean | null = null;
|
|
if (env.REDIS_URL) {
|
|
if (!redis) {
|
|
redisOk = false;
|
|
} else {
|
|
try {
|
|
const pong = await redis.ping();
|
|
redisOk = pong === "PONG";
|
|
} catch {
|
|
redisOk = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
const emulator = await rcon.send("ping", null).catch(() => false);
|
|
|
|
const resendAvailable = !!env.RESEND_API_KEY;
|
|
|
|
const degraded = !database || redisOk === false;
|
|
return apiJson({
|
|
status: degraded ? "degraded" : "ok",
|
|
database,
|
|
redis: redisOk,
|
|
emulator,
|
|
resend: resendAvailable,
|
|
node: process.version,
|
|
uptime: Math.round(process.uptime()),
|
|
time: new Date().toISOString(),
|
|
});
|
|
}
|