Add permission-scoped MCP, readiness checks, and management UI improvements

This commit is contained in:
2026-09-11 18:12:48 -04:00
parent 32e56b1c34
commit 815640d919
40 changed files with 845 additions and 57 deletions
+14 -2
View File
@@ -6,7 +6,7 @@ import type { BannerCrop } from "@album/contracts";
import { processPhotoExport } from "./exports";
import { processEmailDelivery } from "@album/email/queue";
import convert from "heic-convert";
import { eventBanners, getDb, photoJobs, photos } from "@album/database";
import { databaseReady, eventBanners, getDb, photoJobs, photos } from "@album/database";
import {
displayObjectKey,
getObjectBuffer,
@@ -16,6 +16,16 @@ import {
const POLL_MS = 1000;
const CONCURRENCY = Math.max(1, Number(process.env.WORKER_CONCURRENCY ?? 1));
const heartbeats = new Map<string, number>();
const beat = (name: string) => heartbeats.set(name, Date.now());
if (process.env.WORKER_HEALTH_PORT) {
Bun.serve({ hostname: "127.0.0.1", port: Number(process.env.WORKER_HEALTH_PORT), async fetch(request) {
if (new URL(request.url).pathname !== "/health") return new Response(null, { status: 404 });
const fresh = heartbeats.size === CONCURRENCY + 2 && [...heartbeats.values()].every(time => Date.now() - time < 15 * 60_000);
const ready = fresh && await databaseReady();
return Response.json({ status: ready ? "ready" : "unavailable" }, { status: ready ? 200 : 503, headers: { "Cache-Control": "no-store" } });
} });
}
const MIN_INTERVAL_MS = Math.max(
0,
Number(process.env.WORKER_MIN_INTERVAL_MS ?? 250),
@@ -129,6 +139,7 @@ async function failJob(job: ClaimedJob, error: unknown) {
async function workerLoop(workerId: number) {
while (true) {
beat(`photo-${workerId}`);
const started = Date.now();
const job = await claimJob();
if (!job) {
@@ -183,13 +194,14 @@ console.info(
async function emailLoop() {
while (true) {
beat("email");
try { await processEmailDelivery(); } catch { console.error("Email queue check failed"); }
await Bun.sleep(1000);
}
}
await Promise.all([
(async () => { while (true) { try { await processPhotoExport(); } catch { console.error("Export queue check failed"); } await Bun.sleep(1000); } })(),
(async () => { while (true) { beat("exports"); try { await processPhotoExport(); } catch { console.error("Export queue check failed"); } await Bun.sleep(1000); } })(),
emailLoop(),
...Array.from({ length: CONCURRENCY }, (_, index) => workerLoop(index + 1)),
]);