feat(monitor): activity log with rebuild polling + warm cache details
- Rebuild: shows webhook status, then polls every 15s until PMTiles last-modified changes, then shows success with new size/timestamp - Warm cache: shows HIT/MISS/error breakdown after completion - Activity log panel with timestamps, color-coded status, scrollable - 15-minute timeout on rebuild polling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,6 +135,21 @@ export async function GET() {
|
||||
return NextResponse.json(result);
|
||||
}
|
||||
|
||||
async function getPmtilesInfo(): Promise<{ size: string; lastModified: string } | null> {
|
||||
if (!PMTILES_URL) return null;
|
||||
try {
|
||||
const res = await fetchWithTimeout(PMTILES_URL, 3000);
|
||||
return {
|
||||
size: res.headers.get("content-length")
|
||||
? `${(parseInt(res.headers.get("content-length") ?? "0", 10) / 1024 / 1024).toFixed(1)} MB`
|
||||
: "unknown",
|
||||
lastModified: res.headers.get("last-modified") ?? "unknown",
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json() as { action?: string };
|
||||
const action = body.action;
|
||||
@@ -143,8 +158,10 @@ export async function POST(request: NextRequest) {
|
||||
if (!N8N_WEBHOOK_URL) {
|
||||
return NextResponse.json({ error: "N8N_WEBHOOK_URL not configured" }, { status: 400 });
|
||||
}
|
||||
// Get current PMTiles state before rebuild
|
||||
const before = await getPmtilesInfo();
|
||||
try {
|
||||
await fetch(N8N_WEBHOOK_URL, {
|
||||
const webhookRes = await fetch(N8N_WEBHOOK_URL, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
@@ -152,32 +169,68 @@ export async function POST(request: NextRequest) {
|
||||
timestamp: new Date().toISOString(),
|
||||
}),
|
||||
});
|
||||
return NextResponse.json({ ok: true, message: "Rebuild triggered via N8N" });
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
action: "rebuild",
|
||||
webhookStatus: webhookRes.status,
|
||||
previousPmtiles: before,
|
||||
message: `Webhook trimis la N8N (HTTP ${webhookRes.status}). Rebuild-ul ruleaza ~8 min. Urmareste PMTiles last-modified.`,
|
||||
});
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
return NextResponse.json({ error: `Webhook failed: ${msg}` }, { status: 500 });
|
||||
return NextResponse.json({ error: `Webhook esuat: ${msg}` }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
if (action === "check-rebuild") {
|
||||
// Check if PMTiles was updated since a given timestamp
|
||||
const previousLastModified = (body as { previousLastModified?: string }).previousLastModified;
|
||||
const current = await getPmtilesInfo();
|
||||
const changed = !!current && !!previousLastModified && current.lastModified !== previousLastModified;
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
action: "check-rebuild",
|
||||
current,
|
||||
changed,
|
||||
message: changed
|
||||
? `Rebuild finalizat! PMTiles actualizat: ${current?.size}, ${current?.lastModified}`
|
||||
: "Rebuild in curs...",
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "warm-cache") {
|
||||
// Warm cache from within the container (tile-cache is on same Docker network)
|
||||
const sources = ["gis_terenuri", "gis_cladiri"];
|
||||
let warmed = 0;
|
||||
let total = 0;
|
||||
let hits = 0;
|
||||
let misses = 0;
|
||||
let errors = 0;
|
||||
const promises: Promise<void>[] = [];
|
||||
for (const source of sources) {
|
||||
// Bucharest area z14
|
||||
for (let x = 9200; x <= 9210; x++) {
|
||||
for (let y = 5960; y <= 5970; y++) {
|
||||
total++;
|
||||
promises.push(
|
||||
fetchWithTimeout(`${TILE_CACHE_INTERNAL}/${source}/14/${x}/${y}`, 30000)
|
||||
.then(() => { warmed++; })
|
||||
.catch(() => { /* noop */ }),
|
||||
.then((res) => {
|
||||
const cache = res.headers.get("x-cache-status") ?? "";
|
||||
if (cache === "HIT") hits++;
|
||||
else misses++;
|
||||
})
|
||||
.catch(() => { errors++; }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
return NextResponse.json({ ok: true, message: `Cache warmed: ${warmed} tiles` });
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
action: "warm-cache",
|
||||
total,
|
||||
hits,
|
||||
misses,
|
||||
errors,
|
||||
message: `${total} tile-uri procesate: ${hits} HIT, ${misses} MISS (nou incarcate), ${errors} erori`,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Unknown action" }, { status: 400 });
|
||||
|
||||
Reference in New Issue
Block a user