feat: add dual filesystem storage for FIEL credentials
Save encrypted .cer, .key, and metadata to FIEL_STORAGE_PATH alongside the existing DB storage. Each file has separate .iv and .tag sidecar files. Filesystem failure is non-blocking (logs warning, DB remains primary). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
import { Credential } from '@nodecfdi/credentials/node';
|
import { Credential } from '@nodecfdi/credentials/node';
|
||||||
|
import { writeFile, mkdir } from 'fs/promises';
|
||||||
|
import { join } from 'path';
|
||||||
import { prisma } from '../config/database.js';
|
import { prisma } from '../config/database.js';
|
||||||
import { encryptFielCredentials, decryptFielCredentials } from './sat/sat-crypto.service.js';
|
import { env } from '../config/env.js';
|
||||||
|
import { encryptFielCredentials, encrypt, decryptFielCredentials } from './sat/sat-crypto.service.js';
|
||||||
import type { FielStatus } from '@horux/shared';
|
import type { FielStatus } from '@horux/shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,6 +113,37 @@ export async function uploadFiel(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Save encrypted files to filesystem (dual storage)
|
||||||
|
try {
|
||||||
|
const fielDir = join(env.FIEL_STORAGE_PATH, rfc.toUpperCase());
|
||||||
|
await mkdir(fielDir, { recursive: true, mode: 0o700 });
|
||||||
|
|
||||||
|
// Re-encrypt for filesystem (independent keys from DB)
|
||||||
|
const fsEncrypted = encryptFielCredentials(cerData, keyData, password);
|
||||||
|
|
||||||
|
await writeFile(join(fielDir, 'certificate.cer.enc'), fsEncrypted.encryptedCer, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'certificate.cer.iv'), fsEncrypted.cerIv, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'certificate.cer.tag'), fsEncrypted.cerTag, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'private_key.key.enc'), fsEncrypted.encryptedKey, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'private_key.key.iv'), fsEncrypted.keyIv, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'private_key.key.tag'), fsEncrypted.keyTag, { mode: 0o600 });
|
||||||
|
|
||||||
|
// Encrypt and store metadata
|
||||||
|
const metadata = JSON.stringify({
|
||||||
|
serial: serialNumber,
|
||||||
|
validFrom: validFrom.toISOString(),
|
||||||
|
validUntil: validUntil.toISOString(),
|
||||||
|
uploadedAt: new Date().toISOString(),
|
||||||
|
rfc: rfc.toUpperCase(),
|
||||||
|
});
|
||||||
|
const metaEncrypted = encrypt(Buffer.from(metadata, 'utf-8'));
|
||||||
|
await writeFile(join(fielDir, 'metadata.json.enc'), metaEncrypted.encrypted, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'metadata.json.iv'), metaEncrypted.iv, { mode: 0o600 });
|
||||||
|
await writeFile(join(fielDir, 'metadata.json.tag'), metaEncrypted.tag, { mode: 0o600 });
|
||||||
|
} catch (fsError) {
|
||||||
|
console.error('[FIEL] Filesystem storage failed (DB storage OK):', fsError);
|
||||||
|
}
|
||||||
|
|
||||||
const daysUntilExpiration = Math.ceil(
|
const daysUntilExpiration = Math.ceil(
|
||||||
(validUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
(validUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user