fix(storage): route every file op through getStorageBackend()
Removes 12 direct minioClient.{put,get,remove}Object call sites that
bypassed the pluggable storage abstraction. Filesystem-mode deploys
(MULTI_NODE_DEPLOYMENT=false, storage_backend=filesystem) silently
broke at every site: GDPR export, invoice PDF, EOI generation, portal
download, file upload, folder create/rename/delete, signed PDF land,
maintenance cleanup, etc. Each site now resolves the active backend
and uses its put/get/delete + the new presignDownloadUrl() helper.
Folder marker objects in /files/folders/* keep the same on-the-wire
shape but route through the backend. A future refactor should move
folder bookkeeping to a DB-backed virtual-folder table (see audit
HIGH §3 follow-up note in the route file).
Sites left untouched: src/lib/services/system-monitoring.service.ts
and src/app/api/ready/route.ts use minioClient.bucketExists as an S3-
specific health probe — those are correctly mode-aware and stay.
Refs: docs/audit-comprehensive-2026-05-05.md HIGH §3 (auditor-D Issue 1)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,8 @@ import { notifications } from '@/lib/db/schema/operations';
|
||||
import { files } from '@/lib/db/schema/documents';
|
||||
import { ports } from '@/lib/db/schema/ports';
|
||||
import { generatePdf } from '@/lib/pdf/generate';
|
||||
import { minioClient, getPresignedUrl, buildStoragePath } from '@/lib/minio/index';
|
||||
import { buildStoragePath } from '@/lib/minio/index';
|
||||
import { getStorageBackend, presignDownloadUrl } from '@/lib/storage';
|
||||
import { emitToRoom } from '@/lib/socket/server';
|
||||
import { getQueue } from '@/lib/queue';
|
||||
import { env } from '@/lib/env';
|
||||
@@ -67,11 +68,7 @@ type ReportType = keyof typeof REPORT_TYPE_MAP;
|
||||
|
||||
// ─── requestReport ────────────────────────────────────────────────────────────
|
||||
|
||||
export async function requestReport(
|
||||
portId: string,
|
||||
userId: string,
|
||||
data: RequestReportInput,
|
||||
) {
|
||||
export async function requestReport(portId: string, userId: string, data: RequestReportInput) {
|
||||
const [report] = await db
|
||||
.insert(generatedReports)
|
||||
.values({
|
||||
@@ -163,7 +160,7 @@ export async function getDownloadUrl(reportId: string, portId: string) {
|
||||
throw new NotFoundError('File');
|
||||
}
|
||||
|
||||
const url = await getPresignedUrl(file.storagePath);
|
||||
const url = await presignDownloadUrl(file.storagePath);
|
||||
return { url };
|
||||
}
|
||||
|
||||
@@ -208,7 +205,9 @@ export async function generateReport(reportJobId: string): Promise<void> {
|
||||
const portSlug = port?.slug ?? 'port';
|
||||
|
||||
// 6. Build inputs (pass portName)
|
||||
const inputs = (config.buildInputs as (data: unknown, portName: string) => Record<string, string>[])(data, portName);
|
||||
const inputs = (
|
||||
config.buildInputs as (data: unknown, portName: string) => Record<string, string>[]
|
||||
)(data, portName);
|
||||
|
||||
// 7. Generate PDF
|
||||
const pdfBytes = await generatePdf(config.template, inputs);
|
||||
@@ -217,15 +216,13 @@ export async function generateReport(reportJobId: string): Promise<void> {
|
||||
const fileId = crypto.randomUUID();
|
||||
const storagePath = buildStoragePath(portSlug, 'reports', reportJobId, fileId, 'pdf');
|
||||
|
||||
// 9. Upload PDF to MinIO
|
||||
// 9. Upload PDF via the active storage backend (filesystem or s3)
|
||||
const buffer = Buffer.from(pdfBytes);
|
||||
await minioClient.putObject(
|
||||
env.MINIO_BUCKET,
|
||||
storagePath,
|
||||
buffer,
|
||||
buffer.length,
|
||||
{ 'Content-Type': 'application/pdf', 'report-type': reportType },
|
||||
);
|
||||
const backend = await getStorageBackend();
|
||||
await backend.put(storagePath, buffer, {
|
||||
contentType: 'application/pdf',
|
||||
sizeBytes: buffer.length,
|
||||
});
|
||||
|
||||
// 10. Insert into files table
|
||||
const [fileRecord] = await db
|
||||
|
||||
Reference in New Issue
Block a user