@hed-hog/lms 0.0.358 → 0.0.364

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 (330) hide show
  1. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +65 -0
  2. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
  3. package/dist/bitcode-wallet/bitcode-wallet.service.js +72 -0
  4. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
  5. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts +8 -0
  6. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts.map +1 -0
  7. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js +40 -0
  8. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js.map +1 -0
  9. package/dist/class-group/class-group.controller.d.ts +16 -16
  10. package/dist/class-group/class-group.service.d.ts +12 -12
  11. package/dist/course/course-audio-transcription.service.d.ts +3 -2
  12. package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
  13. package/dist/course/course-audio-transcription.service.js +49 -8
  14. package/dist/course/course-audio-transcription.service.js.map +1 -1
  15. package/dist/course/course-lesson.controller.d.ts +4 -0
  16. package/dist/course/course-lesson.controller.d.ts.map +1 -1
  17. package/dist/course/course-lesson.controller.js +10 -0
  18. package/dist/course/course-lesson.controller.js.map +1 -1
  19. package/dist/course/course-structure.controller.d.ts +11 -4
  20. package/dist/course/course-structure.controller.d.ts.map +1 -1
  21. package/dist/course/course-structure.controller.js +14 -0
  22. package/dist/course/course-structure.controller.js.map +1 -1
  23. package/dist/course/course-structure.service.d.ts +15 -1
  24. package/dist/course/course-structure.service.d.ts.map +1 -1
  25. package/dist/course/course-structure.service.js +139 -4
  26. package/dist/course/course-structure.service.js.map +1 -1
  27. package/dist/course/course-video-conversion.service.d.ts +8 -0
  28. package/dist/course/course-video-conversion.service.d.ts.map +1 -1
  29. package/dist/course/course-video-conversion.service.js +87 -51
  30. package/dist/course/course-video-conversion.service.js.map +1 -1
  31. package/dist/course/course.controller.d.ts +73 -1
  32. package/dist/course/course.controller.d.ts.map +1 -1
  33. package/dist/course/course.controller.js +27 -3
  34. package/dist/course/course.controller.js.map +1 -1
  35. package/dist/course/course.module.d.ts.map +1 -1
  36. package/dist/course/course.module.js +15 -3
  37. package/dist/course/course.module.js.map +1 -1
  38. package/dist/course/course.service.d.ts +108 -4
  39. package/dist/course/course.service.d.ts.map +1 -1
  40. package/dist/course/course.service.js +631 -30
  41. package/dist/course/course.service.js.map +1 -1
  42. package/dist/course/dto/cleanup-course-storage.dto.d.ts +6 -0
  43. package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -0
  44. package/dist/course/dto/cleanup-course-storage.dto.js +33 -0
  45. package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -0
  46. package/dist/course/dto/cleanup-upload-history.dto.d.ts +9 -0
  47. package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -0
  48. package/dist/course/dto/cleanup-upload-history.dto.js +36 -0
  49. package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -0
  50. package/dist/course/dto/create-course-bulk-job.dto.d.ts +4 -0
  51. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -0
  52. package/dist/course/dto/create-course-bulk-job.dto.js +21 -0
  53. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -0
  54. package/dist/course/lms-bulk-upload-automation.service.d.ts +39 -0
  55. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -0
  56. package/dist/course/lms-bulk-upload-automation.service.js +443 -0
  57. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -0
  58. package/dist/course/lms-bulk-upload-infra.service.d.ts +31 -0
  59. package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -0
  60. package/dist/course/lms-bulk-upload-infra.service.js +277 -0
  61. package/dist/course/lms-bulk-upload-infra.service.js.map +1 -0
  62. package/dist/course/lms-bulk-upload.constants.d.ts +4 -0
  63. package/dist/course/lms-bulk-upload.constants.d.ts.map +1 -0
  64. package/dist/course/lms-bulk-upload.constants.js +7 -0
  65. package/dist/course/lms-bulk-upload.constants.js.map +1 -0
  66. package/dist/course/lms-bulk-upload.controller.d.ts +153 -0
  67. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -0
  68. package/dist/course/lms-bulk-upload.controller.js +129 -0
  69. package/dist/course/lms-bulk-upload.controller.js.map +1 -0
  70. package/dist/course/lms-bulk-upload.service.d.ts +181 -0
  71. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -0
  72. package/dist/course/lms-bulk-upload.service.js +754 -0
  73. package/dist/course/lms-bulk-upload.service.js.map +1 -0
  74. package/dist/course/lms-setting.controller.d.ts +7 -1
  75. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  76. package/dist/course/lms-setting.controller.js +26 -3
  77. package/dist/course/lms-setting.controller.js.map +1 -1
  78. package/dist/enterprise/enterprise.controller.d.ts +20 -20
  79. package/dist/enterprise/enterprise.service.d.ts +20 -20
  80. package/dist/enterprise/training/training-admin.controller.d.ts +11 -11
  81. package/dist/enterprise/training/training-admin.service.d.ts +11 -11
  82. package/dist/enterprise/training/training-instructor.controller.d.ts +2 -2
  83. package/dist/enterprise/training/training-instructor.service.d.ts +2 -2
  84. package/dist/enterprise/training/training-student.controller.d.ts +1 -1
  85. package/dist/enterprise/training/training-student.service.d.ts +1 -1
  86. package/dist/enterprise/training/training-viewer.controller.d.ts +2 -2
  87. package/dist/evaluation/evaluation.controller.d.ts +8 -8
  88. package/dist/evaluation/evaluation.service.d.ts +8 -8
  89. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts +6 -0
  90. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts.map +1 -0
  91. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js +34 -0
  92. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js.map +1 -0
  93. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts +28 -0
  94. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts.map +1 -0
  95. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js +123 -0
  96. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js.map +1 -0
  97. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts +4 -0
  98. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts.map +1 -0
  99. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js +22 -0
  100. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js.map +1 -0
  101. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts +10 -0
  102. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts.map +1 -0
  103. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js +52 -0
  104. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js.map +1 -0
  105. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts +15 -0
  106. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts.map +1 -0
  107. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js +86 -0
  108. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js.map +1 -0
  109. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +26 -0
  110. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -0
  111. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +304 -0
  112. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -0
  113. package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts +87 -0
  114. package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts.map +1 -0
  115. package/dist/lesson-xp-map/lesson-xp-map.controller.js +185 -0
  116. package/dist/lesson-xp-map/lesson-xp-map.controller.js.map +1 -0
  117. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts +3 -0
  118. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -0
  119. package/dist/lesson-xp-map/lesson-xp-map.module.js +34 -0
  120. package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -0
  121. package/dist/lesson-xp-map/lesson-xp-map.service.d.ts +84 -0
  122. package/dist/lesson-xp-map/lesson-xp-map.service.d.ts.map +1 -0
  123. package/dist/lesson-xp-map/lesson-xp-map.service.js +353 -0
  124. package/dist/lesson-xp-map/lesson-xp-map.service.js.map +1 -0
  125. package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts +10 -0
  126. package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts.map +1 -0
  127. package/dist/lesson-xp-map/lesson-xp-segment.controller.js +63 -0
  128. package/dist/lesson-xp-map/lesson-xp-segment.controller.js.map +1 -0
  129. package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts +27 -0
  130. package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts.map +1 -0
  131. package/dist/lesson-xp-map/lesson-xp-segment.service.js +194 -0
  132. package/dist/lesson-xp-map/lesson-xp-segment.service.js.map +1 -0
  133. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -0
  134. package/dist/lms.module.d.ts.map +1 -1
  135. package/dist/lms.module.js +17 -2
  136. package/dist/lms.module.js.map +1 -1
  137. package/dist/platforma/dto/update-profile.dto.d.ts +17 -0
  138. package/dist/platforma/dto/update-profile.dto.d.ts.map +1 -0
  139. package/dist/platforma/dto/update-profile.dto.js +87 -0
  140. package/dist/platforma/dto/update-profile.dto.js.map +1 -0
  141. package/dist/platforma/platforma.controller.d.ts +94 -7
  142. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  143. package/dist/platforma/platforma.controller.js +85 -2
  144. package/dist/platforma/platforma.controller.js.map +1 -1
  145. package/dist/platforma/platforma.service.d.ts +27 -0
  146. package/dist/platforma/platforma.service.d.ts.map +1 -0
  147. package/dist/platforma/platforma.service.js +274 -0
  148. package/dist/platforma/platforma.service.js.map +1 -0
  149. package/dist/student-xp/student-xp.controller.d.ts +41 -0
  150. package/dist/student-xp/student-xp.controller.d.ts.map +1 -0
  151. package/dist/student-xp/student-xp.controller.js +114 -0
  152. package/dist/student-xp/student-xp.controller.js.map +1 -0
  153. package/dist/student-xp/student-xp.module.d.ts +3 -0
  154. package/dist/student-xp/student-xp.module.d.ts.map +1 -0
  155. package/dist/student-xp/student-xp.module.js +25 -0
  156. package/dist/student-xp/student-xp.module.js.map +1 -0
  157. package/dist/student-xp/student-xp.service.d.ts +65 -0
  158. package/dist/student-xp/student-xp.service.d.ts.map +1 -0
  159. package/dist/student-xp/student-xp.service.js +197 -0
  160. package/dist/student-xp/student-xp.service.js.map +1 -0
  161. package/dist/xp-catalog/dto/create-xp-area.dto.d.ts +12 -0
  162. package/dist/xp-catalog/dto/create-xp-area.dto.d.ts.map +1 -0
  163. package/dist/xp-catalog/dto/create-xp-area.dto.js +63 -0
  164. package/dist/xp-catalog/dto/create-xp-area.dto.js.map +1 -0
  165. package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts +11 -0
  166. package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts.map +1 -0
  167. package/dist/xp-catalog/dto/create-xp-learning-type.dto.js +57 -0
  168. package/dist/xp-catalog/dto/create-xp-learning-type.dto.js.map +1 -0
  169. package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts +11 -0
  170. package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts.map +1 -0
  171. package/dist/xp-catalog/dto/create-xp-skill.dto.js +57 -0
  172. package/dist/xp-catalog/dto/create-xp-skill.dto.js.map +1 -0
  173. package/dist/xp-catalog/dto/update-xp-area.dto.d.ts +12 -0
  174. package/dist/xp-catalog/dto/update-xp-area.dto.d.ts.map +1 -0
  175. package/dist/xp-catalog/dto/update-xp-area.dto.js +66 -0
  176. package/dist/xp-catalog/dto/update-xp-area.dto.js.map +1 -0
  177. package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts +11 -0
  178. package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts.map +1 -0
  179. package/dist/xp-catalog/dto/update-xp-learning-type.dto.js +60 -0
  180. package/dist/xp-catalog/dto/update-xp-learning-type.dto.js.map +1 -0
  181. package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts +11 -0
  182. package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts.map +1 -0
  183. package/dist/xp-catalog/dto/update-xp-skill.dto.js +60 -0
  184. package/dist/xp-catalog/dto/update-xp-skill.dto.js.map +1 -0
  185. package/dist/xp-catalog/xp-area.controller.d.ts +25 -0
  186. package/dist/xp-catalog/xp-area.controller.d.ts.map +1 -0
  187. package/dist/xp-catalog/xp-area.controller.js +105 -0
  188. package/dist/xp-catalog/xp-area.controller.js.map +1 -0
  189. package/dist/xp-catalog/xp-area.service.d.ts +35 -0
  190. package/dist/xp-catalog/xp-area.service.d.ts.map +1 -0
  191. package/dist/xp-catalog/xp-area.service.js +168 -0
  192. package/dist/xp-catalog/xp-area.service.js.map +1 -0
  193. package/dist/xp-catalog/xp-catalog.module.d.ts +3 -0
  194. package/dist/xp-catalog/xp-catalog.module.d.ts.map +1 -0
  195. package/dist/xp-catalog/xp-catalog.module.js +29 -0
  196. package/dist/xp-catalog/xp-catalog.module.js.map +1 -0
  197. package/dist/xp-catalog/xp-learning-type.controller.d.ts +20 -0
  198. package/dist/xp-catalog/xp-learning-type.controller.d.ts.map +1 -0
  199. package/dist/xp-catalog/xp-learning-type.controller.js +96 -0
  200. package/dist/xp-catalog/xp-learning-type.controller.js.map +1 -0
  201. package/dist/xp-catalog/xp-learning-type.service.d.ts +30 -0
  202. package/dist/xp-catalog/xp-learning-type.service.d.ts.map +1 -0
  203. package/dist/xp-catalog/xp-learning-type.service.js +146 -0
  204. package/dist/xp-catalog/xp-learning-type.service.js.map +1 -0
  205. package/dist/xp-catalog/xp-skill.controller.d.ts +26 -0
  206. package/dist/xp-catalog/xp-skill.controller.d.ts.map +1 -0
  207. package/dist/xp-catalog/xp-skill.controller.js +113 -0
  208. package/dist/xp-catalog/xp-skill.controller.js.map +1 -0
  209. package/dist/xp-catalog/xp-skill.service.d.ts +37 -0
  210. package/dist/xp-catalog/xp-skill.service.d.ts.map +1 -0
  211. package/dist/xp-catalog/xp-skill.service.js +174 -0
  212. package/dist/xp-catalog/xp-skill.service.js.map +1 -0
  213. package/hedhog/data/menu.yaml +101 -0
  214. package/hedhog/data/role.yaml +8 -0
  215. package/hedhog/data/route.yaml +547 -0
  216. package/hedhog/data/setting_group.yaml +33 -0
  217. package/hedhog/data/xp_area.yaml +164 -0
  218. package/hedhog/data/xp_learning_type.yaml +131 -0
  219. package/hedhog/data/xp_skill.yaml +1834 -0
  220. package/hedhog/frontend/app/achievements/page.tsx.ejs +108 -118
  221. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +22 -34
  222. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +1453 -0
  223. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +21 -45
  224. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +40 -74
  225. package/hedhog/frontend/app/classes/page.tsx.ejs +56 -85
  226. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +3 -2
  227. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +48 -5
  228. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +4 -4
  229. package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +19 -2
  230. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +1170 -0
  231. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +16 -0
  232. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +2 -0
  233. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +623 -0
  234. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +1458 -0
  235. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +55 -2
  236. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +442 -104
  237. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +296 -49
  238. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +3 -0
  239. package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +1 -0
  240. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +101 -85
  241. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +21 -1
  242. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +3 -0
  243. package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +7 -1
  244. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +44 -0
  245. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +54 -0
  246. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +52 -0
  247. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-xp-overview.ts.ejs +76 -0
  248. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-xp-map.ts.ejs +128 -0
  249. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +30 -0
  250. package/hedhog/frontend/app/courses/[id]/structure/_utils/xp-color-config.ts.ejs +115 -0
  251. package/hedhog/frontend/app/courses/_components/CourseDeleteDialog.tsx.ejs +223 -0
  252. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +89 -0
  253. package/hedhog/frontend/app/courses/page.tsx.ejs +400 -230
  254. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -63
  255. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +53 -77
  256. package/hedhog/frontend/app/exams/page.tsx.ejs +54 -90
  257. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +23 -36
  258. package/hedhog/frontend/app/instructors/page.tsx.ejs +72 -81
  259. package/hedhog/frontend/app/paths/page.tsx.ejs +40 -68
  260. package/hedhog/frontend/app/training/page.tsx.ejs +40 -68
  261. package/hedhog/frontend/app/xp/areas/page.tsx.ejs +782 -0
  262. package/hedhog/frontend/app/xp/learning-types/page.tsx.ejs +690 -0
  263. package/hedhog/frontend/app/xp/skills/page.tsx.ejs +811 -0
  264. package/hedhog/frontend/messages/en.json +386 -3
  265. package/hedhog/frontend/messages/pt.json +386 -3
  266. package/hedhog/table/lesson_xp_map.yaml +50 -0
  267. package/hedhog/table/lesson_xp_segment.yaml +40 -0
  268. package/hedhog/table/lesson_xp_segment_area.yaml +24 -0
  269. package/hedhog/table/lesson_xp_segment_learning_type.yaml +24 -0
  270. package/hedhog/table/lesson_xp_segment_skill.yaml +24 -0
  271. package/hedhog/table/lms_bulk_upload_item.yaml +44 -0
  272. package/hedhog/table/lms_bulk_upload_session.yaml +42 -0
  273. package/hedhog/table/student_area_xp.yaml +30 -0
  274. package/hedhog/table/student_learning_type_xp.yaml +30 -0
  275. package/hedhog/table/student_skill_xp.yaml +30 -0
  276. package/hedhog/table/student_xp_event.yaml +34 -0
  277. package/hedhog/table/xp_area.yaml +39 -0
  278. package/hedhog/table/xp_learning_type.yaml +34 -0
  279. package/hedhog/table/xp_skill.yaml +39 -0
  280. package/package.json +9 -8
  281. package/src/bitcode-wallet/bitcode-wallet.service.ts +113 -0
  282. package/src/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.ts +32 -0
  283. package/src/course/course-audio-transcription.service.ts +58 -21
  284. package/src/course/course-lesson.controller.ts +6 -1
  285. package/src/course/course-structure.controller.ts +10 -0
  286. package/src/course/course-structure.service.ts +174 -1
  287. package/src/course/course-video-conversion.service.ts +113 -75
  288. package/src/course/course.controller.ts +22 -3
  289. package/src/course/course.module.ts +15 -3
  290. package/src/course/course.service.ts +847 -30
  291. package/src/course/dto/cleanup-course-storage.dto.ts +22 -0
  292. package/src/course/dto/cleanup-upload-history.dto.ts +26 -0
  293. package/src/course/dto/create-course-bulk-job.dto.ts +6 -0
  294. package/src/course/lms-bulk-upload-automation.service.ts +560 -0
  295. package/src/course/lms-bulk-upload-infra.service.ts +327 -0
  296. package/src/course/lms-bulk-upload.constants.ts +5 -0
  297. package/src/course/lms-bulk-upload.controller.ts +103 -0
  298. package/src/course/lms-bulk-upload.service.ts +1029 -0
  299. package/src/course/lms-setting.controller.ts +28 -1
  300. package/src/lesson-xp-map/dto/create-lesson-xp-map.dto.ts +17 -0
  301. package/src/lesson-xp-map/dto/create-lesson-xp-segment.dto.ts +102 -0
  302. package/src/lesson-xp-map/dto/review-lesson-xp-map.dto.ts +7 -0
  303. package/src/lesson-xp-map/dto/update-lesson-xp-map.dto.ts +36 -0
  304. package/src/lesson-xp-map/dto/update-lesson-xp-segment.dto.ts +78 -0
  305. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +396 -0
  306. package/src/lesson-xp-map/lesson-xp-map.controller.ts +116 -0
  307. package/src/lesson-xp-map/lesson-xp-map.module.ts +21 -0
  308. package/src/lesson-xp-map/lesson-xp-map.service.ts +442 -0
  309. package/src/lesson-xp-map/lesson-xp-segment.controller.ts +36 -0
  310. package/src/lesson-xp-map/lesson-xp-segment.service.ts +229 -0
  311. package/src/lms.module.ts +17 -2
  312. package/src/platforma/dto/update-profile.dto.ts +59 -0
  313. package/src/platforma/platforma.controller.ts +57 -2
  314. package/src/platforma/platforma.service.ts +268 -0
  315. package/src/student-xp/student-xp.controller.ts +76 -0
  316. package/src/student-xp/student-xp.module.ts +12 -0
  317. package/src/student-xp/student-xp.service.ts +236 -0
  318. package/src/xp-catalog/dto/create-xp-area.dto.ts +40 -0
  319. package/src/xp-catalog/dto/create-xp-learning-type.dto.ts +35 -0
  320. package/src/xp-catalog/dto/create-xp-skill.dto.ts +35 -0
  321. package/src/xp-catalog/dto/update-xp-area.dto.ts +43 -0
  322. package/src/xp-catalog/dto/update-xp-learning-type.dto.ts +38 -0
  323. package/src/xp-catalog/dto/update-xp-skill.dto.ts +38 -0
  324. package/src/xp-catalog/xp-area.controller.ts +64 -0
  325. package/src/xp-catalog/xp-area.service.ts +196 -0
  326. package/src/xp-catalog/xp-catalog.module.ts +16 -0
  327. package/src/xp-catalog/xp-learning-type.controller.ts +59 -0
  328. package/src/xp-catalog/xp-learning-type.service.ts +170 -0
  329. package/src/xp-catalog/xp-skill.controller.ts +71 -0
  330. package/src/xp-catalog/xp-skill.service.ts +205 -0
