This commit is contained in:
David Arranz 2026-02-13 11:39:26 +01:00
parent 52ae21cb57
commit 770fb33bb0
18 changed files with 49 additions and 35 deletions

2
.vscode/launch.json vendored
View File

@ -1,5 +1,5 @@
{
"version": "0.4.7",
"version": "0.4.8",
"configurations": [
{
"name": "WEB: Vite (Chrome)",

View File

@ -1,6 +1,6 @@
{
"name": "@erp/factuges-server",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"scripts": {
"build": "tsup src/index.ts --config tsup.config.ts",

View File

@ -1,7 +1,7 @@
{
"name": "@erp/factuges-web",
"private": true,
"version": "0.4.7",
"version": "0.4.8",
"type": "module",
"scripts": {
"dev": "vite --host --clearScreen false",

View File

@ -1,6 +1,6 @@
{
"name": "@erp/auth",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,6 +1,6 @@
{
"name": "@erp/core",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,7 +1,13 @@
import { createHash } from "node:crypto";
export class DocumentStorageKeyFactory {
static fromMetadataRecord(metadata: Record<string, unknown>): string {
return createHash("sha256").update(JSON.stringify(metadata)).digest("hex");
static fromMetadataRecord(metadata: Record<string, unknown>): {
paths: string[];
storageKey: string;
} {
return {
paths: [String(metadata.companySlug), String(metadata.documentType)],
storageKey: createHash("sha256").update(JSON.stringify(metadata)).digest("hex"),
};
}
}

View File

@ -8,7 +8,7 @@ export interface IDocumentStorage {
* - Best-effort
* - Nunca lanza (errores se gestionan internamente)
*/
existsKeyStorage(storageKey: string): Promise<Boolean>;
existsKeyStorage(storageKey: string, paths: string[]): Promise<Boolean>;
/**
* Recupera un documento guardado.
@ -17,7 +17,7 @@ export interface IDocumentStorage {
* - Best-effort
* - Nunca lanza (errores se gestionan internamente)
*/
readDocument(storageKey: string): Promise<IDocument | null>;
readDocument(storageKey: string, paths: string[]): Promise<IDocument | null>;
/**
* Persiste un documento generado.

View File

@ -1,6 +1,8 @@
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
import path from "node:path";
import { buildSafePath } from "@repo/rdx-utils";
import {
DocumentStorageKeyFactory,
type IDocument,
@ -15,11 +17,15 @@ import {
* - No afecta al flujo del caso de uso
*/
export class FilesystemDocumentStorage implements IDocumentStorage {
public constructor(private readonly basePath: string) {}
public constructor(private readonly docRootPath: string) {}
async existsKeyStorage(storageKey: string, paths: string[]): Promise<Boolean> {
// Ejemplo: .../signed-documents/770c138fc58548f72029c1ed4f3670e94be4cc7a0fab9f7f0b84d0aef77f43ae
// paths: => [".../signed-documents"],
// storageKey: => 770c138fc58548f72029c1ed4f3670e94be4cc7a0fab9f7f0b84d0aef77f43ae
async existsKeyStorage(storageKey: string): Promise<Boolean> {
try {
const dir = this.resolveDirFromStorageKey(storageKey);
const dir = this.resolveDir(storageKey, paths);
return (await stat(dir)).isDirectory();
} catch {
// Consistente con saveDocument: best-effort
@ -27,12 +33,12 @@ export class FilesystemDocumentStorage implements IDocumentStorage {
}
}
async readDocument(storageKey: string) {
async readDocument(storageKey: string, paths: string[]) {
try {
const dir = this.resolveDirFromStorageKey(storageKey);
const dir = this.resolveDir(storageKey, paths);
const payload = await readFile(path.join(dir, "document.bin"));
const metaRaw = JSON.parse(await readFile(path.join(dir, "document.meta.json"), "utf-8"));
const metaRaw = await readFile(path.join(dir, "document.meta.json"), "utf-8");
const meta = JSON.parse(metaRaw) as {
mimeType: string;
@ -78,15 +84,15 @@ export class FilesystemDocumentStorage implements IDocumentStorage {
}
private resolveDirFromMetadataRecord(metadataRecord: Record<string, unknown>): string {
/**
* El storage NO decide claves semánticas.
* Se limita a generar un path técnico estable.
*/
const storageKey = DocumentStorageKeyFactory.fromMetadataRecord(metadataRecord);
return this.resolveDirFromStorageKey(storageKey);
const { paths, storageKey } = DocumentStorageKeyFactory.fromMetadataRecord(metadataRecord);
return this.resolveDir(storageKey, paths);
}
private resolveDirFromStorageKey(storageKey: string): string {
return path.join(this.basePath, storageKey);
private resolveDir(storageKey: string, paths: string[]): string {
return buildSafePath({
basePath: this.docRootPath,
segments: [...paths, storageKey],
});
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@erp/customer-invoices",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -21,23 +21,25 @@ export class IssuedInvoiceSignedDocumentCachePreProcessor implements IDocumentPr
async tryResolve(metadata: IDocumentMetadata): Promise<IDocument | null> {
const metadataRecord = metadata as unknown as Record<string, unknown>;
try {
const storageKey = DocumentStorageKeyFactory.fromMetadataRecord(metadataRecord);
const { paths, storageKey } = DocumentStorageKeyFactory.fromMetadataRecord(metadataRecord);
if (!storageKey) {
return null;
}
const exists = await this.docStorage.existsKeyStorage(storageKey);
const exists = await this.docStorage.existsKeyStorage(storageKey, paths);
if (!exists) {
return null;
}
const document = await this.docStorage.readDocument(storageKey);
logger.info(`✅ Found Server cached document for key ${storageKey}`);
const document = await this.docStorage.readDocument(storageKey, paths);
if (!this.isValid(document)) {
logger.warn(`Storage key ${storageKey} not exists!`, {
lable: "IssuedInvoiceSignedDocumentCachePreProcessor",
logger.warn(`Corrupted or invalid cached document for key ${storageKey}`, {
label: "IssuedInvoiceSignedDocumentCachePreProcessor",
});
return null;
}

View File

@ -1,6 +1,6 @@
{
"name": "@erp/customers",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,6 +1,6 @@
{
"name": "@erp/doc-numbering",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,6 +1,6 @@
{
"name": "@repo/rdx-criteria",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,6 +1,6 @@
{
"name": "@repo/rdx-ddd",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,6 +1,6 @@
{
"name": "@repo/rdx-logger",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -1,6 +1,6 @@
{
"name": "@repo/rdx-utils",
"version": "0.4.7",
"version": "0.4.8",
"private": true,
"type": "module",
"sideEffects": false,