Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/nperez0111/bookhive. Track your books, share your shelves, see what others are reading bookhive.buzz
atproto bluesky books bookshelf goodreads management-system
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159import inspector from "node:inspector";import os from "node:os";import { Hono } from "hono";import { z } from "zod";import { zValidator } from "@hono/zod-validator";
import type { AppEnv } from "../context";import { env } from "../env";import { isAuthorizedExportRequest } from "../utils/dbExport";
let isProfilingActive = false;
const debug = new Hono<AppEnv>() // Auth middleware for all /debug/* routes .use("*", async (c, next) => { const authorization = c.req.header("authorization"); if ( !env.EXPORT_SHARED_SECRET || !isAuthorizedExportRequest({ authorizationHeader: authorization, sharedSecret: env.EXPORT_SHARED_SECRET, }) ) { return c.json({ message: "Not Found" }, 404) as never; } await next(); })
.get("/heap-snapshot", (c) => { const timestamp = Date.now(); const snapshot = Bun.generateHeapSnapshot("v8");
return c.body(snapshot, 200, { "Content-Type": "application/octet-stream", "Content-Disposition": `attachment; filename="heap-${timestamp}.heapsnapshot"`, "Cache-Control": "no-store", }); })
.get( "/cpu-profile", zValidator( "query", z.object({ duration: z.coerce.number().int().min(1).max(120).default(30), }), ), async (c) => { if (isProfilingActive) { return c.json({ message: "A CPU profile is already in progress" }, 409); }
const { duration } = c.req.valid("query"); const abortSignal = c.req.raw.signal; isProfilingActive = true;
const session = new inspector.Session(); try { session.connect();
const post = (method: string, params?: Record<string, unknown>) => new Promise<Record<string, unknown>>((resolve, reject) => { session.post(method, params, (err: Error | null, result: unknown) => { if (err) reject(err); else resolve(result as Record<string, unknown>); }); });
await post("Profiler.enable"); await post("Profiler.start");
await new Promise<void>((resolve, reject) => { const timer = setTimeout(resolve, duration * 1000); abortSignal.addEventListener( "abort", () => { clearTimeout(timer); reject(new DOMException("Client disconnected", "AbortError")); }, { once: true }, ); });
if (abortSignal.aborted) { throw new DOMException("Client disconnected", "AbortError"); }
const { profile } = await post("Profiler.stop"); await post("Profiler.disable"); session.disconnect();
const timestamp = Date.now(); return c.json(profile, 200, { "Content-Disposition": `attachment; filename="cpu-${timestamp}.cpuprofile"`, "Cache-Control": "no-store", }); } catch (err) { // Always stop the profiler cleanly on any error try { const quietPost = (method: string) => new Promise<void>((resolve) => { session.post(method, {}, () => resolve()); }); await quietPost("Profiler.stop"); await quietPost("Profiler.disable"); session.disconnect(); } catch { // Best-effort cleanup }
if (err instanceof DOMException && err.name === "AbortError") { return c.json({ message: "Profile aborted: client disconnected" }, 408); } throw err; } finally { isProfilingActive = false; } }, )
.get("/memory", (c) => { const mem = process.memoryUsage(); return c.json({ rss: mem.rss, heapTotal: mem.heapTotal, heapUsed: mem.heapUsed, external: mem.external, arrayBuffers: mem.arrayBuffers, uptimeSeconds: process.uptime(), cpuCount: os.cpus().length, }); })
.post("/gc", (c) => { const before = process.memoryUsage(); Bun.gc(true); const after = process.memoryUsage();
return c.json({ before: { rss: before.rss, heapTotal: before.heapTotal, heapUsed: before.heapUsed, }, after: { rss: after.rss, heapTotal: after.heapTotal, heapUsed: after.heapUsed, }, freed: { rss: before.rss - after.rss, heapTotal: before.heapTotal - after.heapTotal, heapUsed: before.heapUsed - after.heapUsed, }, }); });
export default debug;