feat(sync): auto-trigger PMTiles rebuild after sync + fix progress display

- Add pmtiles-webhook.ts shared helper for triggering PMTiles rebuild
- sync-county: trigger rebuild when new features synced, pass jobId to
  syncLayer for sub-progress, update % after UAT completion (not before)
- sync-all-counties: same progress fix + rebuild trigger at end
- geoportal monitor: use shared helper instead of raw fetch
- weekend-deep-sync + auto-refresh: consolidate webhook code via helper
- docker-compose: default N8N_WEBHOOK_URL to pmtiles-webhook on satra:9876

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude VM
2026-04-09 14:59:18 +03:00
parent b356e70148
commit 377b88c48d
8 changed files with 120 additions and 73 deletions
+36 -4
View File
@@ -21,6 +21,7 @@ import { EterraClient } from "@/modules/parcel-sync/services/eterra-client";
import { checkEterraHealthNow } from "@/modules/parcel-sync/services/eterra-health";
import { createAppNotification } from "@/core/notifications/app-notifications";
import { getSessionCredentials } from "@/modules/parcel-sync/services/session-store";
import { firePmtilesRebuild } from "@/modules/parcel-sync/services/pmtiles-webhook";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
@@ -169,6 +170,8 @@ async function runCountySync(
}> = [];
let errors = 0;
let totalNewFeatures = 0;
for (let i = 0; i < uats.length; i++) {
const uat = uats[i]!;
const uatName = uat.name ?? uat.siruta;
@@ -189,12 +192,12 @@ async function runCountySync(
const uatStart = Date.now();
try {
// Sync TERENURI + CLADIRI
// Sync TERENURI + CLADIRI — pass jobId for sub-progress
const tRes = await syncLayer(username, password, uat.siruta, "TERENURI_ACTIVE", {
uatName,
uatName, jobId, isSubStep: true,
});
const cRes = await syncLayer(username, password, uat.siruta, "CLADIRI_ACTIVE", {
uatName,
uatName, jobId, isSubStep: true,
});
// Sync ADMINISTRATIV (intravilan) — wrapped in try/catch since it needs UAT geometry
@@ -205,7 +208,7 @@ async function runCountySync(
password,
uat.siruta,
"LIMITE_INTRAV_DYNAMIC",
{ uatName },
{ uatName, jobId, isSubStep: true },
);
if (aRes.newFeatures > 0) {
adminNote = ` | A:+${aRes.newFeatures}`;
@@ -236,8 +239,19 @@ async function runCountySync(
? `C:+${cRes.newFeatures}/${cRes.validFromUpdated ?? 0}vf`
: "C:ok",
];
totalNewFeatures += tRes.newFeatures + cRes.newFeatures;
const note = `${parts.join(", ")}${adminNote}${enrichNote} (${dur}s)`;
results.push({ siruta: uat.siruta, name: uatName, mode, duration: dur, note });
// Update progress AFTER UAT completion (so % reflects completed work)
const completedPct = Math.round(((i + 1) / uats.length) * 100);
push({
downloaded: completedPct,
total: 100,
phase: `[${i + 1}/${uats.length}] ${uatName} finalizat`,
note: `${note}`,
});
console.log(`[sync-county:${county}] ${i + 1}/${uats.length} ${uatName}: ${note}`);
} catch (err) {
errors++;
@@ -250,6 +264,13 @@ async function runCountySync(
duration: dur,
note: `ERR: ${msg}`,
});
// Still update progress after error
const completedPct = Math.round(((i + 1) / uats.length) * 100);
push({
downloaded: completedPct,
total: 100,
phase: `[${i + 1}/${uats.length}] ${uatName} — eroare`,
});
console.error(`[sync-county:${county}] ${uatName}: ${msg}`);
}
}
@@ -278,6 +299,17 @@ async function runCountySync(
});
console.log(`[sync-county:${county}] Done: ${summary}`);
// Trigger PMTiles rebuild if new features were synced
if (totalNewFeatures > 0) {
await firePmtilesRebuild("county-sync-complete", {
county,
uatCount: uats.length,
newFeatures: totalNewFeatures,
errors,
});
}
setTimeout(() => clearProgress(jobId), 6 * 3_600_000);
} catch (err) {
const msg = err instanceof Error ? err.message : "Unknown";