initial push

This commit is contained in:
Marcelo Dares
2026-03-15 15:03:56 +01:00
parent d48b9d5352
commit 65aaf9275e
146 changed files with 70245 additions and 100 deletions

View File

@@ -0,0 +1,134 @@
import { Prisma } from "@prisma/client";
import { NextResponse } from "next/server";
import { requireAdminApiUser } from "@/lib/auth/admin";
import { prisma } from "@/lib/prisma";
import {
MAX_STRATEGIC_EVIDENCE_BYTES,
isAllowedEvidenceMimeType,
storeStrategicEvidenceFile,
} from "@/lib/strategic-diagnostic/evidence-storage";
import { mapSectionKeyToEnum, recomputeStrategicDiagnosticFromStoredData } from "@/lib/strategic-diagnostic/server";
import { STRATEGIC_SECTION_KEYS, type StrategicSectionKey } from "@/lib/strategic-diagnostic/types";
function parseSection(value: unknown): StrategicSectionKey | null {
if (typeof value !== "string") {
return null;
}
const section = value.trim() as StrategicSectionKey;
return STRATEGIC_SECTION_KEYS.includes(section) ? section : null;
}
function parseCategory(value: unknown) {
if (typeof value !== "string") {
return "";
}
return value.trim();
}
function isSchemaNotReadyError(error: unknown) {
return error instanceof Prisma.PrismaClientKnownRequestError && (error.code === "P2021" || error.code === "P2022");
}
export async function POST(request: Request) {
const user = await requireAdminApiUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
try {
const formData = await request.formData();
const section = parseSection(formData.get("section"));
const category = parseCategory(formData.get("category"));
const file = formData.get("file");
if (!section) {
return NextResponse.json({ error: "Seccion de evidencia invalida." }, { status: 400 });
}
if (!category) {
return NextResponse.json({ error: "Categoria de evidencia requerida." }, { status: 400 });
}
if (!(file instanceof File)) {
return NextResponse.json({ error: "Archivo requerido." }, { status: 400 });
}
if (file.size <= 0) {
return NextResponse.json({ error: "El archivo esta vacio." }, { status: 400 });
}
if (file.size > MAX_STRATEGIC_EVIDENCE_BYTES) {
return NextResponse.json({ error: "El archivo excede el limite de 10MB." }, { status: 400 });
}
if (!isAllowedEvidenceMimeType(file.type)) {
return NextResponse.json({ error: "Tipo de archivo no permitido (usa PDF, DOC, DOCX, JPG o PNG)." }, { status: 400 });
}
const organization = await prisma.organization.findUnique({
where: { userId: user.id },
select: {
id: true,
},
});
if (!organization) {
return NextResponse.json({ error: "No existe un perfil organizacional para este usuario." }, { status: 400 });
}
const fileBuffer = Buffer.from(await file.arrayBuffer());
const stored = await storeStrategicEvidenceFile(user.id, file.name, file.type, fileBuffer);
const row = await prisma.strategicDiagnosticEvidenceDocument.create({
data: {
organizationId: organization.id,
userId: user.id,
section: mapSectionKeyToEnum(section),
category,
fileName: stored.fileName,
storedFileName: stored.storedFileName,
filePath: stored.filePath,
mimeType: stored.mimeType,
sizeBytes: stored.sizeBytes,
checksumSha256: stored.checksumSha256,
},
select: {
id: true,
category: true,
fileName: true,
filePath: true,
mimeType: true,
sizeBytes: true,
createdAt: true,
},
});
const snapshot = await recomputeStrategicDiagnosticFromStoredData(user.id);
return NextResponse.json({
ok: true,
document: {
id: row.id,
section,
category: row.category,
fileName: row.fileName,
filePath: row.filePath,
mimeType: row.mimeType,
sizeBytes: row.sizeBytes,
createdAt: row.createdAt.toISOString(),
},
payload: snapshot,
});
} catch (error) {
if (isSchemaNotReadyError(error)) {
return NextResponse.json(
{ error: "La base de datos aun no tiene las tablas de evidencias de Modulo 2. Ejecuta prisma migrate para continuar." },
{ status: 503 },
);
}
return NextResponse.json({ error: "No fue posible subir la evidencia." }, { status: 400 });
}
}

View File

@@ -0,0 +1,38 @@
import { Prisma } from "@prisma/client";
import { NextResponse } from "next/server";
import { requireAdminApiUser } from "@/lib/auth/admin";
import { saveStrategicDiagnosticData } from "@/lib/strategic-diagnostic/server";
function isSchemaNotReadyError(error: unknown) {
return error instanceof Prisma.PrismaClientKnownRequestError && (error.code === "P2021" || error.code === "P2022");
}
export async function POST(request: Request) {
const user = await requireAdminApiUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
try {
const body = (await request.json()) as Record<string, unknown>;
const payload = await saveStrategicDiagnosticData(user.id, body.data, {
forceCompleted: body.forceCompleted === true,
});
if (!payload) {
return NextResponse.json({ error: "No existe un perfil organizacional para este usuario." }, { status: 400 });
}
return NextResponse.json({ ok: true, payload });
} catch (error) {
if (isSchemaNotReadyError(error)) {
return NextResponse.json(
{ error: "La base de datos aun no tiene las tablas de Modulo 2. Ejecuta prisma migrate para continuar." },
{ status: 503 },
);
}
return NextResponse.json({ error: "No fue posible guardar el Modulo 2." }, { status: 400 });
}
}