- Add proxy module with types, service, controller, and routes - Configure llmAgent and dataService in config - Register proxy routes in main Express app - All Python service access now goes through authenticated Express gateway ARCH-001: Centralized proxy with auth, logging, and error handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
267 lines
5.5 KiB
TypeScript
267 lines
5.5 KiB
TypeScript
/**
|
|
* Risk Assessment Controller
|
|
* Handles HTTP endpoints for risk questionnaire and assessments
|
|
*/
|
|
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { riskService } from '../services/risk.service';
|
|
|
|
// ============================================================================
|
|
// Types
|
|
// ============================================================================
|
|
|
|
type AuthRequest = Request;
|
|
|
|
// ============================================================================
|
|
// Controllers
|
|
// ============================================================================
|
|
|
|
/**
|
|
* GET /api/v1/risk/questions
|
|
* Get all risk questionnaire questions
|
|
*/
|
|
export async function getQuestions(
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
const questions = riskService.getQuestions();
|
|
|
|
res.json({
|
|
success: true,
|
|
data: questions,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/risk/assessment
|
|
* Get current user's risk assessment
|
|
*/
|
|
export async function getCurrentUserAssessment(
|
|
req: AuthRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const assessment = await riskService.getUserAssessment(req.user.id);
|
|
|
|
if (!assessment) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: {
|
|
message: 'No risk assessment found',
|
|
code: 'NOT_FOUND',
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: assessment,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/risk/assessment/valid
|
|
* Check if current user has a valid (non-expired) assessment
|
|
*/
|
|
export async function checkValidAssessment(
|
|
req: AuthRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const isValid = await riskService.isAssessmentValid(req.user.id);
|
|
const assessment = isValid
|
|
? await riskService.getValidAssessment(req.user.id)
|
|
: null;
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
isValid,
|
|
assessment,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/risk/assessment
|
|
* Submit risk questionnaire responses
|
|
* Body: { responses: [{ questionId, answer }] }
|
|
*/
|
|
export async function submitAssessment(
|
|
req: AuthRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { responses, completionTimeSeconds } = req.body;
|
|
|
|
if (!responses || !Array.isArray(responses)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: {
|
|
message: 'Invalid request: responses array is required',
|
|
code: 'INVALID_REQUEST',
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const assessment = await riskService.submitAssessment({
|
|
userId: req.user.id,
|
|
responses,
|
|
ipAddress: req.ip,
|
|
userAgent: req.get('user-agent'),
|
|
completionTimeSeconds,
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: assessment,
|
|
message: `Risk assessment completed. Your profile: ${assessment.riskProfile}`,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/risk/assessment/:userId
|
|
* Get risk assessment for specific user (admin only)
|
|
*/
|
|
export async function getUserAssessment(
|
|
req: AuthRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { userId } = req.params;
|
|
|
|
if (!userId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: {
|
|
message: 'User ID is required',
|
|
code: 'INVALID_REQUEST',
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const assessment = await riskService.getUserAssessment(userId);
|
|
|
|
if (!assessment) {
|
|
res.status(404).json({
|
|
success: false,
|
|
error: {
|
|
message: 'No risk assessment found for this user',
|
|
code: 'NOT_FOUND',
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: assessment,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/risk/assessment/history
|
|
* Get assessment history for current user
|
|
*/
|
|
export async function getAssessmentHistory(
|
|
req: AuthRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const history = await riskService.getAssessmentHistory(req.user.id);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: history,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/risk/statistics
|
|
* Get risk profile statistics (admin only)
|
|
*/
|
|
export async function getStatistics(
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
const statistics = await riskService.getProfileStatistics();
|
|
|
|
res.json({
|
|
success: true,
|
|
data: statistics,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|