UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

175 lines (174 loc) • 6.95 kB
"use strict"; /** * @fileoverview Results Routes - Clean Architecture HTTP routes for quiz results and statistics * @version 1.0.0 * @since 2025-07-31 * @lastUpdated 2025-07-31 * @module ResultsRoutes * @description HTTP routes for quiz results, statistics, and analytics following clean architecture principles. * Provides endpoints for retrieving user performance data and system analytics. * @contributors Claude Code Agent * @dependencies express, error-handler, audit-logger * @requirements REQ_API_001, REQ_PERF_001, REQ_COMPLIANCE_004 * @testCoverage Results route integration tests */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.createResultsRoutes = createResultsRoutes; const express = __importStar(require("express")); const error_handler_1 = require("../../../shared/error-handler"); /** * Creates results and statistics HTTP routes following clean architecture patterns. * * @description Sets up endpoints for quiz results, user statistics, and system * analytics with proper audit logging and error handling. * * @param config - Configuration object with dependencies * @returns Express router with results routes * * @example * ```typescript * const resultsRouter = createResultsRoutes({ * auditLogger, * legacyService: quizService * }); * app.use('/results', resultsRouter); * ``` * * @since 2025-07-31 * @author Claude Code Agent * @requirements REQ_API_001 (Quiz CRUD Operations), REQ_PERF_001 (Performance Standards) * @accessibility Standard HTTP REST API for results data */ function createResultsRoutes(config) { const router = express.Router(); const { auditLogger, legacyService } = config; /** * GET /results/recent - Get recent quiz results * Retrieves the most recent quiz submissions across all quizzes */ router.get('/recent', (0, error_handler_1.asyncHandler)(async (req, res, next) => { try { if (!legacyService) { return res.status(503).json({ error: 'Results service not available' }); } const limit = parseInt(req.query.limit) || 10; const results = await legacyService.getRecentResults(limit); // Audit log results access auditLogger.logUserAction('RESULTS_ACCESS', { resource: 'quiz_results', resourceId: 'recent_results', ipAddress: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent'), success: true, metadata: { limit, resultCount: results.length, accessType: 'recent', }, }); res.json({ success: true, results, metadata: { limit, count: results.length, timestamp: new Date().toISOString(), }, }); } catch (error) { error_handler_1.Logger.error('Error fetching recent results:', error instanceof Error ? error : new Error(String(error))); res.status(500).json({ success: false, error: 'Failed to fetch recent results', details: error instanceof Error ? error.message : String(error), }); } })); /** * POST /results/submit/:resultId - Submit quiz result (legacy endpoint) * Handles quiz result submission for specific result IDs */ router.post('/submit/:resultId', (0, error_handler_1.asyncHandler)(async (req, res, next) => { try { if (!legacyService) { return res.status(503).json({ error: 'Results service not available' }); } const { answers } = req.body; if (!answers || !Array.isArray(answers)) { return res.status(400).json({ success: false, error: 'Answers array is required', }); } const result = await legacyService.submitQuizAttempt(req.params.resultId, answers); error_handler_1.Logger.info('Quiz result submitted successfully', { resultId: req.params.resultId, score: result.score, }); // Audit log quiz submission auditLogger.logUserAction('QUIZ_SUBMIT', { resource: 'quiz_result', resourceId: req.params.resultId, ipAddress: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent'), success: true, metadata: { answerCount: answers.length, score: result.score, percentage: result.percentage, submissionType: 'legacy_endpoint', }, }); res.json({ success: true, result: { ...result, timestamp: new Date().toISOString(), }, }); } catch (error) { error_handler_1.Logger.error('Error submitting quiz result:', error instanceof Error ? error : new Error(String(error))); res.status(500).json({ success: false, error: 'Failed to submit quiz result', details: error instanceof Error ? error.message : String(error), }); } })); return router; }