feat(api): hourly reconciliation endpoint
This commit is contained in:
parent
e8cd596702
commit
c5246f66f3
1 changed files with 45 additions and 0 deletions
45
src/app/api/cron/reconcile/route.ts
Normal file
45
src/app/api/cron/reconcile/route.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { ingestProject, projectsWithRepos } from "@/lib/git/ingest";
|
||||
import { jsonError, jsonOk } from "@/lib/http/json";
|
||||
|
||||
/**
|
||||
* POST /api/cron/reconcile — hourly catch-up across every linked project.
|
||||
*
|
||||
* Exists because webhooks fail. A delivery lost while the app restarts is gone
|
||||
* for good, and the dashboard stays plausible while being wrong, which is the
|
||||
* worst failure available here. This is also the same code path as backfill,
|
||||
* so the reliable path is the one that gets exercised constantly.
|
||||
*
|
||||
* Triggered by a Coolify scheduled task:
|
||||
* curl -X POST -H "Authorization: Bearer $CRON_SECRET" https://<app>/api/cron/reconcile
|
||||
*
|
||||
* One project failing must not stop the rest — a repository that was renamed
|
||||
* should not silently freeze every other project's dashboard.
|
||||
*/
|
||||
export async function POST(request: Request): Promise<Response> {
|
||||
const secret = process.env.CRON_SECRET;
|
||||
if (!secret) {
|
||||
console.error("[cron] CRON_SECRET is not set; refusing to run");
|
||||
return jsonError("Not configured", 500);
|
||||
}
|
||||
|
||||
if (request.headers.get("authorization") !== `Bearer ${secret}`) {
|
||||
return jsonError("Unauthorized", 401);
|
||||
}
|
||||
|
||||
const projects = await projectsWithRepos();
|
||||
const results: Array<{ project: string; created?: number; error?: string }> = [];
|
||||
|
||||
for (const project of projects) {
|
||||
try {
|
||||
const result = await ingestProject(project, { stopAfterKnownPages: 1 });
|
||||
results.push({ project: project.slug, created: result.created });
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "unknown error";
|
||||
console.error(`[cron] ${project.slug} failed: ${message}`);
|
||||
results.push({ project: project.slug, error: message });
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[cron] reconciled ${projects.length} projects`);
|
||||
return jsonOk({ projects: results });
|
||||
}
|
||||
Loading…
Reference in a new issue