0c4b91707f
CRITICAL fixes: - Fix SQL injection in geoportal search (template literal in $queryRaw) - Preserve enrichment data during GIS re-sync (upsert update explicit fields only) - Fix ePay version race condition (advisory lock in transaction) - Add requireAuth() to compress-pdf and unlock routes (were unauthenticated) - Remove hardcoded Stirling PDF API key (env vars now required) IMPORTANT fixes: - Add admin role check on registratura debug-sequences endpoint - Fix reserved slot race condition with advisory lock in transaction - Use SSO identity in close-guard-dialog instead of hardcoded "Utilizator" - Storage DELETE catches only P2025 (not found), re-throws real errors - Add onDelete: SetNull for GisFeature → GisSyncRun relation - Move portal-only users to PORTAL_ONLY_USERS env var - Add security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy) - Add periodic cleanup for eTerra/ePay session caches and progress store - Log warning when ePay dataDocument is missing (expiry fallback) Cleanup: - Delete orphaned rgi-test page (1086 lines, unregistered, inaccessible) - Delete legacy/ folder (5 files, unreferenced from src/) - Remove unused ensureBucketExists() from minio-client.ts Documentation: - Optimize CLAUDE.md: 464 → 197 lines (moved per-module details to docs/) - Create docs/ARCHITECTURE-QUICK.md (80 lines: data flow, deps, env vars) - Create docs/MODULE-MAP.md (140 lines: entry points, API routes, cross-deps) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { requireAuth } from "../auth-check";
|
|
|
|
const STIRLING_PDF_URL = process.env.STIRLING_PDF_URL;
|
|
const STIRLING_PDF_API_KEY = process.env.STIRLING_PDF_API_KEY;
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const authErr = await requireAuth(req);
|
|
if (authErr) return authErr;
|
|
|
|
if (!STIRLING_PDF_URL || !STIRLING_PDF_API_KEY) {
|
|
return NextResponse.json(
|
|
{ error: "Stirling PDF nu este configurat" },
|
|
{ status: 503 },
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Stream body directly to Stirling — avoids FormData re-serialization
|
|
// failure on large files ("Failed to parse body as FormData")
|
|
const res = await fetch(
|
|
`${STIRLING_PDF_URL}/api/v1/security/remove-password`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"X-API-KEY": STIRLING_PDF_API_KEY,
|
|
"Content-Type": req.headers.get("content-type") || "",
|
|
},
|
|
body: req.body,
|
|
// @ts-expect-error duplex required for streaming request bodies in Node
|
|
duplex: "half",
|
|
},
|
|
);
|
|
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => res.statusText);
|
|
return NextResponse.json(
|
|
{ error: `Stirling PDF error: ${res.status} — ${text}` },
|
|
{ status: res.status },
|
|
);
|
|
}
|
|
|
|
const blob = await res.blob();
|
|
const buffer = Buffer.from(await blob.arrayBuffer());
|
|
|
|
return new NextResponse(buffer, {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": "application/pdf",
|
|
"Content-Disposition": 'attachment; filename="unlocked.pdf"',
|
|
},
|
|
});
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
return NextResponse.json(
|
|
{ error: `Nu s-a putut contacta Stirling PDF: ${message}` },
|
|
{ status: 502 },
|
|
);
|
|
}
|
|
}
|