Re_Backend/src/controllers/workflow.controller.ts

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);
}
}
}