feat(sat): add SAT authentication and download services (Phase 3)

- Add sat-auth.service.ts for SAML token authentication with SAT
  using FIEL credentials and SOAP protocol
- Add sat-download.service.ts with full download workflow:
  - Request CFDI download (emitted/received)
  - Verify request status with polling support
  - Download ZIP packages when ready
  - Helper functions for status checking
- Install fast-xml-parser and adm-zip dependencies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Consultoria AS
2026-01-25 00:49:02 +00:00
parent a64aa11548
commit 56e6e27ab3
4 changed files with 605 additions and 0 deletions

View File

@@ -17,17 +17,20 @@
"@horux/shared": "workspace:*", "@horux/shared": "workspace:*",
"@nodecfdi/credentials": "^3.2.0", "@nodecfdi/credentials": "^3.2.0",
"@prisma/client": "^5.22.0", "@prisma/client": "^5.22.0",
"adm-zip": "^0.5.16",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"exceljs": "^4.4.0", "exceljs": "^4.4.0",
"express": "^4.21.0", "express": "^4.21.0",
"fast-xml-parser": "^5.3.3",
"helmet": "^8.0.0", "helmet": "^8.0.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"node-forge": "^1.3.3", "node-forge": "^1.3.3",
"zod": "^3.23.0" "zod": "^3.23.0"
}, },
"devDependencies": { "devDependencies": {
"@types/adm-zip": "^0.5.7",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/express": "^5.0.0", "@types/express": "^5.0.0",

View File

@@ -0,0 +1,159 @@
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
import { createHash, randomUUID } from 'crypto';
import type { Credential } from '@nodecfdi/credentials/node';
const SAT_AUTH_URL = 'https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/Autenticacion/Autenticacion.svc';
interface SatToken {
token: string;
expiresAt: Date;
}
/**
* Genera el timestamp para la solicitud SOAP
*/
function createTimestamp(): { created: string; expires: string } {
const now = new Date();
const created = now.toISOString();
const expires = new Date(now.getTime() + 5 * 60 * 1000).toISOString(); // 5 minutos
return { created, expires };
}
/**
* Construye el XML de solicitud de autenticación
*/
function buildAuthRequest(credential: Credential): string {
const timestamp = createTimestamp();
const uuid = randomUUID();
const certificate = credential.certificate();
const cerB64 = Buffer.from(certificate.pem().replace(/-----.*-----/g, '').replace(/\n/g, '')).toString('base64');
// Canonicalizar y firmar
const toDigestXml = `<u:Timestamp xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" u:Id="_0">` +
`<u:Created>${timestamp.created}</u:Created>` +
`<u:Expires>${timestamp.expires}</u:Expires>` +
`</u:Timestamp>`;
const digestValue = createHash('sha1').update(toDigestXml).digest('base64');
const signedInfoXml = `<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">` +
`<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>` +
`<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>` +
`<Reference URI="#_0">` +
`<Transforms>` +
`<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>` +
`</Transforms>` +
`<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>` +
`<DigestValue>${digestValue}</DigestValue>` +
`</Reference>` +
`</SignedInfo>`;
// Firmar con la llave privada (sign retorna binary string, convertir a base64)
const signatureBinary = credential.sign(signedInfoXml, 'sha1');
const signatureValue = Buffer.from(signatureBinary, 'binary').toString('base64');
const soapEnvelope = `<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
<u:Timestamp u:Id="_0">
<u:Created>${timestamp.created}</u:Created>
<u:Expires>${timestamp.expires}</u:Expires>
</u:Timestamp>
<o:BinarySecurityToken u:Id="uuid-${uuid}-1" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">${cerB64}</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
${signedInfoXml}
<SignatureValue>${signatureValue}</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference URI="#uuid-${uuid}-1" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>
<s:Body>
<Autentica xmlns="http://DescargaMasivaTerceros.gob.mx"/>
</s:Body>
</s:Envelope>`;
return soapEnvelope;
}
/**
* Extrae el token de la respuesta SOAP
*/
function parseAuthResponse(responseXml: string): SatToken {
const parser = new XMLParser({
ignoreAttributes: false,
removeNSPrefix: true,
});
const result = parser.parse(responseXml);
// Navegar la estructura de respuesta SOAP
const envelope = result.Envelope || result['s:Envelope'];
if (!envelope) {
throw new Error('Respuesta SOAP inválida');
}
const body = envelope.Body || envelope['s:Body'];
if (!body) {
throw new Error('No se encontró el cuerpo de la respuesta');
}
const autenticaResponse = body.AutenticaResponse;
if (!autenticaResponse) {
throw new Error('No se encontró AutenticaResponse');
}
const autenticaResult = autenticaResponse.AutenticaResult;
if (!autenticaResult) {
throw new Error('No se obtuvo token de autenticación');
}
// El token es un SAML assertion en base64
const token = autenticaResult;
// El token expira en 5 minutos según documentación SAT
const expiresAt = new Date(Date.now() + 5 * 60 * 1000);
return { token, expiresAt };
}
/**
* Autentica con el SAT usando la FIEL y obtiene un token
*/
export async function authenticate(credential: Credential): Promise<SatToken> {
const soapRequest = buildAuthRequest(credential);
try {
const response = await fetch(SAT_AUTH_URL, {
method: 'POST',
headers: {
'Content-Type': 'text/xml;charset=UTF-8',
'SOAPAction': 'http://DescargaMasivaTerceros.gob.mx/IAutenticacion/Autentica',
},
body: soapRequest,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error HTTP ${response.status}: ${errorText}`);
}
const responseXml = await response.text();
return parseAuthResponse(responseXml);
} catch (error: any) {
console.error('[SAT Auth Error]', error);
throw new Error(`Error al autenticar con el SAT: ${error.message}`);
}
}
/**
* Verifica si un token está vigente
*/
export function isTokenValid(token: SatToken): boolean {
return new Date() < token.expiresAt;
}

View File

@@ -0,0 +1,408 @@
import { XMLParser } from 'fast-xml-parser';
import { createHash, randomUUID } from 'crypto';
import type { Credential } from '@nodecfdi/credentials/node';
import type {
SatDownloadRequestResponse,
SatVerifyResponse,
SatPackageResponse,
CfdiSyncType
} from '@horux/shared';
const SAT_SOLICITUD_URL = 'https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/SolicitaDescargaService.svc';
const SAT_VERIFICA_URL = 'https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/VerificaSolicitudDescargaService.svc';
const SAT_DESCARGA_URL = 'https://cfdidescargamasaborrar.clouda.sat.gob.mx/DescargaMasivaService.svc';
type TipoSolicitud = 'CFDI' | 'Metadata';
interface RequestDownloadParams {
credential: Credential;
token: string;
rfc: string;
fechaInicio: Date;
fechaFin: Date;
tipoSolicitud: TipoSolicitud;
tipoCfdi: CfdiSyncType;
}
/**
* Formatea fecha a formato SAT (YYYY-MM-DDTHH:MM:SS)
*/
function formatSatDate(date: Date): string {
return date.toISOString().slice(0, 19);
}
/**
* Construye el XML de solicitud de descarga
*/
function buildDownloadRequest(params: RequestDownloadParams): string {
const { credential, token, rfc, fechaInicio, fechaFin, tipoSolicitud, tipoCfdi } = params;
const uuid = randomUUID();
const certificate = credential.certificate();
const cerB64 = Buffer.from(certificate.pem().replace(/-----.*-----/g, '').replace(/\n/g, '')).toString('base64');
// Construir el elemento de solicitud
const rfcEmisor = tipoCfdi === 'emitidos' ? rfc : undefined;
const rfcReceptor = tipoCfdi === 'recibidos' ? rfc : undefined;
const solicitudContent = `<des:RfcSolicitante>${rfc}</des:RfcSolicitante>` +
`<des:FechaInicial>${formatSatDate(fechaInicio)}</des:FechaInicial>` +
`<des:FechaFinal>${formatSatDate(fechaFin)}</des:FechaFinal>` +
`<des:TipoSolicitud>${tipoSolicitud}</des:TipoSolicitud>` +
(rfcEmisor ? `<des:RfcEmisor>${rfcEmisor}</des:RfcEmisor>` : '') +
(rfcReceptor ? `<des:RfcReceptor>${rfcReceptor}</des:RfcReceptor>` : '');
const solicitudToSign = `<des:SolicitaDescarga xmlns:des="http://DescargaMasivaTerceros.sat.gob.mx">${solicitudContent}</des:SolicitaDescarga>`;
const digestValue = createHash('sha1').update(solicitudToSign).digest('base64');
const signedInfoXml = `<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">` +
`<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>` +
`<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>` +
`<Reference URI="">` +
`<Transforms>` +
`<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>` +
`</Transforms>` +
`<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>` +
`<DigestValue>${digestValue}</DigestValue>` +
`</Reference>` +
`</SignedInfo>`;
const signatureBinary = credential.sign(signedInfoXml, 'sha1');
const signatureValue = Buffer.from(signatureBinary, 'binary').toString('base64');
return `<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:des="http://DescargaMasivaTerceros.sat.gob.mx" xmlns:xd="http://www.w3.org/2000/09/xmldsig#">
<s:Header/>
<s:Body>
<des:SolicitaDescarga>
<des:solicitud RfcSolicitante="${rfc}" FechaInicial="${formatSatDate(fechaInicio)}" FechaFinal="${formatSatDate(fechaFin)}" TipoSolicitud="${tipoSolicitud}"${rfcEmisor ? ` RfcEmisor="${rfcEmisor}"` : ''}${rfcReceptor ? ` RfcReceptor="${rfcReceptor}"` : ''}>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
${signedInfoXml}
<SignatureValue>${signatureValue}</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>${certificate.issuerAsRfc4514()}</X509IssuerName>
<X509SerialNumber>${certificate.serialNumber().bytes()}</X509SerialNumber>
</X509IssuerSerial>
<X509Certificate>${cerB64}</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</des:solicitud>
</des:SolicitaDescarga>
</s:Body>
</s:Envelope>`;
}
/**
* Solicita la descarga de CFDIs al SAT
*/
export async function requestDownload(params: RequestDownloadParams): Promise<SatDownloadRequestResponse> {
const soapRequest = buildDownloadRequest(params);
try {
const response = await fetch(SAT_SOLICITUD_URL, {
method: 'POST',
headers: {
'Content-Type': 'text/xml;charset=UTF-8',
'SOAPAction': 'http://DescargaMasivaTerceros.sat.gob.mx/ISolicitaDescargaService/SolicitaDescarga',
'Authorization': `WRAP access_token="${params.token}"`,
},
body: soapRequest,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error HTTP ${response.status}: ${errorText}`);
}
const responseXml = await response.text();
return parseDownloadRequestResponse(responseXml);
} catch (error: any) {
console.error('[SAT Download Request Error]', error);
throw new Error(`Error al solicitar descarga: ${error.message}`);
}
}
/**
* Parsea la respuesta de solicitud de descarga
*/
function parseDownloadRequestResponse(responseXml: string): SatDownloadRequestResponse {
const parser = new XMLParser({
ignoreAttributes: false,
removeNSPrefix: true,
attributeNamePrefix: '@_',
});
const result = parser.parse(responseXml);
const envelope = result.Envelope || result['s:Envelope'];
const body = envelope?.Body || envelope?.['s:Body'];
const respuesta = body?.SolicitaDescargaResponse?.SolicitaDescargaResult;
if (!respuesta) {
throw new Error('Respuesta inválida del SAT');
}
return {
idSolicitud: respuesta['@_IdSolicitud'] || '',
codEstatus: respuesta['@_CodEstatus'] || '',
mensaje: respuesta['@_Mensaje'] || '',
};
}
/**
* Verifica el estado de una solicitud de descarga
*/
export async function verifyRequest(
credential: Credential,
token: string,
rfc: string,
idSolicitud: string
): Promise<SatVerifyResponse> {
const certificate = credential.certificate();
const cerB64 = Buffer.from(certificate.pem().replace(/-----.*-----/g, '').replace(/\n/g, '')).toString('base64');
const verificaContent = `<des:VerificaSolicitudDescarga xmlns:des="http://DescargaMasivaTerceros.sat.gob.mx"><des:solicitud IdSolicitud="${idSolicitud}" RfcSolicitante="${rfc}"/></des:VerificaSolicitudDescarga>`;
const digestValue = createHash('sha1').update(verificaContent).digest('base64');
const signedInfoXml = `<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">` +
`<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>` +
`<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>` +
`<Reference URI="">` +
`<Transforms><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms>` +
`<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>` +
`<DigestValue>${digestValue}</DigestValue>` +
`</Reference>` +
`</SignedInfo>`;
const signatureBinary = credential.sign(signedInfoXml, 'sha1');
const signatureValue = Buffer.from(signatureBinary, 'binary').toString('base64');
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:des="http://DescargaMasivaTerceros.sat.gob.mx">
<s:Header/>
<s:Body>
<des:VerificaSolicitudDescarga>
<des:solicitud IdSolicitud="${idSolicitud}" RfcSolicitante="${rfc}">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
${signedInfoXml}
<SignatureValue>${signatureValue}</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>${certificate.issuerAsRfc4514()}</X509IssuerName>
<X509SerialNumber>${certificate.serialNumber().bytes()}</X509SerialNumber>
</X509IssuerSerial>
<X509Certificate>${cerB64}</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</des:solicitud>
</des:VerificaSolicitudDescarga>
</s:Body>
</s:Envelope>`;
try {
const response = await fetch(SAT_VERIFICA_URL, {
method: 'POST',
headers: {
'Content-Type': 'text/xml;charset=UTF-8',
'SOAPAction': 'http://DescargaMasivaTerceros.sat.gob.mx/IVerificaSolicitudDescargaService/VerificaSolicitudDescarga',
'Authorization': `WRAP access_token="${token}"`,
},
body: soapRequest,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error HTTP ${response.status}: ${errorText}`);
}
const responseXml = await response.text();
return parseVerifyResponse(responseXml);
} catch (error: any) {
console.error('[SAT Verify Error]', error);
throw new Error(`Error al verificar solicitud: ${error.message}`);
}
}
/**
* Parsea la respuesta de verificación
*/
function parseVerifyResponse(responseXml: string): SatVerifyResponse {
const parser = new XMLParser({
ignoreAttributes: false,
removeNSPrefix: true,
attributeNamePrefix: '@_',
});
const result = parser.parse(responseXml);
const envelope = result.Envelope || result['s:Envelope'];
const body = envelope?.Body || envelope?.['s:Body'];
const respuesta = body?.VerificaSolicitudDescargaResponse?.VerificaSolicitudDescargaResult;
if (!respuesta) {
throw new Error('Respuesta de verificación inválida');
}
// Extraer paquetes
let paquetes: string[] = [];
const paquetesNode = respuesta.IdsPaquetes;
if (paquetesNode) {
if (Array.isArray(paquetesNode)) {
paquetes = paquetesNode;
} else if (typeof paquetesNode === 'string') {
paquetes = [paquetesNode];
}
}
return {
codEstatus: respuesta['@_CodEstatus'] || '',
estadoSolicitud: parseInt(respuesta['@_EstadoSolicitud'] || '0', 10),
codigoEstadoSolicitud: respuesta['@_CodigoEstadoSolicitud'] || '',
numeroCfdis: parseInt(respuesta['@_NumeroCFDIs'] || '0', 10),
mensaje: respuesta['@_Mensaje'] || '',
paquetes,
};
}
/**
* Descarga un paquete de CFDIs
*/
export async function downloadPackage(
credential: Credential,
token: string,
rfc: string,
idPaquete: string
): Promise<SatPackageResponse> {
const certificate = credential.certificate();
const cerB64 = Buffer.from(certificate.pem().replace(/-----.*-----/g, '').replace(/\n/g, '')).toString('base64');
const descargaContent = `<des:PeticionDescargaMasivaTercerosEntrada xmlns:des="http://DescargaMasivaTerceros.sat.gob.mx"><des:peticionDescarga IdPaquete="${idPaquete}" RfcSolicitante="${rfc}"/></des:PeticionDescargaMasivaTercerosEntrada>`;
const digestValue = createHash('sha1').update(descargaContent).digest('base64');
const signedInfoXml = `<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">` +
`<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>` +
`<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>` +
`<Reference URI="">` +
`<Transforms><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms>` +
`<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>` +
`<DigestValue>${digestValue}</DigestValue>` +
`</Reference>` +
`</SignedInfo>`;
const signatureBinary = credential.sign(signedInfoXml, 'sha1');
const signatureValue = Buffer.from(signatureBinary, 'binary').toString('base64');
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:des="http://DescargaMasivaTerceros.sat.gob.mx">
<s:Header/>
<s:Body>
<des:PeticionDescargaMasivaTercerosEntrada>
<des:peticionDescarga IdPaquete="${idPaquete}" RfcSolicitante="${rfc}">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
${signedInfoXml}
<SignatureValue>${signatureValue}</SignatureValue>
<KeyInfo>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>${certificate.issuerAsRfc4514()}</X509IssuerName>
<X509SerialNumber>${certificate.serialNumber().bytes()}</X509SerialNumber>
</X509IssuerSerial>
<X509Certificate>${cerB64}</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</des:peticionDescarga>
</des:PeticionDescargaMasivaTercerosEntrada>
</s:Body>
</s:Envelope>`;
try {
const response = await fetch(SAT_DESCARGA_URL, {
method: 'POST',
headers: {
'Content-Type': 'text/xml;charset=UTF-8',
'SOAPAction': 'http://DescargaMasivaTerceros.sat.gob.mx/IDescargaMasivaService/Descargar',
'Authorization': `WRAP access_token="${token}"`,
},
body: soapRequest,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error HTTP ${response.status}: ${errorText}`);
}
const responseXml = await response.text();
return parseDownloadResponse(responseXml);
} catch (error: any) {
console.error('[SAT Download Package Error]', error);
throw new Error(`Error al descargar paquete: ${error.message}`);
}
}
/**
* Parsea la respuesta de descarga de paquete
*/
function parseDownloadResponse(responseXml: string): SatPackageResponse {
const parser = new XMLParser({
ignoreAttributes: false,
removeNSPrefix: true,
attributeNamePrefix: '@_',
});
const result = parser.parse(responseXml);
const envelope = result.Envelope || result['s:Envelope'];
const body = envelope?.Body || envelope?.['s:Body'];
const respuesta = body?.RespuestaDescargaMasivaTercerosSalida?.Paquete;
if (!respuesta) {
throw new Error('No se pudo obtener el paquete');
}
return {
paquete: respuesta,
};
}
/**
* Estados de solicitud del SAT
*/
export const SAT_REQUEST_STATES = {
ACCEPTED: 1,
IN_PROGRESS: 2,
COMPLETED: 3,
ERROR: 4,
REJECTED: 5,
EXPIRED: 6,
} as const;
/**
* Verifica si la solicitud está completa
*/
export function isRequestComplete(estadoSolicitud: number): boolean {
return estadoSolicitud === SAT_REQUEST_STATES.COMPLETED;
}
/**
* Verifica si la solicitud falló
*/
export function isRequestFailed(estadoSolicitud: number): boolean {
return (
estadoSolicitud === SAT_REQUEST_STATES.ERROR ||
estadoSolicitud === SAT_REQUEST_STATES.REJECTED ||
estadoSolicitud === SAT_REQUEST_STATES.EXPIRED
);
}
/**
* Verifica si la solicitud está en progreso
*/
export function isRequestInProgress(estadoSolicitud: number): boolean {
return (
estadoSolicitud === SAT_REQUEST_STATES.ACCEPTED ||
estadoSolicitud === SAT_REQUEST_STATES.IN_PROGRESS
);
}

35
pnpm-lock.yaml generated
View File

@@ -26,6 +26,9 @@ importers:
'@prisma/client': '@prisma/client':
specifier: ^5.22.0 specifier: ^5.22.0
version: 5.22.0(prisma@5.22.0) version: 5.22.0(prisma@5.22.0)
adm-zip:
specifier: ^0.5.16
version: 0.5.16
bcryptjs: bcryptjs:
specifier: ^2.4.3 specifier: ^2.4.3
version: 2.4.3 version: 2.4.3
@@ -41,6 +44,9 @@ importers:
express: express:
specifier: ^4.21.0 specifier: ^4.21.0
version: 4.22.1 version: 4.22.1
fast-xml-parser:
specifier: ^5.3.3
version: 5.3.3
helmet: helmet:
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.1.0 version: 8.1.0
@@ -54,6 +60,9 @@ importers:
specifier: ^3.23.0 specifier: ^3.23.0
version: 3.25.76 version: 3.25.76
devDependencies: devDependencies:
'@types/adm-zip':
specifier: ^0.5.7
version: 0.5.7
'@types/bcryptjs': '@types/bcryptjs':
specifier: ^2.4.6 specifier: ^2.4.6
version: 2.4.6 version: 2.4.6
@@ -958,6 +967,9 @@ packages:
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@types/adm-zip@0.5.7':
resolution: {integrity: sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==}
'@types/bcryptjs@2.4.6': '@types/bcryptjs@2.4.6':
resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==} resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==}
@@ -1052,6 +1064,10 @@ packages:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
adm-zip@0.5.16:
resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
engines: {node: '>=12.0'}
any-promise@1.3.0: any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
@@ -1424,6 +1440,10 @@ packages:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'} engines: {node: '>=8.6.0'}
fast-xml-parser@5.3.3:
resolution: {integrity: sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==}
hasBin: true
fastq@1.20.1: fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
@@ -2112,6 +2132,9 @@ packages:
string_decoder@1.3.0: string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strnum@2.1.2:
resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==}
styled-jsx@5.1.1: styled-jsx@5.1.1:
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@@ -2977,6 +3000,10 @@ snapshots:
'@tanstack/table-core@8.21.3': {} '@tanstack/table-core@8.21.3': {}
'@types/adm-zip@0.5.7':
dependencies:
'@types/node': 22.19.7
'@types/bcryptjs@2.4.6': {} '@types/bcryptjs@2.4.6': {}
'@types/body-parser@1.19.6': '@types/body-parser@1.19.6':
@@ -3079,6 +3106,8 @@ snapshots:
mime-types: 2.1.35 mime-types: 2.1.35
negotiator: 0.6.3 negotiator: 0.6.3
adm-zip@0.5.16: {}
any-promise@1.3.0: {} any-promise@1.3.0: {}
anymatch@3.1.3: anymatch@3.1.3:
@@ -3520,6 +3549,10 @@ snapshots:
merge2: 1.4.1 merge2: 1.4.1
micromatch: 4.0.8 micromatch: 4.0.8
fast-xml-parser@5.3.3:
dependencies:
strnum: 2.1.2
fastq@1.20.1: fastq@1.20.1:
dependencies: dependencies:
reusify: 1.1.0 reusify: 1.1.0
@@ -4185,6 +4218,8 @@ snapshots:
dependencies: dependencies:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
strnum@2.1.2: {}
styled-jsx@5.1.1(react@18.3.1): styled-jsx@5.1.1(react@18.3.1):
dependencies: dependencies:
client-only: 0.0.1 client-only: 0.0.1