import { PatientDocument, CreatePatientDocument } from "@repo/db/types"; import { prisma as db } from "@repo/db/client"; import path from "path"; import fs from "fs/promises"; import { randomBytes } from "crypto"; const UPLOAD_DIR = path.join(process.cwd(), "uploads", "patient-documents"); // Get local file path from URL const getLocalFilePath = (fileUrl: string): string => { const filename = fileUrl.split('/').pop() || ''; return path.join(UPLOAD_DIR, filename); }; // Get the base URL for serving files const getBaseUrl = (): string => { // For development, use localhost instead of 0.0.0.0 const host = process.env.HOST === '0.0.0.0' ? 'localhost' : (process.env.HOST || 'localhost'); const port = process.env.PORT || '5000'; const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http'; return `${protocol}://${host}:${port}`; }; // Ensure upload directory exists const ensureUploadDir = async () => { try { await fs.access(UPLOAD_DIR); } catch { await fs.mkdir(UPLOAD_DIR, { recursive: true }); } }; // Generate unique filename const generateUniqueFilename = (originalName: string): string => { const ext = path.extname(originalName); const name = path.basename(originalName, ext); const timestamp = Date.now(); const random = randomBytes(4).toString("hex"); return `${name}-${timestamp}-${random}${ext}`; }; export const patientDocumentsStorage = { // Create a new patient document createPatientDocument: async ( patientId: number, filename: string, originalName: string, mimeType: string, fileSize: number, buffer: Buffer ): Promise => { await ensureUploadDir(); const uniqueFilename = generateUniqueFilename(filename); const localFilePath = path.join(UPLOAD_DIR, uniqueFilename); // Save file to disk await fs.writeFile(localFilePath, buffer); // Create the full URL for accessing the file using the uploads directory structure const fileUrl = `${getBaseUrl()}/uploads/patient-documents/${uniqueFilename}`; // Create database record with full URL const document = await db.patientDocument.create({ data: { patientId, filename: uniqueFilename, originalName, mimeType, fileSize: BigInt(fileSize), filePath: fileUrl, // Store the full URL instead of local path }, }); return document; }, // Get all documents for a patient getDocumentsByPatientId: async (patientId: number): Promise => { return await db.patientDocument.findMany({ where: { patientId }, orderBy: { uploadedAt: "desc" }, }); }, // Get a specific document by ID getDocumentById: async (id: number): Promise => { return await db.patientDocument.findUnique({ where: { id }, include: { patient: { select: { firstName: true, lastName: true, }, }, }, }); }, // Get document file content getDocumentFile: async (id: number): Promise<{ buffer: Buffer; document: PatientDocument } | null> => { const document = await db.patientDocument.findUnique({ where: { id }, }); if (!document) { return null; } try { // Get local file path from the stored URL const localFilePath = getLocalFilePath(document.filePath); const buffer = await fs.readFile(localFilePath); return { buffer, document }; } catch (error) { console.error("Error reading file:", error); return null; } }, // Delete a document deleteDocument: async (id: number): Promise => { const document = await db.patientDocument.findUnique({ where: { id }, }); if (!document) { return false; } try { // Get local file path from the stored URL const localFilePath = getLocalFilePath(document.filePath); // Delete file from disk await fs.unlink(localFilePath); // Delete database record await db.patientDocument.delete({ where: { id }, }); return true; } catch (error) { console.error("Error deleting document:", error); return false; } }, // Update document metadata updateDocument: async (id: number, data: Partial): Promise => { return await db.patientDocument.update({ where: { id }, data, }); }, // Get documents with pagination getDocumentsByPatientIdPaginated: async ( patientId: number, limit: number, offset: number ): Promise<{ documents: PatientDocument[]; total: number }> => { const [documents, total] = await Promise.all([ db.patientDocument.findMany({ where: { patientId }, orderBy: { uploadedAt: "desc" }, skip: offset, take: limit, }), db.patientDocument.count({ where: { patientId }, }), ]); return { documents, total }; }, };