@octo-cyber/quiz-engine 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/controllers/attempt.controller.d.ts +3 -0
  2. package/dist/controllers/attempt.controller.d.ts.map +1 -0
  3. package/dist/controllers/attempt.controller.js +83 -0
  4. package/dist/controllers/attempt.controller.js.map +1 -0
  5. package/dist/controllers/category.controller.d.ts +3 -0
  6. package/dist/controllers/category.controller.d.ts.map +1 -0
  7. package/dist/controllers/category.controller.js +38 -0
  8. package/dist/controllers/category.controller.js.map +1 -0
  9. package/dist/controllers/exam.controller.d.ts +3 -0
  10. package/dist/controllers/exam.controller.d.ts.map +1 -0
  11. package/dist/controllers/exam.controller.js +48 -0
  12. package/dist/controllers/exam.controller.js.map +1 -0
  13. package/dist/controllers/index.d.ts +7 -0
  14. package/dist/controllers/index.d.ts.map +1 -0
  15. package/dist/controllers/index.js +16 -0
  16. package/dist/controllers/index.js.map +1 -0
  17. package/dist/controllers/paper.controller.d.ts +3 -0
  18. package/dist/controllers/paper.controller.d.ts.map +1 -0
  19. package/dist/controllers/paper.controller.js +70 -0
  20. package/dist/controllers/paper.controller.js.map +1 -0
  21. package/dist/controllers/question.controller.d.ts +3 -0
  22. package/dist/controllers/question.controller.d.ts.map +1 -0
  23. package/dist/controllers/question.controller.js +59 -0
  24. package/dist/controllers/question.controller.js.map +1 -0
  25. package/dist/controllers/statistics.controller.d.ts +3 -0
  26. package/dist/controllers/statistics.controller.d.ts.map +1 -0
  27. package/dist/controllers/statistics.controller.js +37 -0
  28. package/dist/controllers/statistics.controller.js.map +1 -0
  29. package/dist/entities/index.d.ts +24 -0
  30. package/dist/entities/index.d.ts.map +1 -0
  31. package/dist/entities/index.js +43 -0
  32. package/dist/entities/index.js.map +1 -0
  33. package/dist/entities/quiz-answer.entity.d.ts +19 -0
  34. package/dist/entities/quiz-answer.entity.d.ts.map +1 -0
  35. package/dist/entities/quiz-answer.entity.js +81 -0
  36. package/dist/entities/quiz-answer.entity.js.map +1 -0
  37. package/dist/entities/quiz-attempt.entity.d.ts +17 -0
  38. package/dist/entities/quiz-attempt.entity.d.ts.map +1 -0
  39. package/dist/entities/quiz-attempt.entity.js +80 -0
  40. package/dist/entities/quiz-attempt.entity.js.map +1 -0
  41. package/dist/entities/quiz-category.entity.d.ts +10 -0
  42. package/dist/entities/quiz-category.entity.d.ts.map +1 -0
  43. package/dist/entities/quiz-category.entity.js +55 -0
  44. package/dist/entities/quiz-category.entity.js.map +1 -0
  45. package/dist/entities/quiz-exam.entity.d.ts +25 -0
  46. package/dist/entities/quiz-exam.entity.d.ts.map +1 -0
  47. package/dist/entities/quiz-exam.entity.js +99 -0
  48. package/dist/entities/quiz-exam.entity.js.map +1 -0
  49. package/dist/entities/quiz-paper-question.entity.d.ts +12 -0
  50. package/dist/entities/quiz-paper-question.entity.d.ts.map +1 -0
  51. package/dist/entities/quiz-paper-question.entity.js +58 -0
  52. package/dist/entities/quiz-paper-question.entity.js.map +1 -0
  53. package/dist/entities/quiz-paper.entity.d.ts +18 -0
  54. package/dist/entities/quiz-paper.entity.d.ts.map +1 -0
  55. package/dist/entities/quiz-paper.entity.js +75 -0
  56. package/dist/entities/quiz-paper.entity.js.map +1 -0
  57. package/dist/entities/quiz-question.entity.d.ts +28 -0
  58. package/dist/entities/quiz-question.entity.d.ts.map +1 -0
  59. package/dist/entities/quiz-question.entity.js +107 -0
  60. package/dist/entities/quiz-question.entity.js.map +1 -0
  61. package/dist/index.d.ts +27 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +66 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/quiz-engine.module.d.ts +8 -0
  66. package/dist/quiz-engine.module.d.ts.map +1 -0
  67. package/dist/quiz-engine.module.js +45 -0
  68. package/dist/quiz-engine.module.js.map +1 -0
  69. package/dist/schemas/attempt.schema.d.ts +47 -0
  70. package/dist/schemas/attempt.schema.d.ts.map +1 -0
  71. package/dist/schemas/attempt.schema.js +19 -0
  72. package/dist/schemas/attempt.schema.js.map +1 -0
  73. package/dist/schemas/category.schema.d.ts +36 -0
  74. package/dist/schemas/category.schema.d.ts.map +1 -0
  75. package/dist/schemas/category.schema.js +12 -0
  76. package/dist/schemas/category.schema.js.map +1 -0
  77. package/dist/schemas/exam.schema.d.ts +70 -0
  78. package/dist/schemas/exam.schema.d.ts.map +1 -0
  79. package/dist/schemas/exam.schema.js +20 -0
  80. package/dist/schemas/exam.schema.js.map +1 -0
  81. package/dist/schemas/paper.schema.d.ts +71 -0
  82. package/dist/schemas/paper.schema.d.ts.map +1 -0
  83. package/dist/schemas/paper.schema.js +26 -0
  84. package/dist/schemas/paper.schema.js.map +1 -0
  85. package/dist/schemas/question.schema.d.ts +147 -0
  86. package/dist/schemas/question.schema.d.ts.map +1 -0
  87. package/dist/schemas/question.schema.js +32 -0
  88. package/dist/schemas/question.schema.js.map +1 -0
  89. package/dist/services/attempt.service.d.ts +33 -0
  90. package/dist/services/attempt.service.d.ts.map +1 -0
  91. package/dist/services/attempt.service.js +197 -0
  92. package/dist/services/attempt.service.js.map +1 -0
  93. package/dist/services/category.service.d.ts +14 -0
  94. package/dist/services/category.service.d.ts.map +1 -0
  95. package/dist/services/category.service.js +74 -0
  96. package/dist/services/category.service.js.map +1 -0
  97. package/dist/services/exam.service.d.ts +17 -0
  98. package/dist/services/exam.service.d.ts.map +1 -0
  99. package/dist/services/exam.service.js +92 -0
  100. package/dist/services/exam.service.js.map +1 -0
  101. package/dist/services/grade.service.d.ts +16 -0
  102. package/dist/services/grade.service.d.ts.map +1 -0
  103. package/dist/services/grade.service.js +75 -0
  104. package/dist/services/grade.service.js.map +1 -0
  105. package/dist/services/index.d.ts +8 -0
  106. package/dist/services/index.d.ts.map +1 -0
  107. package/dist/services/index.js +18 -0
  108. package/dist/services/index.js.map +1 -0
  109. package/dist/services/paper.service.d.ts +32 -0
  110. package/dist/services/paper.service.d.ts.map +1 -0
  111. package/dist/services/paper.service.js +157 -0
  112. package/dist/services/paper.service.js.map +1 -0
  113. package/dist/services/question.service.d.ts +30 -0
  114. package/dist/services/question.service.d.ts.map +1 -0
  115. package/dist/services/question.service.js +155 -0
  116. package/dist/services/question.service.js.map +1 -0
  117. package/dist/services/statistics.service.d.ts +43 -0
  118. package/dist/services/statistics.service.d.ts.map +1 -0
  119. package/dist/services/statistics.service.js +134 -0
  120. package/dist/services/statistics.service.js.map +1 -0
  121. package/package.json +85 -0
  122. package/web/index.ts +51 -0
  123. package/web/manifest.ts +36 -0
  124. package/web/messages/en-US.json +143 -0
  125. package/web/messages/zh-CN.json +143 -0
  126. package/web/pages/ExamRoomPage.tsx +289 -0
  127. package/web/pages/ExamsPage.tsx +248 -0
  128. package/web/pages/PapersPage.tsx +202 -0
  129. package/web/pages/QuestionBankPage.tsx +263 -0
  130. package/web/pages/StatisticsPage.tsx +178 -0
  131. package/web/services/attempt-service.ts +53 -0
  132. package/web/services/category-service.ts +26 -0
  133. package/web/services/exam-service.ts +31 -0
  134. package/web/services/paper-service.ts +50 -0
  135. package/web/services/question-service.ts +36 -0
  136. package/web/services/statistics-service.ts +28 -0
  137. package/web/stores/quiz-store.ts +31 -0
  138. package/web/types/quiz.ts +166 -0
