initial commit
This commit is contained in:
+179
@@ -0,0 +1,179 @@
|
||||
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<PatientDocument> => {
|
||||
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<PatientDocument[]> => {
|
||||
return await db.patientDocument.findMany({
|
||||
where: { patientId },
|
||||
orderBy: { uploadedAt: "desc" },
|
||||
});
|
||||
},
|
||||
|
||||
// Get a specific document by ID
|
||||
getDocumentById: async (id: number): Promise<PatientDocument | null> => {
|
||||
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<boolean> => {
|
||||
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<CreatePatientDocument>): Promise<PatientDocument | null> => {
|
||||
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 };
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user