Add permission-scoped MCP, readiness checks, and management UI improvements
This commit is contained in:
@@ -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)),
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user