audit: production safety fixes, cleanup, and documentation overhaul
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>
This commit is contained in:
@@ -117,27 +117,29 @@ export async function enqueueBatch(
|
||||
const items: QueueItem[] = [];
|
||||
|
||||
for (const input of inputs) {
|
||||
// Create DB record in "queued" status
|
||||
const record = await prisma.cfExtract.create({
|
||||
data: {
|
||||
nrCadastral: input.nrCadastral,
|
||||
nrCF: input.nrCF ?? input.nrCadastral,
|
||||
siruta: input.siruta,
|
||||
judetIndex: input.judetIndex,
|
||||
judetName: input.judetName,
|
||||
uatId: input.uatId,
|
||||
uatName: input.uatName,
|
||||
gisFeatureId: input.gisFeatureId,
|
||||
prodId: input.prodId ?? 14200,
|
||||
status: "queued",
|
||||
version:
|
||||
((
|
||||
await prisma.cfExtract.aggregate({
|
||||
where: { nrCadastral: input.nrCadastral },
|
||||
_max: { version: true },
|
||||
})
|
||||
)._max.version ?? 0) + 1,
|
||||
},
|
||||
// Create DB record in "queued" status — use transaction + advisory lock
|
||||
// to prevent duplicate version numbers from concurrent requests
|
||||
const record = await prisma.$transaction(async (tx) => {
|
||||
await tx.$executeRaw`SELECT pg_advisory_xact_lock(hashtext(${'cfextract:' + input.nrCadastral}))`;
|
||||
const agg = await tx.cfExtract.aggregate({
|
||||
where: { nrCadastral: input.nrCadastral },
|
||||
_max: { version: true },
|
||||
});
|
||||
return tx.cfExtract.create({
|
||||
data: {
|
||||
nrCadastral: input.nrCadastral,
|
||||
nrCF: input.nrCF ?? input.nrCadastral,
|
||||
siruta: input.siruta,
|
||||
judetIndex: input.judetIndex,
|
||||
judetName: input.judetName,
|
||||
uatId: input.uatId,
|
||||
uatName: input.uatName,
|
||||
gisFeatureId: input.gisFeatureId,
|
||||
prodId: input.prodId ?? 14200,
|
||||
status: "queued",
|
||||
version: (agg._max.version ?? 0) + 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
items.push({ extractId: record.id, input });
|
||||
@@ -418,7 +420,10 @@ async function processBatch(
|
||||
},
|
||||
);
|
||||
|
||||
// Complete
|
||||
// Complete — require document date from ANCPI for accurate expiry
|
||||
if (!doc.dataDocument) {
|
||||
console.warn(`[epay-queue] Missing dataDocument for extract ${item.extractId}, using download date`);
|
||||
}
|
||||
const documentDate = doc.dataDocument
|
||||
? new Date(doc.dataDocument)
|
||||
: new Date();
|
||||
|
||||
Reference in New Issue
Block a user