first commit

This commit is contained in:
Isuru Bimsara
2026-08-14 22:46:02 +05:30
commit 81d0e65686
132 changed files with 24398 additions and 0 deletions
@@ -0,0 +1,417 @@
/**
* Copyright (c) 2026 Niolla
* All rights reserved.
*
* This source code is proprietary and confidential.
* Unauthorized copying, modification, distribution, or use
* of this file, via any medium, is strictly prohibited.
*/
// app/controllers/generateDocument.controller.js
const fs = require("fs");
const path = require("path");
const documentQueue = require("../queues/document.queue");
const { createDocumentData } = require("../utils/document.utill");
const {
generateId,
generateDocumentReferenceNo,
} = require("../utils/idGen.util");
const { GetObjectCommand, DeleteObjectCommand } = require("@aws-sdk/client-s3");
const {log} = require("../utils/consoleLog.utill");
const s3 = require("../config/s3.config");
const db = require("../models");
const Document = db.Document;
const DocumentType = db.DocumentType;
const normalizeDocumentData = (value) => {
if (!value) {
return {};
}
if (typeof value === "string") {
try {
return JSON.parse(value);
} catch (error) {
return {};
}
}
return value;
};
/**
* Get available document types
*/
exports.getAvailableDocumentTypes = async (req, res) => {
try {
const doc_types = await DocumentType.findAll({
attributes: ["doc_type_name"],
});
const types = doc_types.map((t) => t.doc_type_name);
return res.json({
success: true,
availableDocumentTypes: types,
note: "Use these document type names in your requests (case-insensitive)",
});
} catch (error) {
log("Error fetching document types:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};
// Get Saved documents
exports.getSavedDocuments = async (req, res) => {
try {
const { documentType } = req.body;
if (!documentType) {
return res.status(400).json({
success: false,
message: "documentType query parameter is required",
});
}
// Fetch saved documents based on documentType
const savedDocuments = await Document.findAll({
where: { doc_type: documentType.toUpperCase() },
order: [["createdAt", "DESC"]],
exclude: ["data"],
});
// extract only necessary fields to return
const formattedDocuments = savedDocuments.map((doc) => ({
doc_id: doc.doc_id,
reference_no: doc.reference_no,
doc_type: doc.doc_type,
status: doc.status,
createdAt: doc.createdAt,
updatedAt: doc.updatedAt,
}));
return res.json({
success: true,
savedDocuments: formattedDocuments,
});
} catch (error) {
log("Error fetching saved documents:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};
// Get specific document data by doc_id
exports.getDocumentData = async (req, res) => {
try {
const { docId } = req.params;
if (!docId) {
return res.status(400).json({
success: false,
message: "docId parameter is required",
});
}
const document = await Document.findOne({
where: { doc_id: docId },
});
if (!document) {
return res.status(404).json({
success: false,
message: "Document not found",
});
}
return res.json({
success: true,
document: {
doc_id: document.doc_id,
reference_no: document.reference_no,
doc_type: document.doc_type,
data: document.data,
status: document.status,
createdAt: document.createdAt,
updatedAt: document.updatedAt,
},
});
} catch (error) {
log("Error fetching document data:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
}
exports.generateReferenceNo = async (req, res) => {
try {
const { documentType } = req.params;
if (!documentType) {
return res.status(400).json({
success: false,
message: "documentType is required",
});
}
// Generate reference number
const reference_no = await generateDocumentReferenceNo(documentType);
return res.json({
success: true,
reference_no,
});
} catch (error) {
log("Error generating reference number:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};
exports.generateDraftDocument = async (req, res) => {
try {
const { document } = req.body;
const documentData = normalizeDocumentData(req.body.documentData || req.body.data || req.body);
// Validation
if (!document || !documentData) {
return res.status(400).json({
success: false,
message: "document and documentData are required",
});
}
let documentDetails;
if (document !== "PRECOST") {
// Save document details before generating
documentDetails = await createDocumentData(
document,
documentData,
"DRAFT",
);
} else {
return res.status(400).json({
success: false,
message:
"Invalid document type, This document type is not allowed to be generated as draft",
});
}
return res.status(201).json({
success: true,
message: "Draft document created successfully",
documentDetails,
});
} catch (error) {
log("Error generating draft document:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};
/**
* Generate document asynchronously
* Returns jobId immediately
*/
exports.generateDocument = async (req, res) => {
try {
const { document, documentType } = req.body;
const documentData = normalizeDocumentData(req.body.documentData || req.body.data || req.body);
// Validation
if (!document || !documentType || !documentData) {
return res.status(400).json({
success: false,
message: "document, documentType, and documentData are required",
});
}
console.log(
`📨 generateDocument request: document="${document}", documentType="${documentType}"`,
);
if (document !== "PRECOST") {
// Save document details before generating
// If doc_id exists, finalize (update existing); otherwise create as DRAFT
const status = documentData.doc_id ? "FINAL" : "DRAFT";
await createDocumentData(document, documentData, status);
}
// Add job to queue
const job = await documentQueue.add("generate-document", {
document,
documentType,
data: documentData,
});
log(
`📋 Document generation job queued: ${job.id} (document="${document}", type="${documentType}")`,
);
return res.status(202).json({
success: true,
message: "Document generation started",
jobId: job.id,
});
} catch (error) {
log("Error queuing document generation:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};
/**
* Get job status and result
*/
exports.getJobStatus = async (req, res) => {
try {
const { jobId } = req.params;
if (!jobId) {
return res.status(400).json({
success: false,
message: "jobId is required",
});
}
// Get job from queue
const job = await documentQueue.getJob(jobId);
if (!job) {
return res.status(404).json({
success: false,
message: "Job not found",
});
}
// Get job state
const state = await job.getState();
const result = job.returnvalue;
const failedReason = job.failedReason;
return res.json({
success: true,
jobId: job.id,
state, // "waiting" | "active" | "completed" | "failed" | "delayed"
result: state === "completed" ? result : null,
error: state === "failed" ? failedReason : null,
attempts: job.attemptsMade,
stacktrace: job.stacktrace,
});
} catch (error) {
log("Error fetching job status:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};
/**
* Download generated document
*/
exports.downloadDocument = async (req, res) => {
try {
const { uuid } = req.params;
const key = `uploads/${uuid}.pdf`;
const command = new GetObjectCommand({
Bucket: process.env.AWS_S3_BUCKET_NAME,
Key: key,
});
const response = await s3.send(command);
res.setHeader(
"Content-Type",
response.ContentType || "application/octet-stream",
);
res.setHeader("Content-Disposition", `attachment; filename="${uuid}.pdf"`);
response.Body.pipe(res);
res.on("finish", async () => {
try {
await s3.send(
new DeleteObjectCommand({
Bucket: process.env.AWS_S3_BUCKET_NAME,
Key: key,
}),
);
log(`Deleted from S3: ${key}`);
} catch (err) {
log(`Failed to delete ${key}:`, err.message);
}
});
} catch (error) {
log("Download error:", error.message);
return res.status(404).json({
success: false,
message: "Document not found",
});
}
};
/**
* Cancel/delete a job
*/
exports.cancelJob = async (req, res) => {
try {
const { jobId } = req.params;
if (!jobId) {
return res.status(400).json({
success: false,
message: "jobId is required",
});
}
const job = await documentQueue.getJob(jobId);
if (!job) {
return res.status(404).json({
success: false,
message: "Job not found",
});
}
await job.remove();
return res.json({
success: true,
message: "Job cancelled successfully",
jobId,
});
} catch (error) {
log("Error cancelling job:", error.message);
return res.status(500).json({
success: false,
message: error.message,
});
}
};