288 lines
12 KiB
TypeScript
288 lines
12 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import { WorkflowService } from '@services/workflow.service';
|
|
import { validateCreateWorkflow, validateUpdateWorkflow } from '@validators/workflow.validator';
|
|
import { ResponseHandler } from '@utils/responseHandler';
|
|
import type { AuthenticatedRequest } from '../types/express';
|
|
import { Priority } from '../types/common.types';
|
|
import type { UpdateWorkflowRequest } from '../types/workflow.types';
|
|
import { Document } from '@models/Document';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import crypto from 'crypto';
|
|
|
|
const workflowService = new WorkflowService();
|
|
|
|
export class WorkflowController {
|
|
async createWorkflow(req: AuthenticatedRequest, res: Response): Promise<void> {
|
|
try {
|
|
const validatedData = validateCreateWorkflow(req.body);
|
|
// Convert string literal priority to enum
|
|
const workflowData = {
|
|
...validatedData,
|
|
priority: validatedData.priority as Priority
|
|
};
|
|
const workflow = await workflowService.createWorkflow(req.user.userId, workflowData);
|
|
|
|
ResponseHandler.success(res, workflow, 'Workflow created successfully', 201);
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to create workflow', 400, errorMessage);
|
|
}
|
|
}
|
|
|
|
// Multipart create: accepts payload JSON and files[]
|
|
async createWorkflowMultipart(req: AuthenticatedRequest, res: Response): Promise<void> {
|
|
try {
|
|
const userId = req.user?.userId;
|
|
if (!userId) {
|
|
ResponseHandler.error(res, 'Unauthorized', 401);
|
|
return;
|
|
}
|
|
|
|
const raw = String(req.body?.payload || '');
|
|
if (!raw) {
|
|
ResponseHandler.error(res, 'payload is required', 400);
|
|
return;
|
|
}
|
|
const parsed = JSON.parse(raw);
|
|
const validated = validateCreateWorkflow(parsed);
|
|
const workflowData = { ...validated, priority: validated.priority as Priority } as any;
|
|
|
|
const workflow = await workflowService.createWorkflow(userId, workflowData);
|
|
|
|
// Attach files as documents (category defaults to SUPPORTING)
|
|
const files = (req as any).files as Express.Multer.File[] | undefined;
|
|
const category = (req.body?.category as string) || 'OTHER';
|
|
const docs: any[] = [];
|
|
if (files && files.length > 0) {
|
|
for (const file of files) {
|
|
const buffer = fs.readFileSync(file.path);
|
|
const checksum = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
const extension = path.extname(file.originalname).replace('.', '').toLowerCase();
|
|
const doc = await Document.create({
|
|
requestId: workflow.requestId,
|
|
uploadedBy: userId,
|
|
fileName: path.basename(file.filename || file.originalname),
|
|
originalFileName: file.originalname,
|
|
fileType: extension,
|
|
fileExtension: extension,
|
|
fileSize: file.size,
|
|
filePath: file.path,
|
|
storageUrl: `/uploads/${path.basename(file.path)}`,
|
|
mimeType: file.mimetype,
|
|
checksum,
|
|
isGoogleDoc: false,
|
|
googleDocUrl: null as any,
|
|
category: category || 'OTHER',
|
|
version: 1,
|
|
parentDocumentId: null as any,
|
|
isDeleted: false,
|
|
downloadCount: 0,
|
|
} as any);
|
|
docs.push(doc);
|
|
}
|
|
}
|
|
|
|
ResponseHandler.success(res, { requestId: workflow.requestId, documents: docs }, 'Workflow created with documents', 201);
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to create workflow', 400, errorMessage);
|
|
}
|
|
}
|
|
|
|
async getWorkflow(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const workflow = await workflowService.getWorkflowById(id);
|
|
|
|
if (!workflow) {
|
|
ResponseHandler.notFound(res, 'Workflow not found');
|
|
return;
|
|
}
|
|
|
|
ResponseHandler.success(res, workflow, 'Workflow retrieved successfully');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to get workflow', 500, errorMessage);
|
|
}
|
|
}
|
|
|
|
async getWorkflowDetails(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const { id } = req.params as any;
|
|
const result = await workflowService.getWorkflowDetails(id);
|
|
if (!result) {
|
|
ResponseHandler.notFound(res, 'Workflow not found');
|
|
return;
|
|
}
|
|
ResponseHandler.success(res, result, 'Workflow details fetched');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to fetch workflow details', 500, errorMessage);
|
|
}
|
|
}
|
|
|
|
async listWorkflows(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
|
|
const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
|
|
const result = await workflowService.listWorkflows(page, limit);
|
|
ResponseHandler.success(res, result, 'Workflows fetched');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to list workflows', 500, errorMessage);
|
|
}
|
|
}
|
|
|
|
async listMyRequests(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
|
|
const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
|
|
const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
|
|
const result = await workflowService.listMyRequests(userId, page, limit);
|
|
ResponseHandler.success(res, result, 'My requests fetched');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to fetch my requests', 500, errorMessage);
|
|
}
|
|
}
|
|
|
|
async listOpenForMe(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
|
|
const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
|
|
const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
|
|
const result = await workflowService.listOpenForMe(userId, page, limit);
|
|
ResponseHandler.success(res, result, 'Open requests for user fetched');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to fetch open requests for user', 500, errorMessage);
|
|
}
|
|
}
|
|
|
|
async listClosedByMe(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
|
|
const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
|
|
const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
|
|
const result = await workflowService.listClosedByMe(userId, page, limit);
|
|
ResponseHandler.success(res, result, 'Closed requests by user fetched');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to fetch closed requests by user', 500, errorMessage);
|
|
}
|
|
}
|
|
|
|
async updateWorkflow(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const validatedData = validateUpdateWorkflow(req.body);
|
|
// Build a strongly-typed payload for the service layer
|
|
const updateData: UpdateWorkflowRequest = { ...validatedData } as any;
|
|
if (validatedData.priority) {
|
|
// Map string literal to enum value explicitly
|
|
updateData.priority = validatedData.priority === 'EXPRESS' ? Priority.EXPRESS : Priority.STANDARD;
|
|
}
|
|
|
|
const workflow = await workflowService.updateWorkflow(id, updateData);
|
|
|
|
if (!workflow) {
|
|
ResponseHandler.notFound(res, 'Workflow not found');
|
|
return;
|
|
}
|
|
|
|
ResponseHandler.success(res, workflow, 'Workflow updated successfully');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to update workflow', 400, errorMessage);
|
|
}
|
|
}
|
|
|
|
// Multipart update for drafts: accepts payload JSON and files[]
|
|
async updateWorkflowMultipart(req: AuthenticatedRequest, res: Response): Promise<void> {
|
|
try {
|
|
const userId = req.user?.userId;
|
|
if (!userId) {
|
|
ResponseHandler.error(res, 'Unauthorized', 401);
|
|
return;
|
|
}
|
|
|
|
const { id } = req.params;
|
|
const raw = String(req.body?.payload || '');
|
|
if (!raw) {
|
|
ResponseHandler.error(res, 'payload is required', 400);
|
|
return;
|
|
}
|
|
const parsed = JSON.parse(raw);
|
|
const validated = validateUpdateWorkflow(parsed);
|
|
const updateData: UpdateWorkflowRequest = { ...validated } as any;
|
|
if (validated.priority) {
|
|
updateData.priority = validated.priority === 'EXPRESS' ? Priority.EXPRESS : Priority.STANDARD;
|
|
}
|
|
|
|
// Update workflow
|
|
const workflow = await workflowService.updateWorkflow(id, updateData);
|
|
if (!workflow) {
|
|
ResponseHandler.notFound(res, 'Workflow not found');
|
|
return;
|
|
}
|
|
|
|
// Attach new files as documents
|
|
const files = (req as any).files as Express.Multer.File[] | undefined;
|
|
const category = (req.body?.category as string) || 'SUPPORTING';
|
|
const docs: any[] = [];
|
|
if (files && files.length > 0) {
|
|
const actualRequestId = (workflow as any).requestId;
|
|
for (const file of files) {
|
|
const buffer = fs.readFileSync(file.path);
|
|
const checksum = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
const extension = path.extname(file.originalname).replace('.', '').toLowerCase();
|
|
const doc = await Document.create({
|
|
requestId: actualRequestId,
|
|
uploadedBy: userId,
|
|
fileName: path.basename(file.filename || file.originalname),
|
|
originalFileName: file.originalname,
|
|
fileType: extension,
|
|
fileExtension: extension,
|
|
fileSize: file.size,
|
|
filePath: file.path,
|
|
storageUrl: `/uploads/${path.basename(file.path)}`,
|
|
mimeType: file.mimetype,
|
|
checksum,
|
|
isGoogleDoc: false,
|
|
googleDocUrl: null as any,
|
|
category: category || 'OTHER',
|
|
version: 1,
|
|
parentDocumentId: null as any,
|
|
isDeleted: false,
|
|
downloadCount: 0,
|
|
} as any);
|
|
docs.push(doc);
|
|
}
|
|
}
|
|
|
|
ResponseHandler.success(res, { workflow, newDocuments: docs }, 'Workflow updated with documents', 200);
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to update workflow', 400, errorMessage);
|
|
}
|
|
}
|
|
|
|
async submitWorkflow(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const workflow = await workflowService.submitWorkflow(id);
|
|
|
|
if (!workflow) {
|
|
ResponseHandler.notFound(res, 'Workflow not found');
|
|
return;
|
|
}
|
|
|
|
ResponseHandler.success(res, workflow, 'Workflow submitted successfully');
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
ResponseHandler.error(res, 'Failed to submit workflow', 400, errorMessage);
|
|
}
|
|
}
|
|
}
|