Files
ArchiTools/src/app/api/eterra/sync-rules/scheduler/route.ts
T
Claude VM 0cce1c8170 feat(sync-management): rule-based sync scheduling page + API
Phase 1 of unified sync scheduler:

- New Prisma model GisSyncRule: per-UAT or per-county sync frequency
  rules with priority, time windows, step selection (T/C/N/E)
- CRUD API: /api/eterra/sync-rules (list, create, update, delete, bulk)
- Global default frequency via KeyValueStore
- /sync-management page with 3 tabs:
  - Reguli: table with filters, add dialog (UAT search + county select)
  - Status: stats cards, frequency distribution, coverage overview
  - Judete: quick county-level frequency assignment
- Monitor page: link to sync management from eTerra actions section

Rule resolution: UAT-specific > county default > global default.
Scheduler engine (Phase 2) will read these rules to automate syncs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:03:50 +03:00

73 lines
2.0 KiB
TypeScript

/**
* GET /api/eterra/sync-rules/scheduler — Scheduler status
*
* Returns current scheduler state from KeyValueStore + computed stats.
*/
import { prisma } from "@/core/storage/prisma";
import { NextResponse } from "next/server";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export async function GET() {
try {
// Get scheduler state from KV (will be populated by the scheduler in Phase 2)
const kvState = await prisma.keyValueStore.findUnique({
where: {
namespace_key: { namespace: "sync-management", key: "scheduler-state" },
},
});
// Compute rule stats
const [totalRules, activeRules, dueNow, withErrors] = await Promise.all([
prisma.gisSyncRule.count(),
prisma.gisSyncRule.count({ where: { enabled: true } }),
prisma.gisSyncRule.count({
where: { enabled: true, nextDueAt: { lte: new Date() } },
}),
prisma.gisSyncRule.count({
where: { lastSyncStatus: "error" },
}),
]);
// Frequency distribution
const freqDist = await prisma.gisSyncRule.groupBy({
by: ["frequency"],
where: { enabled: true },
_count: true,
});
// County coverage
const totalCounties = await prisma.gisUat.groupBy({
by: ["county"],
where: { county: { not: null } },
_count: true,
});
const countiesWithRules = await prisma.gisSyncRule.groupBy({
by: ["county"],
where: { county: { not: null } },
_count: true,
});
return NextResponse.json({
scheduler: kvState?.value ?? { status: "not-started" },
stats: {
totalRules,
activeRules,
dueNow,
withErrors,
frequencyDistribution: Object.fromEntries(
freqDist.map((f) => [f.frequency, f._count]),
),
totalCounties: totalCounties.length,
countiesWithRules: countiesWithRules.length,
},
});
} catch (error) {
const msg = error instanceof Error ? error.message : "Eroare server";
return NextResponse.json({ error: msg }, { status: 500 });
}
}