Files
ArchiTools/src/app/api/eterra/layers/summary/route.ts
T
AI Assistant f73e639e4f fix(parcel-sync): quote all CSV fields + layer feature counts + drumul de azi
- CSV export: all fields properly quoted to prevent column misalignment
  when values contain commas (e.g. nrTopo with multiple topo numbers)
- Layer catalog: 'Numara toate' button fetches feature count per layer
  via /api/eterra/layers/summary (now supports session auth)
- Feature counts displayed as badges on each layer and category total
- 'Drumul de azi' section: persists today's layer counts in localStorage
  grouped by SIRUTA with timestamps
2026-03-06 23:19:58 +02:00

87 lines
2.8 KiB
TypeScript

import { NextResponse } from "next/server";
import {
EterraClient,
type EsriGeometry,
} from "@/modules/parcel-sync/services/eterra-client";
import { LAYER_CATALOG } from "@/modules/parcel-sync/services/eterra-layers";
import { fetchUatGeometry } from "@/modules/parcel-sync/services/uat-geometry";
import { getSessionCredentials } from "@/modules/parcel-sync/services/session-store";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
type Body = {
username?: string;
password?: string;
siruta?: string | number;
layerIds?: string[]; // subset — omit to count all
};
/**
* POST — Count features per layer on the remote eTerra server.
* Supports session-based auth (falls back to env vars).
*/
export async function POST(req: Request) {
try {
const body = (await req.json()) as Body;
const session = getSessionCredentials();
const username = String(
body.username || session?.username || process.env.ETERRA_USERNAME || "",
).trim();
const password = String(
body.password || session?.password || process.env.ETERRA_PASSWORD || "",
).trim();
const siruta = String(body.siruta ?? "").trim();
if (!username || !password)
return NextResponse.json({ error: "Credențiale lipsă" }, { status: 400 });
if (!/^\d+$/.test(siruta))
return NextResponse.json({ error: "SIRUTA invalid" }, { status: 400 });
const client = await EterraClient.create(username, password);
let uatGeometry: EsriGeometry | undefined;
// Pre-fetch UAT geometry for spatial layers
try {
uatGeometry = await fetchUatGeometry(client, siruta);
} catch {
// Some layers don't need it
}
const layers = body.layerIds
? LAYER_CATALOG.filter((l) => body.layerIds!.includes(l.id))
: LAYER_CATALOG;
const results: Record<string, { count: number; error?: string }> = {};
// Count layers in parallel, max 4 concurrent
const chunks: (typeof layers)[] = [];
for (let i = 0; i < layers.length; i += 4) {
chunks.push(layers.slice(i, i + 4));
}
for (const chunk of chunks) {
const promises = chunk.map(async (layer) => {
try {
const count =
layer.spatialFilter && uatGeometry
? await client.countLayerByGeometry(layer, uatGeometry)
: await client.countLayer(layer, siruta);
results[layer.id] = { count };
} catch (err) {
results[layer.id] = {
count: 0,
error: err instanceof Error ? err.message : "eroare",
};
}
});
await Promise.all(promises);
}
return NextResponse.json({ siruta, counts: results });
} catch (error) {
const message = error instanceof Error ? error.message : "Eroare server";
return NextResponse.json({ error: message }, { status: 500 });
}
}