feat(geoportal): one-click optimize-tiles + unified setup banner
New endpoint POST /api/geoportal/optimize-tiles: - Slims gis_features view (drops attributes, enrichment, timestamps) - Cascades to gis_terenuri, gis_cladiri, gis_administrativ, gis_documentatii - Makes vector tiles dramatically smaller Setup banner now checks 3 optimizations: 1. UAT zoom views (gis_uats_z0/z5/z8/z12) 2. Pre-computed geometry (geom_z0/z5/z8 columns) 3. Slim tile views (no JSON columns) One "Aplica toate" button runs all pending steps sequentially. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* POST /api/geoportal/optimize-tiles
|
||||
*
|
||||
* Slims down gis_features/terenuri/cladiri/administrativ views
|
||||
* by removing heavy JSON columns (attributes, enrichment, timestamps).
|
||||
* Makes Martin vector tiles much smaller and faster.
|
||||
*
|
||||
* Safe to re-run (CREATE OR REPLACE VIEW).
|
||||
*/
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/core/storage/prisma";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
name: "gis_features (slim)",
|
||||
sql: `CREATE OR REPLACE VIEW gis_features AS
|
||||
SELECT id, "layerId" AS layer_id, siruta, "objectId" AS object_id,
|
||||
"cadastralRef" AS cadastral_ref, "areaValue" AS area_value,
|
||||
"isActive" AS is_active, geom
|
||||
FROM "GisFeature" WHERE geom IS NOT NULL`,
|
||||
},
|
||||
{
|
||||
name: "gis_terenuri",
|
||||
sql: `CREATE OR REPLACE VIEW gis_terenuri AS
|
||||
SELECT * FROM gis_features
|
||||
WHERE layer_id LIKE 'TERENURI%' OR layer_id LIKE 'CADGEN_LAND%'`,
|
||||
},
|
||||
{
|
||||
name: "gis_cladiri",
|
||||
sql: `CREATE OR REPLACE VIEW gis_cladiri AS
|
||||
SELECT * FROM gis_features
|
||||
WHERE layer_id LIKE 'CLADIRI%' OR layer_id LIKE 'CADGEN_BUILDING%'`,
|
||||
},
|
||||
{
|
||||
name: "gis_administrativ",
|
||||
sql: `CREATE OR REPLACE VIEW gis_administrativ AS
|
||||
SELECT * FROM gis_features
|
||||
WHERE layer_id LIKE 'LIMITE%' OR layer_id LIKE 'SPECIAL_AREAS%'`,
|
||||
},
|
||||
{
|
||||
name: "gis_documentatii",
|
||||
sql: `CREATE OR REPLACE VIEW gis_documentatii AS
|
||||
SELECT * FROM gis_features
|
||||
WHERE layer_id LIKE 'EXPERTIZA%' OR layer_id LIKE 'ZONE_INTERES%' OR layer_id LIKE 'RECEPTII%'`,
|
||||
},
|
||||
];
|
||||
|
||||
export async function POST() {
|
||||
const results: string[] = [];
|
||||
try {
|
||||
for (const step of STEPS) {
|
||||
await prisma.$executeRawUnsafe(step.sql);
|
||||
results.push(`${step.name} OK`);
|
||||
}
|
||||
return NextResponse.json({ status: "ok", results });
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : "Eroare";
|
||||
return NextResponse.json({ status: "error", results, error: msg }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Check if views are slim (no 'attributes' column)
|
||||
const cols = await prisma.$queryRaw`
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'gis_features' AND table_schema = 'public'
|
||||
ORDER BY ordinal_position
|
||||
` as Array<{ column_name: string }>;
|
||||
const hasAttributes = cols.some((c) => c.column_name === "attributes");
|
||||
return NextResponse.json({
|
||||
optimized: !hasAttributes,
|
||||
columns: cols.map((c) => c.column_name),
|
||||
});
|
||||
} catch {
|
||||
return NextResponse.json({ optimized: false, columns: [] });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user