@@ -0,0 +1,754 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.LmsBulkUploadService = void 0;
16
+ const api_prisma_1 = require("@hed-hog/api-prisma");
17
+ const core_1 = require("@hed-hog/core");
18
+ const common_1 = require("@nestjs/common");
19
+ const cleanup_upload_history_dto_1 = require("./dto/cleanup-upload-history.dto");
20
+ const lms_bulk_upload_infra_service_1 = require("./lms-bulk-upload-infra.service");
21
+ const lms_bulk_upload_constants_1 = require("./lms-bulk-upload.constants");
22
+ let LmsBulkUploadService = class LmsBulkUploadService {
23
+ constructor(settingService, prismaService, fileService, webhookIntegrationService, bulkUploadInfraService) {
24
+ this.settingService = settingService;
25
+ this.prismaService = prismaService;
26
+ this.fileService = fileService;
27
+ this.webhookIntegrationService = webhookIntegrationService;
28
+ this.bulkUploadInfraService = bulkUploadInfraService;
29
+ }
30
+ async configureBulkUpload(userId, payload) {
31
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
32
+ const storageProfileId = Number((_a = payload === null || payload === void 0 ? void 0 : payload.storageProfileId) !== null && _a !== void 0 ? _a : 0);
33
+ const settings = await this.settingService.getSettingValues([
34
+ 'lms-bulk-upload-lambda-role-arn',
35
+ ]);
36
+ const lambdaRoleArn = String((_b = settings['lms-bulk-upload-lambda-role-arn']) !== null && _b !== void 0 ? _b : '').trim();
37
+ if (!Number.isFinite(storageProfileId) || storageProfileId <= 0) {
38
+ throw new common_1.BadRequestException('Select a valid storage integration profile.');
39
+ }
40
+ const { profile, region, bucket, durationSeconds } = await this.resolveStorageProfileAndBucket(userId, {
41
+ storageProfileId,
42
+ });
43
+ const credentials = profile.config;
44
+ const accessKeyId = String((_c = credentials.access_key_id) !== null && _c !== void 0 ? _c : '').trim();
45
+ const secretAccessKey = String((_d = credentials.secret_access_key) !== null && _d !== void 0 ? _d : '').trim();
46
+ const sessionToken = String((_e = credentials.session_token) !== null && _e !== void 0 ? _e : '').trim() || undefined;
47
+ if (!accessKeyId || !secretAccessKey) {
48
+ throw new common_1.BadRequestException('Storage profile does not contain AWS credentials (access_key_id/secret_access_key).');
49
+ }
50
+ await this.fileService.headS3Bucket({
51
+ accessKeyId,
52
+ secretAccessKey,
53
+ sessionToken,
54
+ region,
55
+ bucket,
56
+ });
57
+ const roleArn = String((_f = credentials.role_arn) !== null && _f !== void 0 ? _f : '').trim();
58
+ const externalId = String((_g = credentials.external_id) !== null && _g !== void 0 ? _g : '').trim();
59
+ const tempCredentials = await this.fileService.getTemporaryCredentials({
60
+ accessKeyId,
61
+ secretAccessKey,
62
+ sessionToken,
63
+ region,
64
+ roleArn: roleArn || undefined,
65
+ externalId: externalId || undefined,
66
+ durationSeconds,
67
+ sessionName: `hedhog-desktop-u${userId}-${Date.now()}`,
68
+ });
69
+ await this.fileService.headS3Bucket({
70
+ accessKeyId: tempCredentials.AccessKeyId,
71
+ secretAccessKey: tempCredentials.SecretAccessKey,
72
+ sessionToken: tempCredentials.SessionToken,
73
+ region,
74
+ bucket,
75
+ });
76
+ const webhook = await this.ensureBulkUploadWebhook();
77
+ if (!webhook.public_url || !webhook.plainToken) {
78
+ throw new common_1.BadRequestException('Bulk upload webhook could not be provisioned with URL/token.');
79
+ }
80
+ const infrastructure = await this.bulkUploadInfraService.syncDesktopUploadInfrastructure({
81
+ bucketName: bucket,
82
+ region,
83
+ webhookUrl: String(webhook.public_url),
84
+ webhookSecret: String(webhook.plainToken),
85
+ accessKeyId,
86
+ secretAccessKey,
87
+ sessionToken,
88
+ lambdaRoleArn,
89
+ });
90
+ await this.settingService.setManySettings({
91
+ setting: [
92
+ {
93
+ slug: 'lms-bulk-upload-storage-profile-id',
94
+ value: String(storageProfileId),
95
+ },
96
+ ],
97
+ });
98
+ return {
99
+ success: true,
100
+ storageProfileId,
101
+ bucketName: bucket,
102
+ region,
103
+ credentials: {
104
+ expiresAt: (_j = (_h = tempCredentials.Expiration) === null || _h === void 0 ? void 0 : _h.toISOString()) !== null && _j !== void 0 ? _j : null,
105
+ },
106
+ infrastructure,
107
+ webhook,
108
+ };
109
+ }
110
+ async regenerateWebhookTokenAndSync(userId) {
111
+ var _a, _b, _c, _d;
112
+ const { profile, region, bucket } = await this.resolveStorageProfileAndBucket(userId);
113
+ const settings = await this.settingService.getSettingValues([
114
+ 'lms-bulk-upload-lambda-role-arn',
115
+ ]);
116
+ const lambdaRoleArn = String((_a = settings['lms-bulk-upload-lambda-role-arn']) !== null && _a !== void 0 ? _a : '').trim();
117
+ const credentials = profile.config;
118
+ const accessKeyId = String((_b = credentials.access_key_id) !== null && _b !== void 0 ? _b : '').trim();
119
+ const secretAccessKey = String((_c = credentials.secret_access_key) !== null && _c !== void 0 ? _c : '').trim();
120
+ const sessionToken = String((_d = credentials.session_token) !== null && _d !== void 0 ? _d : '').trim() || undefined;
121
+ if (!accessKeyId || !secretAccessKey) {
122
+ throw new common_1.BadRequestException('Storage profile does not contain AWS credentials (access_key_id/secret_access_key).');
123
+ }
124
+ await this.fileService.headS3Bucket({
125
+ accessKeyId,
126
+ secretAccessKey,
127
+ sessionToken,
128
+ region,
129
+ bucket,
130
+ });
131
+ const webhook = await this.ensureBulkUploadWebhook();
132
+ if (!webhook.public_url || !webhook.plainToken) {
133
+ throw new common_1.BadRequestException('Bulk upload webhook could not be provisioned with URL/token.');
134
+ }
135
+ const infrastructure = await this.bulkUploadInfraService.syncDesktopUploadInfrastructure({
136
+ bucketName: bucket,
137
+ region,
138
+ webhookUrl: String(webhook.public_url),
139
+ webhookSecret: String(webhook.plainToken),
140
+ accessKeyId,
141
+ secretAccessKey,
142
+ sessionToken,
143
+ lambdaRoleArn,
144
+ });
145
+ return {
146
+ success: true,
147
+ storageProfileId: Number(profile.id),
148
+ bucketName: bucket,
149
+ region,
150
+ infrastructure,
151
+ webhook,
152
+ };
153
+ }
154
+ async setBulkUploadLambdaRoleArn(value) {
155
+ const lambdaRoleArn = String(value !== null && value !== void 0 ? value : '').trim();
156
+ await this.settingService.setManySettings({
157
+ setting: [
158
+ {
159
+ slug: 'lms-bulk-upload-lambda-role-arn',
160
+ value: lambdaRoleArn,
161
+ },
162
+ ],
163
+ });
164
+ return {
165
+ success: true,
166
+ lambdaRoleArn,
167
+ };
168
+ }
169
+ async getBulkUploadSettings() {
170
+ var _a, _b, _c;
171
+ const settings = await this.settingService.getSettingValues([
172
+ 'lms-bulk-upload-storage-profile-id',
173
+ 'lms-bulk-upload-lambda-role-arn',
174
+ 'lms-bulk-upload-sts-duration-seconds',
175
+ ]);
176
+ const storageProfileId = Number(settings['lms-bulk-upload-storage-profile-id'] || 0) || null;
177
+ let bucketName = '';
178
+ if (storageProfileId) {
179
+ const profile = await this.prismaService.integration_profile.findUnique({
180
+ where: { id: storageProfileId },
181
+ select: {
182
+ config: true,
183
+ },
184
+ });
185
+ const config = (_a = profile === null || profile === void 0 ? void 0 : profile.config) !== null && _a !== void 0 ? _a : null;
186
+ bucketName = String((_b = config === null || config === void 0 ? void 0 : config.bucket) !== null && _b !== void 0 ? _b : '').trim();
187
+ }
188
+ return {
189
+ storageProfileId,
190
+ bucketName,
191
+ lambdaRoleArn: String((_c = settings['lms-bulk-upload-lambda-role-arn']) !== null && _c !== void 0 ? _c : '').trim(),
192
+ sessionDurationSeconds: Number(settings['lms-bulk-upload-sts-duration-seconds'] || 3600) || 3600,
193
+ };
194
+ }
195
+ async getTemporaryCredentials(userId) {
196
+ var _a, _b, _c, _d, _e, _f, _g;
197
+ const { profile, region, bucket, durationSeconds, prefix } = await this.resolveStorageProfileAndBucket(userId);
198
+ const credentials = profile.config;
199
+ const accessKeyId = String((_a = credentials.access_key_id) !== null && _a !== void 0 ? _a : '').trim();
200
+ const secretAccessKey = String((_b = credentials.secret_access_key) !== null && _b !== void 0 ? _b : '').trim();
201
+ const sessionToken = String((_c = credentials.session_token) !== null && _c !== void 0 ? _c : '').trim() || undefined;
202
+ if (!accessKeyId || !secretAccessKey) {
203
+ throw new common_1.BadRequestException('Storage profile does not contain AWS credentials (access_key_id/secret_access_key).');
204
+ }
205
+ const roleArn = String((_d = credentials.role_arn) !== null && _d !== void 0 ? _d : '').trim();
206
+ const externalId = String((_e = credentials.external_id) !== null && _e !== void 0 ? _e : '').trim();
207
+ const tempCredentials = await this.fileService.getTemporaryCredentials({
208
+ accessKeyId,
209
+ secretAccessKey,
210
+ sessionToken,
211
+ region,
212
+ roleArn: roleArn || undefined,
213
+ externalId: externalId || undefined,
214
+ durationSeconds,
215
+ sessionName: `hedhog-desktop-u${userId}-${Date.now()}`,
216
+ });
217
+ return {
218
+ provider: 'aws-s3',
219
+ bucket,
220
+ region,
221
+ keyPrefix: prefix,
222
+ credentials: {
223
+ accessKeyId: tempCredentials.AccessKeyId,
224
+ secretAccessKey: tempCredentials.SecretAccessKey,
225
+ sessionToken: tempCredentials.SessionToken,
226
+ expiresAt: (_g = (_f = tempCredentials.Expiration) === null || _f === void 0 ? void 0 : _f.toISOString()) !== null && _g !== void 0 ? _g : null,
227
+ },
228
+ };
229
+ }
230
+ async verifyFileOnS3(userId, payload) {
231
+ var _a, _b, _c, _d, _e, _f, _g;
232
+ const { profile, region, bucket, prefix } = await this.resolveStorageProfileAndBucket(userId);
233
+ const credentials = profile.config;
234
+ const accessKeyId = String((_a = credentials.access_key_id) !== null && _a !== void 0 ? _a : '').trim();
235
+ const secretAccessKey = String((_b = credentials.secret_access_key) !== null && _b !== void 0 ? _b : '').trim();
236
+ const sessionToken = String((_c = credentials.session_token) !== null && _c !== void 0 ? _c : '').trim() || undefined;
237
+ if (!accessKeyId || !secretAccessKey) {
238
+ throw new common_1.BadRequestException('Storage profile does not contain AWS credentials (access_key_id/secret_access_key).');
239
+ }
240
+ const objectKey = String((_d = payload.key) !== null && _d !== void 0 ? _d : '').trim() || `${prefix}${String((_e = payload.fileName) !== null && _e !== void 0 ? _e : '').trim()}`;
241
+ if (!objectKey) {
242
+ throw new common_1.BadRequestException('Provide file key or fileName to verify on S3.');
243
+ }
244
+ try {
245
+ const result = await this.fileService.headS3Object({
246
+ accessKeyId,
247
+ secretAccessKey,
248
+ sessionToken,
249
+ region,
250
+ bucket,
251
+ key: objectKey,
252
+ });
253
+ return {
254
+ isValid: true,
255
+ key: objectKey,
256
+ sizeBytes: (_f = result.ContentLength) !== null && _f !== void 0 ? _f : null,
257
+ etag: (_g = result.ETag) !== null && _g !== void 0 ? _g : null,
258
+ };
259
+ }
260
+ catch (_h) {
261
+ return {
262
+ isValid: false,
263
+ key: objectKey,
264
+ };
265
+ }
266
+ }
267
+ async ensureBulkUploadWebhook() {
268
+ const existing = await this.prismaService.webhook_integration.findFirst({
269
+ where: {
270
+ OR: [
271
+ { slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_WEBHOOK_SLUG },
272
+ { slug: { startsWith: `${lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_WEBHOOK_SLUG}-` } },
273
+ ],
274
+ },
275
+ orderBy: { id: 'asc' },
276
+ select: {
277
+ id: true,
278
+ slug: true,
279
+ status: true,
280
+ },
281
+ });
282
+ const webhook = !existing
283
+ ? await this.webhookIntegrationService.create({
284
+ slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_WEBHOOK_SLUG,
285
+ name: 'LMS Bulk Upload',
286
+ description: 'Webhook para integração de upload em massa do LMS',
287
+ status: 'active',
288
+ require_token: true,
289
+ })
290
+ : existing.status !== 'active'
291
+ ? await this.webhookIntegrationService.update(existing.id, {
292
+ status: 'active',
293
+ require_token: true,
294
+ })
295
+ : await this.webhookIntegrationService.get(existing.id);
296
+ const actions = await this.webhookIntegrationService.listActions(webhook.id);
297
+ const receiveAction = actions.find((action) => action.type === 'app_command' &&
298
+ action.app_command_slug === lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_RECEIVE_VIDEO_COMMAND);
299
+ if (!receiveAction) {
300
+ await this.webhookIntegrationService.createAction(webhook.id, {
301
+ type: 'app_command',
302
+ name: 'Receber vídeo do upload em massa',
303
+ status: 'active',
304
+ order: 1,
305
+ app_command_slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_RECEIVE_VIDEO_COMMAND,
306
+ app_command_params: {},
307
+ });
308
+ }
309
+ else if (receiveAction.status !== 'active' || receiveAction.order !== 1) {
310
+ await this.webhookIntegrationService.updateAction(webhook.id, receiveAction.id, {
311
+ type: 'app_command',
312
+ name: 'Receber vídeo do upload em massa',
313
+ status: 'active',
314
+ order: 1,
315
+ app_command_slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_RECEIVE_VIDEO_COMMAND,
316
+ app_command_params: {},
317
+ });
318
+ }
319
+ return this.webhookIntegrationService.regenerateToken(webhook.id);
320
+ }
321
+ async startUploadSession(userId, payload) {
322
+ var _a, _b, _c, _d, _e;
323
+ const files = ((_a = payload === null || payload === void 0 ? void 0 : payload.files) !== null && _a !== void 0 ? _a : [])
324
+ .map((file) => {
325
+ var _a, _b, _c;
326
+ return ({
327
+ uploadId: String((_a = file === null || file === void 0 ? void 0 : file.uploadId) !== null && _a !== void 0 ? _a : '').trim(),
328
+ fileName: String((_b = file === null || file === void 0 ? void 0 : file.fileName) !== null && _b !== void 0 ? _b : '').trim(),
329
+ sizeBytes: Math.max(0, Number((_c = file === null || file === void 0 ? void 0 : file.sizeBytes) !== null && _c !== void 0 ? _c : 0) || 0),
330
+ });
331
+ })
332
+ .filter((file) => file.uploadId.length > 0 && file.fileName.length > 0);
333
+ if (files.length === 0) {
334
+ throw new common_1.BadRequestException('Provide at least one file to register bulk upload session.');
335
+ }
336
+ const appName = String((_b = payload === null || payload === void 0 ? void 0 : payload.appName) !== null && _b !== void 0 ? _b : 'hedhog-desktop').trim().slice(0, 80) || 'hedhog-desktop';
337
+ const folderPath = String((_c = payload === null || payload === void 0 ? void 0 : payload.folderPath) !== null && _c !== void 0 ? _c : '').trim() || null;
338
+ const totalFiles = files.length;
339
+ const totalBytes = files.reduce((sum, file) => sum + file.sizeBytes, 0);
340
+ const now = new Date();
341
+ const insertedSession = await this.prismaService.$queryRawUnsafe(`INSERT INTO "lms_bulk_upload_session"
342
+ ("user_id", "app_name", "folder_path", "status", "progress_percent", "total_files", "total_bytes", "heartbeat_at", "started_at", "created_at", "updated_at")
343
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
344
+ RETURNING "id"`, userId, appName, folderPath, 'started', 0, totalFiles, totalBytes, now, now, now, now);
345
+ const sessionId = Number((_e = (_d = insertedSession === null || insertedSession === void 0 ? void 0 : insertedSession[0]) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : 0);
346
+ if (!Number.isFinite(sessionId) || sessionId <= 0) {
347
+ throw new common_1.BadRequestException('Could not register bulk upload session.');
348
+ }
349
+ for (const file of files) {
350
+ await this.prismaService.$executeRawUnsafe(`INSERT INTO "lms_bulk_upload_item"
351
+ ("session_id", "upload_id", "file_name", "size_bytes", "status", "progress_percent", "last_heartbeat_at", "created_at", "updated_at")
352
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, sessionId, file.uploadId, file.fileName, file.sizeBytes, 'queued', 0, now, now, now);
353
+ }
354
+ return {
355
+ sessionId,
356
+ totalFiles,
357
+ totalBytes,
358
+ status: 'started',
359
+ };
360
+ }
361
+ async heartbeatUploadSession(userId, payload) {
362
+ var _a, _b, _c, _d, _e, _f;
363
+ const sessionId = Number((_a = payload === null || payload === void 0 ? void 0 : payload.sessionId) !== null && _a !== void 0 ? _a : 0);
364
+ if (!Number.isFinite(sessionId) || sessionId <= 0) {
365
+ throw new common_1.BadRequestException('Provide a valid sessionId for heartbeat.');
366
+ }
367
+ const sessionRows = await this.prismaService.$queryRawUnsafe(`SELECT "id", "user_id"
368
+ FROM "lms_bulk_upload_session"
369
+ WHERE "id" = $1
370
+ LIMIT 1`, sessionId);
371
+ const session = (_b = sessionRows === null || sessionRows === void 0 ? void 0 : sessionRows[0]) !== null && _b !== void 0 ? _b : null;
372
+ if (!session) {
373
+ throw new common_1.NotFoundException(`Bulk upload session ${sessionId} was not found.`);
374
+ }
375
+ if (Number(session.user_id) !== userId) {
376
+ throw new common_1.BadRequestException('You can only update your own bulk upload session.');
377
+ }
378
+ const now = new Date();
379
+ const normalizedItems = ((_c = payload === null || payload === void 0 ? void 0 : payload.items) !== null && _c !== void 0 ? _c : []).map((item) => {
380
+ var _a, _b, _c;
381
+ const status = this.normalizeItemStatus(item === null || item === void 0 ? void 0 : item.status);
382
+ return {
383
+ uploadId: String((_a = item === null || item === void 0 ? void 0 : item.uploadId) !== null && _a !== void 0 ? _a : '').trim(),
384
+ status,
385
+ progressPercent: this.clampPercent(item === null || item === void 0 ? void 0 : item.progressPercent),
386
+ errorMessage: String((_b = item === null || item === void 0 ? void 0 : item.errorMessage) !== null && _b !== void 0 ? _b : '').trim() || null,
387
+ uploadedKey: String((_c = item === null || item === void 0 ? void 0 : item.uploadedKey) !== null && _c !== void 0 ? _c : '').trim() || null,
388
+ };
389
+ }).filter((item) => item.uploadId.length > 0);
390
+ for (const item of normalizedItems) {
391
+ await this.prismaService.$executeRawUnsafe(`UPDATE "lms_bulk_upload_item"
392
+ SET
393
+ "status" = $3,
394
+ "progress_percent" = $4,
395
+ "error_message" = $5,
396
+ "uploaded_key" = $6,
397
+ "last_heartbeat_at" = $7,
398
+ "completed_at" = CASE WHEN $3 IN ('received', 'done', 'error', 'cancelled') THEN COALESCE("completed_at", $7) ELSE "completed_at" END,
399
+ "updated_at" = $7
400
+ WHERE "session_id" = $1
401
+ AND "upload_id" = $2`, sessionId, item.uploadId, item.status, item.progressPercent, item.errorMessage, item.uploadedKey, now);
402
+ }
403
+ const summaryRows = await this.prismaService.$queryRawUnsafe(`SELECT
404
+ COUNT(*)::int AS total,
405
+ COUNT(*) FILTER (WHERE "status" = 'done')::int AS done_count,
406
+ COUNT(*) FILTER (WHERE "status" = 'error')::int AS error_count,
407
+ COUNT(*) FILTER (WHERE "status" = 'cancelled')::int AS cancelled_count,
408
+ COUNT(*) FILTER (WHERE "status" = 'uploading')::int AS uploading_count,
409
+ COUNT(*) FILTER (WHERE "status" = 'queued')::int AS queued_count,
410
+ COALESCE(AVG("progress_percent"), 0)::float AS average_progress
411
+ FROM "lms_bulk_upload_item"
412
+ WHERE "session_id" = $1`, sessionId);
413
+ const summary = (_d = summaryRows === null || summaryRows === void 0 ? void 0 : summaryRows[0]) !== null && _d !== void 0 ? _d : {
414
+ total: 0,
415
+ done_count: 0,
416
+ error_count: 0,
417
+ cancelled_count: 0,
418
+ uploading_count: 0,
419
+ queued_count: 0,
420
+ average_progress: 0,
421
+ };
422
+ const requestedStatus = this.normalizeSessionStatus(payload === null || payload === void 0 ? void 0 : payload.status);
423
+ const computedStatus = this.computeSessionStatus(summary);
424
+ const finalStatus = requestedStatus !== null && requestedStatus !== void 0 ? requestedStatus : computedStatus;
425
+ const progressPercent = this.clampPercent((_f = (_e = payload === null || payload === void 0 ? void 0 : payload.progressPercent) !== null && _e !== void 0 ? _e : summary.average_progress) !== null && _f !== void 0 ? _f : 0);
426
+ const isTerminal = ['completed', 'failed', 'cancelled'].includes(finalStatus);
427
+ await this.prismaService.$executeRawUnsafe(`UPDATE "lms_bulk_upload_session"
428
+ SET
429
+ "status" = $2,
430
+ "progress_percent" = $3,
431
+ "heartbeat_at" = $4,
432
+ "finished_at" = CASE WHEN $5 THEN COALESCE("finished_at", $4) ELSE "finished_at" END,
433
+ "updated_at" = $4
434
+ WHERE "id" = $1`, sessionId, finalStatus, progressPercent, now, isTerminal);
435
+ return {
436
+ sessionId,
437
+ status: finalStatus,
438
+ progressPercent,
439
+ totals: {
440
+ total: Number(summary.total || 0),
441
+ done: Number(summary.done_count || 0),
442
+ error: Number(summary.error_count || 0),
443
+ cancelled: Number(summary.cancelled_count || 0),
444
+ },
445
+ };
446
+ }
447
+ async listUploadItems(params) {
448
+ var _a, _b, _c;
449
+ const page = Math.max(Number(params.page) || 1, 1);
450
+ const pageSize = Math.min(Math.max(Number(params.pageSize) || 20, 1), 100);
451
+ const offset = (page - 1) * pageSize;
452
+ const search = String((_a = params.search) !== null && _a !== void 0 ? _a : '').trim();
453
+ const status = this.normalizeItemStatus(params.status, true);
454
+ const where = [];
455
+ const args = [];
456
+ if (status) {
457
+ args.push(status);
458
+ where.push(`i."status" = $${args.length}`);
459
+ }
460
+ if (search) {
461
+ args.push(`%${search}%`);
462
+ const searchParam = `$${args.length}`;
463
+ where.push(`(i."file_name" ILIKE ${searchParam} OR s."app_name" ILIKE ${searchParam} OR COALESCE(sender."name", '') ILIKE ${searchParam} OR CAST(s."user_id" AS TEXT) ILIKE ${searchParam} OR COALESCE(mc."title", '') ILIKE ${searchParam} OR COALESCE(ml."title", '') ILIKE ${searchParam})`);
464
+ }
465
+ const whereSql = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
466
+ const countRows = await this.prismaService.$queryRawUnsafe(`SELECT COUNT(*)::int AS total
467
+ FROM "lms_bulk_upload_item" i
468
+ JOIN "lms_bulk_upload_session" s ON s."id" = i."session_id"
469
+ LEFT JOIN LATERAL (
470
+ SELECT p."name"
471
+ FROM "person_user" pu
472
+ JOIN "person" p ON p."id" = pu."person_id"
473
+ WHERE pu."user_id" = s."user_id"
474
+ ORDER BY pu."id" DESC
475
+ LIMIT 1
476
+ ) sender ON TRUE
477
+ LEFT JOIN "course" mc ON mc."id" = i."matched_course_id"
478
+ LEFT JOIN "course_module" ms ON ms."id" = i."matched_session_id"
479
+ LEFT JOIN "course_lesson" ml ON ml."id" = i."matched_lesson_id"
480
+ ${whereSql}`, ...args);
481
+ const total = Number((_c = (_b = countRows === null || countRows === void 0 ? void 0 : countRows[0]) === null || _b === void 0 ? void 0 : _b.total) !== null && _c !== void 0 ? _c : 0);
482
+ const dataRows = await this.prismaService.$queryRawUnsafe(`SELECT
483
+ i."id",
484
+ i."session_id",
485
+ i."upload_id",
486
+ i."file_name",
487
+ i."size_bytes",
488
+ i."status",
489
+ i."progress_percent",
490
+ i."error_message",
491
+ i."uploaded_key",
492
+ i."received_at",
493
+ i."updated_at",
494
+ i."completed_at",
495
+ s."app_name",
496
+ s."user_id",
497
+ u."photo_id" AS user_photo_id,
498
+ sender."name" AS user_name,
499
+ s."status" AS session_status,
500
+ s."started_at" AS session_started_at,
501
+ mc."id" AS matched_course_id,
502
+ mc."title" AS matched_course_title,
503
+ mc."slug" AS matched_course_slug,
504
+ logo."file_id" AS matched_course_logo_file_id,
505
+ ms."id" AS matched_session_id,
506
+ ms."title" AS matched_session_title,
507
+ ml."id" AS matched_lesson_id,
508
+ ml."title" AS matched_lesson_title
509
+ FROM "lms_bulk_upload_item" i
510
+ JOIN "lms_bulk_upload_session" s ON s."id" = i."session_id"
511
+ LEFT JOIN "user" u ON u."id" = s."user_id"
512
+ LEFT JOIN LATERAL (
513
+ SELECT p."name"
514
+ FROM "person_user" pu
515
+ JOIN "person" p ON p."id" = pu."person_id"
516
+ WHERE pu."user_id" = s."user_id"
517
+ ORDER BY pu."id" DESC
518
+ LIMIT 1
519
+ ) sender ON TRUE
520
+ LEFT JOIN "course" mc ON mc."id" = i."matched_course_id"
521
+ LEFT JOIN "course_module" ms ON ms."id" = i."matched_session_id"
522
+ LEFT JOIN "course_lesson" ml ON ml."id" = i."matched_lesson_id"
523
+ LEFT JOIN LATERAL (
524
+ SELECT ci."file_id"
525
+ FROM "course_image" ci
526
+ INNER JOIN "image_type" it ON it."id" = ci."image_type_id"
527
+ WHERE ci."course_id" = mc."id"
528
+ AND it."slug" = 'course-logo'
529
+ ORDER BY ci."id" DESC
530
+ LIMIT 1
531
+ ) logo ON TRUE
532
+ ${whereSql}
533
+ ORDER BY i."updated_at" DESC
534
+ LIMIT $${args.length + 1}
535
+ OFFSET $${args.length + 2}`, ...args, pageSize, offset);
536
+ return {
537
+ data: dataRows.map((row) => ({
538
+ id: row.id,
539
+ sessionId: row.session_id,
540
+ uploadId: row.upload_id,
541
+ fileName: row.file_name,
542
+ sizeBytes: Number(row.size_bytes || 0),
543
+ status: row.status,
544
+ progressPercent: Number(row.progress_percent || 0),
545
+ errorMessage: row.error_message,
546
+ uploadedKey: row.uploaded_key,
547
+ receivedAt: row.received_at,
548
+ updatedAt: row.updated_at,
549
+ completedAt: row.completed_at,
550
+ appName: row.app_name,
551
+ userId: row.user_id,
552
+ userPhotoId: row.user_photo_id ? Number(row.user_photo_id) : null,
553
+ userName: row.user_name,
554
+ sessionStatus: row.session_status,
555
+ startedAt: row.session_started_at,
556
+ matchedCourseId: row.matched_course_id ? Number(row.matched_course_id) : null,
557
+ matchedCourseTitle: row.matched_course_title,
558
+ matchedCourseSlug: row.matched_course_slug,
559
+ matchedCourseLogoFileId: row.matched_course_logo_file_id ? Number(row.matched_course_logo_file_id) : null,
560
+ matchedSessionId: row.matched_session_id ? Number(row.matched_session_id) : null,
561
+ matchedSessionTitle: row.matched_session_title,
562
+ matchedLessonId: row.matched_lesson_id ? Number(row.matched_lesson_id) : null,
563
+ matchedLessonTitle: row.matched_lesson_title,
564
+ })),
565
+ total,
566
+ page,
567
+ pageSize,
568
+ lastPage: Math.max(1, Math.ceil(total / pageSize)),
569
+ };
570
+ }
571
+ async cleanupUploadHistory(payload) {
572
+ var _a, _b;
573
+ const statusSet = new Set();
574
+ for (const rawStatus of (_a = payload === null || payload === void 0 ? void 0 : payload.statuses) !== null && _a !== void 0 ? _a : []) {
575
+ const normalized = String(rawStatus !== null && rawStatus !== void 0 ? rawStatus : '').trim().toLowerCase();
576
+ if (cleanup_upload_history_dto_1.BULK_UPLOAD_CLEANUP_STATUSES.includes(normalized)) {
577
+ statusSet.add(normalized);
578
+ }
579
+ }
580
+ if (statusSet.size === 0) {
581
+ statusSet.add('done');
582
+ }
583
+ const statuses = Array.from(statusSet);
584
+ const allowedWindows = new Set(cleanup_upload_history_dto_1.BULK_UPLOAD_CLEANUP_WINDOWS);
585
+ const rawWindow = String((_b = payload === null || payload === void 0 ? void 0 : payload.timeWindow) !== null && _b !== void 0 ? _b : '').trim().toLowerCase();
586
+ const timeWindow = allowedWindows.has(rawWindow) ? rawWindow : 'last_day';
587
+ let cutoffDate = null;
588
+ if (timeWindow === 'last_hour') {
589
+ cutoffDate = new Date(Date.now() - 60 * 60 * 1000);
590
+ }
591
+ else if (timeWindow === 'last_day') {
592
+ cutoffDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
593
+ }
594
+ else if (timeWindow === 'last_week') {
595
+ cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
596
+ }
597
+ const statusPlaceholders = statuses.map((_, index) => `$${index + 1}`).join(', ');
598
+ const args = [...statuses];
599
+ let timeFilterSql = '';
600
+ if (cutoffDate) {
601
+ args.push(cutoffDate);
602
+ timeFilterSql = ` AND i."updated_at" >= $${args.length}`;
603
+ }
604
+ const deletedRows = await this.prismaService.$queryRawUnsafe(`DELETE FROM "lms_bulk_upload_item" i
605
+ USING "lms_bulk_upload_session" s
606
+ WHERE i."session_id" = s."id"
607
+ AND i."status" IN (${statusPlaceholders})
608
+ ${timeFilterSql}
609
+ RETURNING i."session_id"`, ...args);
610
+ const deletedItems = deletedRows.length;
611
+ const sessionIds = Array.from(new Set(deletedRows
612
+ .map((row) => Number(row.session_id || 0))
613
+ .filter((sessionId) => Number.isFinite(sessionId) && sessionId > 0)));
614
+ let deletedSessions = 0;
615
+ if (sessionIds.length > 0) {
616
+ const sessionPlaceholders = sessionIds
617
+ .map((_, index) => `$${index + 1}`)
618
+ .join(', ');
619
+ const deletedSessionRows = await this.prismaService.$queryRawUnsafe(`DELETE FROM "lms_bulk_upload_session" s
620
+ WHERE s."id" IN (${sessionPlaceholders})
621
+ AND NOT EXISTS (
622
+ SELECT 1
623
+ FROM "lms_bulk_upload_item" i
624
+ WHERE i."session_id" = s."id"
625
+ )
626
+ RETURNING s."id"`, ...sessionIds);
627
+ deletedSessions = deletedSessionRows.length;
628
+ }
629
+ return {
630
+ success: true,
631
+ deletedItems,
632
+ deletedSessions,
633
+ filtersApplied: {
634
+ statuses,
635
+ timeWindow,
636
+ },
637
+ };
638
+ }
639
+ clampPercent(value) {
640
+ const parsed = Number(value);
641
+ if (!Number.isFinite(parsed))
642
+ return 0;
643
+ return Math.max(0, Math.min(100, Math.round(parsed)));
644
+ }
645
+ normalizeSessionStatus(value) {
646
+ const normalized = String(value !== null && value !== void 0 ? value : '').trim().toLowerCase();
647
+ if (!normalized)
648
+ return null;
649
+ if (normalized === 'started' ||
650
+ normalized === 'uploading' ||
651
+ normalized === 'paused' ||
652
+ normalized === 'cancelling' ||
653
+ normalized === 'completed' ||
654
+ normalized === 'failed' ||
655
+ normalized === 'cancelled') {
656
+ return normalized;
657
+ }
658
+ return null;
659
+ }
660
+ normalizeItemStatus(value, allowEmpty = false) {
661
+ const normalized = String(value !== null && value !== void 0 ? value : '').trim().toLowerCase();
662
+ if (!normalized && allowEmpty)
663
+ return null;
664
+ if (normalized === 'queued' ||
665
+ normalized === 'uploading' ||
666
+ normalized === 'cancelling' ||
667
+ normalized === 'received' ||
668
+ normalized === 'done' ||
669
+ normalized === 'error' ||
670
+ normalized === 'cancelled') {
671
+ return normalized;
672
+ }
673
+ return allowEmpty ? null : 'queued';
674
+ }
675
+ computeSessionStatus(summary) {
676
+ const total = Number(summary.total || 0);
677
+ const done = Number(summary.done_count || 0);
678
+ const error = Number(summary.error_count || 0);
679
+ const cancelled = Number(summary.cancelled_count || 0);
680
+ const uploading = Number(summary.uploading_count || 0);
681
+ const queued = Number(summary.queued_count || 0);
682
+ if (total === 0)
683
+ return 'started';
684
+ if (done === total)
685
+ return 'completed';
686
+ if (done + error + cancelled === total && error > 0)
687
+ return 'failed';
688
+ if (done + error + cancelled === total && cancelled > 0 && error === 0)
689
+ return 'cancelled';
690
+ if (uploading > 0)
691
+ return 'uploading';
692
+ if (queued === total)
693
+ return 'started';
694
+ return 'uploading';
695
+ }
696
+ async resolveStorageProfileAndBucket(userId, overrides) {
697
+ var _a, _b, _c, _d, _e, _f;
698
+ const settings = await this.settingService.getSettingValues([
699
+ 'lms-bulk-upload-storage-profile-id',
700
+ 'lms-bulk-upload-sts-duration-seconds',
701
+ ]);
702
+ const profileId = Number((_b = (_a = overrides === null || overrides === void 0 ? void 0 : overrides.storageProfileId) !== null && _a !== void 0 ? _a : settings['lms-bulk-upload-storage-profile-id']) !== null && _b !== void 0 ? _b : 0);
703
+ if (!Number.isFinite(profileId) || profileId <= 0) {
704
+ throw new common_1.BadRequestException('LMS bulk upload storage profile is not configured (lms-bulk-upload-storage-profile-id).');
705
+ }
706
+ const profile = await this.prismaService.integration_profile.findUnique({
707
+ where: { id: profileId },
708
+ include: {
709
+ integration_type: { select: { slug: true } },
710
+ integration_provider: { select: { slug: true } },
711
+ },
712
+ });
713
+ if (!profile) {
714
+ throw new common_1.NotFoundException(`Storage integration profile ${profileId} was not found.`);
715
+ }
716
+ if (profile.integration_type.slug !== 'storage') {
717
+ throw new common_1.BadRequestException(`Integration profile ${profileId} is not a storage profile.`);
718
+ }
719
+ const providerSlug = String((_c = profile.integration_provider.slug) !== null && _c !== void 0 ? _c : '').toLowerCase();
720
+ if (providerSlug !== 's3') {
721
+ throw new common_1.BadRequestException(`Integration provider ${providerSlug} is not supported for desktop bulk upload. Use AWS S3 profile.`);
722
+ }
723
+ const config = (_d = profile.config) !== null && _d !== void 0 ? _d : {};
724
+ const region = String((_e = config.region) !== null && _e !== void 0 ? _e : '').trim() || 'us-east-1';
725
+ const bucket = String((_f = config.bucket) !== null && _f !== void 0 ? _f : '').trim();
726
+ if (!bucket) {
727
+ throw new common_1.BadRequestException(`Storage integration profile ${profileId} does not define a bucket in config.bucket.`);
728
+ }
729
+ const durationSeconds = Math.max(900, Math.min(43200, Number(settings['lms-bulk-upload-sts-duration-seconds'] || 3600) || 3600));
730
+ const prefix = `desktop/lms/u${userId}/`;
731
+ return {
732
+ profile,
733
+ region,
734
+ bucket,
735
+ durationSeconds,
736
+ prefix,
737
+ };
738
+ }
739
+ };
740
+ exports.LmsBulkUploadService = LmsBulkUploadService;
741
+ exports.LmsBulkUploadService = LmsBulkUploadService = __decorate([
742
+ (0, common_1.Injectable)(),
743
+ __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.SettingService))),
744
+ __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_prisma_1.PrismaService))),
745
+ __param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.FileService))),
746
+ __param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.WebhookIntegrationService))),
747
+ __param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => lms_bulk_upload_infra_service_1.LmsBulkUploadInfraService))),
748
+ __metadata("design:paramtypes", [core_1.SettingService,
749
+ api_prisma_1.PrismaService,
750
+ core_1.FileService,
751
+ core_1.WebhookIntegrationService,
752
+ lms_bulk_upload_infra_service_1.LmsBulkUploadInfraService])
753
+ ], LmsBulkUploadService);
754
+ //# sourceMappingURL=lms-bulk-upload.service.js.map