@@ -0,0 +1,3 @@
1
+ import type { Express } from '@octo-cyber/core';
2
+ export declare function registerAttemptRoutes(app: Express): void;
3
+ //# sourceMappingURL=attempt.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/attempt.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAanE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAoExD"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAttemptRoutes = registerAttemptRoutes;
4
+ const core_1 = require("@octo-cyber/core");
5
+ const zod_1 = require("zod");
6
+ const auth_1 = require("@octo-cyber/auth");
7
+ const attempt_service_js_1 = require("../services/attempt.service.js");
8
+ const grade_service_js_1 = require("../services/grade.service.js");
9
+ const attempt_schema_js_1 = require("../schemas/attempt.schema.js");
10
+ const PageQuerySchema = zod_1.z.object({
11
+ page: zod_1.z.coerce.number().int().min(1).default(1),
12
+ pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(20),
13
+ });
14
+ function registerAttemptRoutes(app) {
15
+ const auth = (0, auth_1.authMiddleware)();
16
+ const attemptService = core_1.Container.get(attempt_service_js_1.AttemptService);
17
+ const gradeService = core_1.Container.get(grade_service_js_1.GradeService);
18
+ /** 开始答题 */
19
+ app.post('/api/v1/quiz/attempts/start', auth, (0, core_1.asyncHandler)(async (req, res) => {
20
+ const parsed = attempt_schema_js_1.StartAttemptSchema.safeParse(req.body);
21
+ if (!parsed.success)
22
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
23
+ const userId = req.user?.userId;
24
+ if (!userId)
25
+ throw core_1.AppError.unauthorized('Not authenticated');
26
+ const attempt = await attemptService.startAttempt(parsed.data.examId, userId);
27
+ res.status(201).json(core_1.ApiResponse.ok(attempt, 'Attempt started'));
28
+ }));
29
+ /** 提交答卷 */
30
+ app.post('/api/v1/quiz/attempts/:id/submit', auth, (0, core_1.asyncHandler)(async (req, res) => {
31
+ const parsed = attempt_schema_js_1.SubmitAnswersSchema.safeParse(req.body);
32
+ if (!parsed.success)
33
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
34
+ const userId = req.user?.userId;
35
+ if (!userId)
36
+ throw core_1.AppError.unauthorized('Not authenticated');
37
+ const attempt = await attemptService.submitAttempt(req.params.id, userId, parsed.data);
38
+ res.json(core_1.ApiResponse.ok(attempt, 'Attempt submitted'));
39
+ }));
40
+ /** 查看答卷详情 */
41
+ app.get('/api/v1/quiz/attempts/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
42
+ const detail = await attemptService.findAttemptDetail(req.params.id);
43
+ res.json(core_1.ApiResponse.ok(detail));
44
+ }));
45
+ /** 查询某考试的所有答卷(管理员) */
46
+ app.get('/api/v1/quiz/exams/:examId/attempts', auth, (0, core_1.asyncHandler)(async (req, res) => {
47
+ const parsed = PageQuerySchema.safeParse(req.query);
48
+ if (!parsed.success)
49
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid query');
50
+ const { page, pageSize } = parsed.data;
51
+ const result = await attemptService.findAttemptsByExam(req.params.examId, page, pageSize);
52
+ res.json(core_1.ApiResponse.paginated(result.items, result.total, page, pageSize));
53
+ }));
54
+ /** 查询当前用户的答卷记录 */
55
+ app.get('/api/v1/quiz/my/attempts', auth, (0, core_1.asyncHandler)(async (req, res) => {
56
+ const parsed = PageQuerySchema.safeParse(req.query);
57
+ if (!parsed.success)
58
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid query');
59
+ const userId = req.user?.userId;
60
+ if (!userId)
61
+ throw core_1.AppError.unauthorized('Not authenticated');
62
+ const { page, pageSize } = parsed.data;
63
+ const result = await attemptService.findAttemptsByUser(userId, page, pageSize);
64
+ res.json(core_1.ApiResponse.paginated(result.items, result.total, page, pageSize));
65
+ }));
66
+ /** 批改主观题答案 */
67
+ app.put('/api/v1/quiz/answers/:answerId/grade', auth, (0, core_1.asyncHandler)(async (req, res) => {
68
+ const parsed = attempt_schema_js_1.GradeAnswerSchema.safeParse(req.body);
69
+ if (!parsed.success)
70
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
71
+ const graderId = req.user?.userId;
72
+ if (!graderId)
73
+ throw core_1.AppError.unauthorized('Not authenticated');
74
+ const answer = await gradeService.gradeAnswer(req.params.answerId, graderId, parsed.data);
75
+ res.json(core_1.ApiResponse.ok(answer, 'Answer graded'));
76
+ }));
77
+ /** 完成阅卷 */
78
+ app.post('/api/v1/quiz/attempts/:id/finalize', auth, (0, core_1.asyncHandler)(async (req, res) => {
79
+ const attempt = await gradeService.finalizeGrading(req.params.id);
80
+ res.json(core_1.ApiResponse.ok(attempt, 'Grading finalized'));
81
+ }));
82
+ }
83
+ //# sourceMappingURL=attempt.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt.controller.js","sourceRoot":"","sources":["../../src/controllers/attempt.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Express } from '@octo-cyber/core';
2
+ export declare function registerCategoryRoutes(app: Express): void;
3
+ //# sourceMappingURL=category.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/category.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAMnE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAgCzD"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerCategoryRoutes = registerCategoryRoutes;
4
+ const core_1 = require("@octo-cyber/core");
5
+ const auth_1 = require("@octo-cyber/auth");
6
+ const category_service_js_1 = require("../services/category.service.js");
7
+ const category_schema_js_1 = require("../schemas/category.schema.js");
8
+ function registerCategoryRoutes(app) {
9
+ const auth = (0, auth_1.authMiddleware)();
10
+ const service = core_1.Container.get(category_service_js_1.CategoryService);
11
+ app.get('/api/v1/quiz/categories', auth, (0, core_1.asyncHandler)(async (_req, res) => {
12
+ const items = await service.findAll();
13
+ res.json(core_1.ApiResponse.ok(items));
14
+ }));
15
+ app.post('/api/v1/quiz/categories', auth, (0, core_1.asyncHandler)(async (req, res) => {
16
+ const parsed = category_schema_js_1.CreateCategorySchema.safeParse(req.body);
17
+ if (!parsed.success)
18
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
19
+ const item = await service.create(parsed.data);
20
+ res.status(201).json(core_1.ApiResponse.ok(item, 'Category created'));
21
+ }));
22
+ app.get('/api/v1/quiz/categories/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
23
+ const item = await service.findById(req.params.id);
24
+ res.json(core_1.ApiResponse.ok(item));
25
+ }));
26
+ app.put('/api/v1/quiz/categories/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
27
+ const parsed = category_schema_js_1.UpdateCategorySchema.safeParse(req.body);
28
+ if (!parsed.success)
29
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
30
+ const item = await service.update(req.params.id, parsed.data);
31
+ res.json(core_1.ApiResponse.ok(item, 'Category updated'));
32
+ }));
33
+ app.delete('/api/v1/quiz/categories/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
34
+ await service.delete(req.params.id);
35
+ res.json(core_1.ApiResponse.ok(null, 'Category deleted'));
36
+ }));
37
+ }
38
+ //# sourceMappingURL=category.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category.controller.js","sourceRoot":"","sources":["../../src/controllers/category.controller.ts"],"names":[],"mappings":";;AAMA,wDAgCC;AArCD,2CAAkF;AAClF,2CAAkD;AAClD,yEAAkE;AAClE,sEAA2F;AAE3F,SAAgB,sBAAsB,CAAC,GAAY;IACjD,MAAM,IAAI,GAAG,IAAA,qBAAc,GAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,gBAAS,CAAC,GAAG,CAAC,qCAAe,CAAC,CAAC;IAE/C,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,EAAE;QAC3F,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC3F,MAAM,MAAM,GAAG,yCAAoB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC;QACnG,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC9F,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAC7D,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC9F,MAAM,MAAM,GAAG,yCAAoB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC;QACnG,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACxE,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACjG,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAC9C,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Express } from '@octo-cyber/core';
2
+ export declare function registerExamRoutes(app: Express): void;
3
+ //# sourceMappingURL=exam.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exam.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/exam.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAYnE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAoCrD"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerExamRoutes = registerExamRoutes;
4
+ const core_1 = require("@octo-cyber/core");
5
+ const zod_1 = require("zod");
6
+ const auth_1 = require("@octo-cyber/auth");
7
+ const exam_service_js_1 = require("../services/exam.service.js");
8
+ const exam_schema_js_1 = require("../schemas/exam.schema.js");
9
+ const PageQuerySchema = zod_1.z.object({
10
+ page: zod_1.z.coerce.number().int().min(1).default(1),
11
+ pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(20),
12
+ });
13
+ function registerExamRoutes(app) {
14
+ const auth = (0, auth_1.authMiddleware)();
15
+ const service = core_1.Container.get(exam_service_js_1.ExamService);
16
+ app.get('/api/v1/quiz/exams', auth, (0, core_1.asyncHandler)(async (req, res) => {
17
+ const parsed = PageQuerySchema.safeParse(req.query);
18
+ if (!parsed.success)
19
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid query');
20
+ const { page, pageSize } = parsed.data;
21
+ const result = await service.findAll(page, pageSize);
22
+ res.json(core_1.ApiResponse.paginated(result.items, result.total, page, pageSize));
23
+ }));
24
+ app.post('/api/v1/quiz/exams', auth, (0, core_1.asyncHandler)(async (req, res) => {
25
+ const parsed = exam_schema_js_1.CreateExamSchema.safeParse(req.body);
26
+ if (!parsed.success)
27
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
28
+ const userId = req.user?.userId;
29
+ const item = await service.create(parsed.data, userId);
30
+ res.status(201).json(core_1.ApiResponse.ok(item, 'Exam created'));
31
+ }));
32
+ app.get('/api/v1/quiz/exams/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
33
+ const item = await service.findById(req.params.id);
34
+ res.json(core_1.ApiResponse.ok(item));
35
+ }));
36
+ app.put('/api/v1/quiz/exams/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
37
+ const parsed = exam_schema_js_1.UpdateExamSchema.safeParse(req.body);
38
+ if (!parsed.success)
39
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
40
+ const item = await service.update(req.params.id, parsed.data);
41
+ res.json(core_1.ApiResponse.ok(item, 'Exam updated'));
42
+ }));
43
+ app.delete('/api/v1/quiz/exams/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
44
+ await service.delete(req.params.id);
45
+ res.json(core_1.ApiResponse.ok(null, 'Exam deleted'));
46
+ }));
47
+ }
48
+ //# sourceMappingURL=exam.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exam.controller.js","sourceRoot":"","sources":["../../src/controllers/exam.controller.ts"],"names":[],"mappings":";;AAYA,gDAoCC;AA/CD,2CAAkF;AAClF,6BAAwB;AACxB,2CAAkD;AAClD,iEAA0D;AAC1D,8DAA+E;AAE/E,MAAM,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH,SAAgB,kBAAkB,CAAC,GAAY;IAC7C,MAAM,IAAI,GAAG,IAAA,qBAAc,GAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,gBAAS,CAAC,GAAG,CAAC,6BAAW,CAAC,CAAC;IAE3C,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACrF,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC;QACnG,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;QACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACtF,MAAM,MAAM,GAAG,iCAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC;QACnG,MAAM,MAAM,GAAI,GAA+C,CAAC,IAAI,EAAE,MAAM,CAAC;QAC7E,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACzF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAC7D,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACzF,MAAM,MAAM,GAAG,iCAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC;QACnG,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACxE,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,MAAM,CAAC,wBAAwB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC5F,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAC9C,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { registerCategoryRoutes } from './category.controller.js';
2
+ export { registerQuestionRoutes } from './question.controller.js';
3
+ export { registerPaperRoutes } from './paper.controller.js';
4
+ export { registerExamRoutes } from './exam.controller.js';
5
+ export { registerAttemptRoutes } from './attempt.controller.js';
6
+ export { registerStatisticsRoutes } from './statistics.controller.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/controllers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerStatisticsRoutes = exports.registerAttemptRoutes = exports.registerExamRoutes = exports.registerPaperRoutes = exports.registerQuestionRoutes = exports.registerCategoryRoutes = void 0;
4
+ var category_controller_js_1 = require("./category.controller.js");
5
+ Object.defineProperty(exports, "registerCategoryRoutes", { enumerable: true, get: function () { return category_controller_js_1.registerCategoryRoutes; } });
6
+ var question_controller_js_1 = require("./question.controller.js");
7
+ Object.defineProperty(exports, "registerQuestionRoutes", { enumerable: true, get: function () { return question_controller_js_1.registerQuestionRoutes; } });
8
+ var paper_controller_js_1 = require("./paper.controller.js");
9
+ Object.defineProperty(exports, "registerPaperRoutes", { enumerable: true, get: function () { return paper_controller_js_1.registerPaperRoutes; } });
10
+ var exam_controller_js_1 = require("./exam.controller.js");
11
+ Object.defineProperty(exports, "registerExamRoutes", { enumerable: true, get: function () { return exam_controller_js_1.registerExamRoutes; } });
12
+ var attempt_controller_js_1 = require("./attempt.controller.js");
13
+ Object.defineProperty(exports, "registerAttemptRoutes", { enumerable: true, get: function () { return attempt_controller_js_1.registerAttemptRoutes; } });
14
+ var statistics_controller_js_1 = require("./statistics.controller.js");
15
+ Object.defineProperty(exports, "registerStatisticsRoutes", { enumerable: true, get: function () { return statistics_controller_js_1.registerStatisticsRoutes; } });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/controllers/index.ts"],"names":[],"mappings":";;;AAAA,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAC5B,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA"}
@@ -0,0 +1,3 @@
1
+ import type { Express } from '@octo-cyber/core';
2
+ export declare function registerPaperRoutes(app: Express): void;
3
+ //# sourceMappingURL=paper.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paper.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/paper.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAiBnE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA4DtD"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPaperRoutes = registerPaperRoutes;
4
+ const core_1 = require("@octo-cyber/core");
5
+ const zod_1 = require("zod");
6
+ const auth_1 = require("@octo-cyber/auth");
7
+ const paper_service_js_1 = require("../services/paper.service.js");
8
+ const paper_schema_js_1 = require("../schemas/paper.schema.js");
9
+ const PageQuerySchema = zod_1.z.object({
10
+ page: zod_1.z.coerce.number().int().min(1).default(1),
11
+ pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(20),
12
+ });
13
+ function registerPaperRoutes(app) {
14
+ const auth = (0, auth_1.authMiddleware)();
15
+ const service = core_1.Container.get(paper_service_js_1.PaperService);
16
+ app.get('/api/v1/quiz/papers', auth, (0, core_1.asyncHandler)(async (req, res) => {
17
+ const parsed = PageQuerySchema.safeParse(req.query);
18
+ if (!parsed.success)
19
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid query');
20
+ const { page, pageSize } = parsed.data;
21
+ const result = await service.findAll(page, pageSize);
22
+ res.json(core_1.ApiResponse.paginated(result.items, result.total, page, pageSize));
23
+ }));
24
+ app.post('/api/v1/quiz/papers', auth, (0, core_1.asyncHandler)(async (req, res) => {
25
+ const parsed = paper_schema_js_1.CreatePaperSchema.safeParse(req.body);
26
+ if (!parsed.success)
27
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
28
+ const userId = req.user?.userId;
29
+ const item = await service.create(parsed.data, userId);
30
+ res.status(201).json(core_1.ApiResponse.ok(item, 'Paper created'));
31
+ }));
32
+ app.get('/api/v1/quiz/papers/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
33
+ const item = await service.findById(req.params.id);
34
+ res.json(core_1.ApiResponse.ok(item));
35
+ }));
36
+ app.get('/api/v1/quiz/papers/:id/questions', auth, (0, core_1.asyncHandler)(async (req, res) => {
37
+ const detail = await service.findWithQuestions(req.params.id);
38
+ res.json(core_1.ApiResponse.ok(detail));
39
+ }));
40
+ app.put('/api/v1/quiz/papers/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
41
+ const parsed = paper_schema_js_1.UpdatePaperSchema.safeParse(req.body);
42
+ if (!parsed.success)
43
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
44
+ const item = await service.update(req.params.id, parsed.data);
45
+ res.json(core_1.ApiResponse.ok(item, 'Paper updated'));
46
+ }));
47
+ app.delete('/api/v1/quiz/papers/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
48
+ await service.delete(req.params.id);
49
+ res.json(core_1.ApiResponse.ok(null, 'Paper deleted'));
50
+ }));
51
+ app.post('/api/v1/quiz/papers/:id/questions', auth, (0, core_1.asyncHandler)(async (req, res) => {
52
+ const parsed = paper_schema_js_1.AddQuestionSchema.safeParse(req.body);
53
+ if (!parsed.success)
54
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
55
+ const pq = await service.addQuestion(req.params.id, parsed.data);
56
+ res.status(201).json(core_1.ApiResponse.ok(pq, 'Question added to paper'));
57
+ }));
58
+ app.put('/api/v1/quiz/papers/:id/questions/:qid', auth, (0, core_1.asyncHandler)(async (req, res) => {
59
+ const parsed = paper_schema_js_1.UpdatePaperQuestionSchema.safeParse(req.body);
60
+ if (!parsed.success)
61
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
62
+ const pq = await service.updateQuestion(req.params.id, req.params.qid, parsed.data);
63
+ res.json(core_1.ApiResponse.ok(pq, 'Paper question updated'));
64
+ }));
65
+ app.delete('/api/v1/quiz/papers/:id/questions/:qid', auth, (0, core_1.asyncHandler)(async (req, res) => {
66
+ await service.removeQuestion(req.params.id, req.params.qid);
67
+ res.json(core_1.ApiResponse.ok(null, 'Question removed from paper'));
68
+ }));
69
+ }
70
+ //# sourceMappingURL=paper.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paper.controller.js","sourceRoot":"","sources":["../../src/controllers/paper.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Express } from '@octo-cyber/core';
2
+ export declare function registerQuestionRoutes(app: Express): void;
3
+ //# sourceMappingURL=question.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/question.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAgBnE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA2CzD"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerQuestionRoutes = registerQuestionRoutes;
4
+ const core_1 = require("@octo-cyber/core");
5
+ const zod_1 = require("zod");
6
+ const auth_1 = require("@octo-cyber/auth");
7
+ const question_service_js_1 = require("../services/question.service.js");
8
+ const question_schema_js_1 = require("../schemas/question.schema.js");
9
+ const QuestionQuerySchema = zod_1.z.object({
10
+ page: zod_1.z.coerce.number().int().min(1).default(1),
11
+ pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(20),
12
+ categoryId: zod_1.z.string().optional(),
13
+ type: zod_1.z.string().optional(),
14
+ difficulty: zod_1.z.coerce.number().int().min(1).max(5).optional(),
15
+ search: zod_1.z.string().optional(),
16
+ });
17
+ function registerQuestionRoutes(app) {
18
+ const auth = (0, auth_1.authMiddleware)();
19
+ const service = core_1.Container.get(question_service_js_1.QuestionService);
20
+ app.get('/api/v1/quiz/questions', auth, (0, core_1.asyncHandler)(async (req, res) => {
21
+ const parsed = QuestionQuerySchema.safeParse(req.query);
22
+ if (!parsed.success)
23
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid query');
24
+ const { page, pageSize, ...filters } = parsed.data;
25
+ const result = await service.findAll(page, pageSize, filters);
26
+ res.json(core_1.ApiResponse.paginated(result.items, result.total, page, pageSize));
27
+ }));
28
+ app.post('/api/v1/quiz/questions', auth, (0, core_1.asyncHandler)(async (req, res) => {
29
+ const parsed = question_schema_js_1.CreateQuestionSchema.safeParse(req.body);
30
+ if (!parsed.success)
31
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
32
+ const userId = req.user?.userId;
33
+ const item = await service.create(parsed.data, userId);
34
+ res.status(201).json(core_1.ApiResponse.ok(item, 'Question created'));
35
+ }));
36
+ app.get('/api/v1/quiz/questions/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
37
+ const item = await service.findById(req.params.id);
38
+ res.json(core_1.ApiResponse.ok(item));
39
+ }));
40
+ app.put('/api/v1/quiz/questions/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
41
+ const parsed = question_schema_js_1.UpdateQuestionSchema.safeParse(req.body);
42
+ if (!parsed.success)
43
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
44
+ const item = await service.update(req.params.id, parsed.data);
45
+ res.json(core_1.ApiResponse.ok(item, 'Question updated'));
46
+ }));
47
+ app.delete('/api/v1/quiz/questions/:id', auth, (0, core_1.asyncHandler)(async (req, res) => {
48
+ await service.delete(req.params.id);
49
+ res.json(core_1.ApiResponse.ok(null, 'Question deleted'));
50
+ }));
51
+ app.post('/api/v1/quiz/questions/smart-generate', auth, (0, core_1.asyncHandler)(async (req, res) => {
52
+ const parsed = question_schema_js_1.SmartGenerateSchema.safeParse(req.body);
53
+ if (!parsed.success)
54
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid input');
55
+ const result = await service.smartGenerate(parsed.data);
56
+ res.json(core_1.ApiResponse.ok(result, `Added ${result.added} questions`));
57
+ }));
58
+ }
59
+ //# sourceMappingURL=question.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question.controller.js","sourceRoot":"","sources":["../../src/controllers/question.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Express } from '@octo-cyber/core';
2
+ export declare function registerStatisticsRoutes(app: Express): void;
3
+ //# sourceMappingURL=statistics.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/statistics.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAUnE,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA6B3D"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerStatisticsRoutes = registerStatisticsRoutes;
4
+ const core_1 = require("@octo-cyber/core");
5
+ const zod_1 = require("zod");
6
+ const auth_1 = require("@octo-cyber/auth");
7
+ const statistics_service_js_1 = require("../services/statistics.service.js");
8
+ const RankingQuerySchema = zod_1.z.object({
9
+ limit: zod_1.z.coerce.number().int().min(1).max(100).default(20),
10
+ });
11
+ function registerStatisticsRoutes(app) {
12
+ const auth = (0, auth_1.authMiddleware)();
13
+ const service = core_1.Container.get(statistics_service_js_1.StatisticsService);
14
+ app.get('/api/v1/quiz/exams/:examId/stats', auth, (0, core_1.asyncHandler)(async (req, res) => {
15
+ const stats = await service.getExamStats(req.params.examId);
16
+ res.json(core_1.ApiResponse.ok(stats));
17
+ }));
18
+ app.get('/api/v1/quiz/exams/:examId/ranking', auth, (0, core_1.asyncHandler)(async (req, res) => {
19
+ const parsed = RankingQuerySchema.safeParse(req.query);
20
+ if (!parsed.success)
21
+ throw core_1.AppError.badRequest(parsed.error.issues[0]?.message ?? 'Invalid query');
22
+ const ranking = await service.getRanking(req.params.examId, parsed.data.limit);
23
+ res.json(core_1.ApiResponse.ok(ranking));
24
+ }));
25
+ app.get('/api/v1/quiz/my/stats', auth, (0, core_1.asyncHandler)(async (req, res) => {
26
+ const userId = req.user?.userId;
27
+ if (!userId)
28
+ throw core_1.AppError.unauthorized('Not authenticated');
29
+ const stats = await service.getUserStats(userId);
30
+ res.json(core_1.ApiResponse.ok(stats));
31
+ }));
32
+ app.get('/api/v1/quiz/stats/user/:userId', auth, (0, core_1.asyncHandler)(async (req, res) => {
33
+ const stats = await service.getUserStats(req.params.userId);
34
+ res.json(core_1.ApiResponse.ok(stats));
35
+ }));
36
+ }
37
+ //# sourceMappingURL=statistics.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.controller.js","sourceRoot":"","sources":["../../src/controllers/statistics.controller.ts"],"names":[],"mappings":";;AAUA,4DA6BC;AAtCD,2CAAkF;AAClF,6BAAwB;AACxB,2CAAkD;AAClD,6EAAsE;AAEtE,MAAM,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IAClC,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC3D,CAAC,CAAC;AAEH,SAAgB,wBAAwB,CAAC,GAAY;IACnD,MAAM,IAAI,GAAG,IAAA,qBAAc,GAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,gBAAS,CAAC,GAAG,CAAC,yCAAiB,CAAC,CAAC;IAIjD,GAAG,CAAC,GAAG,CAAC,kCAAkC,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACnG,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,MAAgB,CAAC,CAAC;QACtE,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,oCAAoC,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACrG,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,eAAe,CAAC,CAAC;QACnG,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,MAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzF,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,uBAAuB,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACxF,MAAM,MAAM,GAAI,GAAmB,CAAC,IAAI,EAAE,MAAM,CAAC;QACjD,IAAI,CAAC,MAAM;YAAE,MAAM,eAAQ,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACjD,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC,CAAC;IAEJ,GAAG,CAAC,GAAG,CAAC,iCAAiC,EAAE,IAAI,EAAE,IAAA,mBAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAClG,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,MAAgB,CAAC,CAAC;QACtE,GAAG,CAAC,IAAI,CAAC,kBAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { QuizCategory } from './quiz-category.entity.js';
2
+ import { QuizQuestion } from './quiz-question.entity.js';
3
+ import { QuizPaper } from './quiz-paper.entity.js';
4
+ import { QuizPaperQuestion } from './quiz-paper-question.entity.js';
5
+ import { QuizExam } from './quiz-exam.entity.js';
6
+ import { QuizAttempt } from './quiz-attempt.entity.js';
7
+ import { QuizAnswer } from './quiz-answer.entity.js';
8
+ export { QuizCategory } from './quiz-category.entity.js';
9
+ export { QuizQuestion } from './quiz-question.entity.js';
10
+ export type { QuestionType, DifficultyLevel } from './quiz-question.entity.js';
11
+ export { QUESTION_TYPES, DIFFICULTY_LEVELS } from './quiz-question.entity.js';
12
+ export { QuizPaper } from './quiz-paper.entity.js';
13
+ export type { PaperStatus } from './quiz-paper.entity.js';
14
+ export { PAPER_STATUSES } from './quiz-paper.entity.js';
15
+ export { QuizPaperQuestion } from './quiz-paper-question.entity.js';
16
+ export { QuizExam } from './quiz-exam.entity.js';
17
+ export type { ExamStatus } from './quiz-exam.entity.js';
18
+ export { EXAM_STATUSES } from './quiz-exam.entity.js';
19
+ export { QuizAttempt } from './quiz-attempt.entity.js';
20
+ export type { AttemptStatus } from './quiz-attempt.entity.js';
21
+ export { ATTEMPT_STATUSES } from './quiz-attempt.entity.js';
22
+ export { QuizAnswer } from './quiz-answer.entity.js';
23
+ export declare const QUIZ_ENTITIES: readonly [typeof QuizCategory, typeof QuizQuestion, typeof QuizPaper, typeof QuizPaperQuestion, typeof QuizExam, typeof QuizAttempt, typeof QuizAnswer];
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/entities/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,eAAO,MAAM,aAAa,yJAQhB,CAAC"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QUIZ_ENTITIES = exports.QuizAnswer = exports.ATTEMPT_STATUSES = exports.QuizAttempt = exports.EXAM_STATUSES = exports.QuizExam = exports.QuizPaperQuestion = exports.PAPER_STATUSES = exports.QuizPaper = exports.DIFFICULTY_LEVELS = exports.QUESTION_TYPES = exports.QuizQuestion = exports.QuizCategory = void 0;
4
+ const quiz_category_entity_js_1 = require("./quiz-category.entity.js");
5
+ const quiz_question_entity_js_1 = require("./quiz-question.entity.js");
6
+ const quiz_paper_entity_js_1 = require("./quiz-paper.entity.js");
7
+ const quiz_paper_question_entity_js_1 = require("./quiz-paper-question.entity.js");
8
+ const quiz_exam_entity_js_1 = require("./quiz-exam.entity.js");
9
+ const quiz_attempt_entity_js_1 = require("./quiz-attempt.entity.js");
10
+ const quiz_answer_entity_js_1 = require("./quiz-answer.entity.js");
11
+ var quiz_category_entity_js_2 = require("./quiz-category.entity.js");
12
+ Object.defineProperty(exports, "QuizCategory", { enumerable: true, get: function () { return quiz_category_entity_js_2.QuizCategory; } });
13
+ var quiz_question_entity_js_2 = require("./quiz-question.entity.js");
14
+ Object.defineProperty(exports, "QuizQuestion", { enumerable: true, get: function () { return quiz_question_entity_js_2.QuizQuestion; } });
15
+ var quiz_question_entity_js_3 = require("./quiz-question.entity.js");
16
+ Object.defineProperty(exports, "QUESTION_TYPES", { enumerable: true, get: function () { return quiz_question_entity_js_3.QUESTION_TYPES; } });
17
+ Object.defineProperty(exports, "DIFFICULTY_LEVELS", { enumerable: true, get: function () { return quiz_question_entity_js_3.DIFFICULTY_LEVELS; } });
18
+ var quiz_paper_entity_js_2 = require("./quiz-paper.entity.js");
19
+ Object.defineProperty(exports, "QuizPaper", { enumerable: true, get: function () { return quiz_paper_entity_js_2.QuizPaper; } });
20
+ var quiz_paper_entity_js_3 = require("./quiz-paper.entity.js");
21
+ Object.defineProperty(exports, "PAPER_STATUSES", { enumerable: true, get: function () { return quiz_paper_entity_js_3.PAPER_STATUSES; } });
22
+ var quiz_paper_question_entity_js_2 = require("./quiz-paper-question.entity.js");
23
+ Object.defineProperty(exports, "QuizPaperQuestion", { enumerable: true, get: function () { return quiz_paper_question_entity_js_2.QuizPaperQuestion; } });
24
+ var quiz_exam_entity_js_2 = require("./quiz-exam.entity.js");
25
+ Object.defineProperty(exports, "QuizExam", { enumerable: true, get: function () { return quiz_exam_entity_js_2.QuizExam; } });
26
+ var quiz_exam_entity_js_3 = require("./quiz-exam.entity.js");
27
+ Object.defineProperty(exports, "EXAM_STATUSES", { enumerable: true, get: function () { return quiz_exam_entity_js_3.EXAM_STATUSES; } });
28
+ var quiz_attempt_entity_js_2 = require("./quiz-attempt.entity.js");
29
+ Object.defineProperty(exports, "QuizAttempt", { enumerable: true, get: function () { return quiz_attempt_entity_js_2.QuizAttempt; } });
30
+ var quiz_attempt_entity_js_3 = require("./quiz-attempt.entity.js");
31
+ Object.defineProperty(exports, "ATTEMPT_STATUSES", { enumerable: true, get: function () { return quiz_attempt_entity_js_3.ATTEMPT_STATUSES; } });
32
+ var quiz_answer_entity_js_2 = require("./quiz-answer.entity.js");
33
+ Object.defineProperty(exports, "QuizAnswer", { enumerable: true, get: function () { return quiz_answer_entity_js_2.QuizAnswer; } });
34
+ exports.QUIZ_ENTITIES = [
35
+ quiz_category_entity_js_1.QuizCategory,
36
+ quiz_question_entity_js_1.QuizQuestion,
37
+ quiz_paper_entity_js_1.QuizPaper,
38
+ quiz_paper_question_entity_js_1.QuizPaperQuestion,
39
+ quiz_exam_entity_js_1.QuizExam,
40
+ quiz_attempt_entity_js_1.QuizAttempt,
41
+ quiz_answer_entity_js_1.QuizAnswer,
42
+ ];
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/entities/index.ts"],"names":[],"mappings":";;;AAAA,uEAAyD;AACzD,uEAAyD;AACzD,iEAAmD;AACnD,mFAAoE;AACpE,+DAAiD;AACjD,qEAAuD;AACvD,mEAAqD;AAErD,qEAAyD;AAAhD,uHAAA,YAAY,OAAA;AACrB,qEAAyD;AAAhD,uHAAA,YAAY,OAAA;AAErB,qEAA8E;AAArE,yHAAA,cAAc,OAAA;AAAE,4HAAA,iBAAiB,OAAA;AAC1C,+DAAmD;AAA1C,iHAAA,SAAS,OAAA;AAElB,+DAAwD;AAA/C,sHAAA,cAAc,OAAA;AACvB,iFAAoE;AAA3D,kIAAA,iBAAiB,OAAA;AAC1B,6DAAiD;AAAxC,+GAAA,QAAQ,OAAA;AAEjB,6DAAsD;AAA7C,oHAAA,aAAa,OAAA;AACtB,mEAAuD;AAA9C,qHAAA,WAAW,OAAA;AAEpB,mEAA4D;AAAnD,0HAAA,gBAAgB,OAAA;AACzB,iEAAqD;AAA5C,mHAAA,UAAU,OAAA;AAEN,QAAA,aAAa,GAAG;IAC3B,sCAAY;IACZ,sCAAY;IACZ,gCAAS;IACT,iDAAiB;IACjB,8BAAQ;IACR,oCAAW;IACX,kCAAU;CACF,CAAC"}
@@ -0,0 +1,19 @@
1
+ export declare class QuizAnswer {
2
+ id: string;
3
+ attemptId: string;
4
+ questionId: string;
5
+ /** 用户作答 JSON: string | string[] | null(主观题为字符串) */
6
+ userAnswer: string | null;
7
+ /** 客观题自动判断,主观题阅卷后设置 */
8
+ isCorrect: boolean | null;
9
+ /** 得分 */
10
+ score: number;
11
+ /** 阅卷批注 */
12
+ feedback: string | null;
13
+ /** 批改人 */
14
+ gradedBy: string | null;
15
+ gradedAt: Date | null;
16
+ createdAt: Date;
17
+ updatedAt: Date;
18
+ }
19
+ //# sourceMappingURL=quiz-answer.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quiz-answer.entity.d.ts","sourceRoot":"","sources":["../../src/entities/quiz-answer.entity.ts"],"names":[],"mappings":"AASA,qBACa,UAAU;IAErB,EAAE,EAAG,MAAM,CAAC;IAIZ,SAAS,EAAG,MAAM,CAAC;IAGnB,UAAU,EAAG,MAAM,CAAC;IAEpB,mDAAmD;IAEnD,UAAU,EAAG,MAAM,GAAG,IAAI,CAAC;IAE3B,uBAAuB;IAEvB,SAAS,EAAG,OAAO,GAAG,IAAI,CAAC;IAE3B,SAAS;IAET,KAAK,EAAG,MAAM,CAAC;IAEf,WAAW;IAEX,QAAQ,EAAG,MAAM,GAAG,IAAI,CAAC;IAEzB,UAAU;IAEV,QAAQ,EAAG,MAAM,GAAG,IAAI,CAAC;IAGzB,QAAQ,EAAG,IAAI,GAAG,IAAI,CAAC;IAGvB,SAAS,EAAG,IAAI,CAAC;IAGjB,SAAS,EAAG,IAAI,CAAC;CAClB"}