@studious-lms/server 1.2.53 → 1.3.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 (477) hide show
  1. package/.coderabbit.yaml +9 -0
  2. package/.env.example +9 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +102 -8
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/config/env.d.ts +21 -0
  7. package/dist/lib/config/env.d.ts.map +1 -1
  8. package/dist/lib/config/env.js +8 -2
  9. package/dist/lib/config/env.js.map +1 -1
  10. package/dist/lib/fileUpload.d.ts.map +1 -1
  11. package/dist/lib/fileUpload.js +2 -2
  12. package/dist/lib/fileUpload.js.map +1 -1
  13. package/dist/lib/googleCloudStorage.d.ts +6 -0
  14. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  15. package/dist/lib/googleCloudStorage.js +19 -2
  16. package/dist/lib/googleCloudStorage.js.map +1 -1
  17. package/dist/lib/pusher.d.ts +4 -1
  18. package/dist/lib/pusher.d.ts.map +1 -1
  19. package/dist/lib/pusher.js +6 -3
  20. package/dist/lib/pusher.js.map +1 -1
  21. package/dist/lib/redis.d.ts +5 -0
  22. package/dist/lib/redis.d.ts.map +1 -0
  23. package/dist/lib/redis.js +53 -0
  24. package/dist/lib/redis.js.map +1 -0
  25. package/dist/lib/thumbnailGenerator.d.ts +0 -21
  26. package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
  27. package/dist/lib/thumbnailGenerator.js +157 -160
  28. package/dist/lib/thumbnailGenerator.js.map +1 -1
  29. package/dist/middleware/auth.d.ts.map +1 -1
  30. package/dist/middleware/auth.js +33 -95
  31. package/dist/middleware/auth.js.map +1 -1
  32. package/dist/models/agenda.d.ts +97 -0
  33. package/dist/models/agenda.d.ts.map +1 -0
  34. package/dist/models/agenda.js +40 -0
  35. package/dist/models/agenda.js.map +1 -0
  36. package/dist/models/announcement.d.ts +223 -0
  37. package/dist/models/announcement.d.ts.map +1 -0
  38. package/dist/models/announcement.js +120 -0
  39. package/dist/models/announcement.js.map +1 -0
  40. package/dist/models/assignment.d.ts +1292 -0
  41. package/dist/models/assignment.d.ts.map +1 -0
  42. package/dist/models/assignment.js +309 -0
  43. package/dist/models/assignment.js.map +1 -0
  44. package/dist/models/attendance.d.ts +180 -0
  45. package/dist/models/attendance.d.ts.map +1 -0
  46. package/dist/models/attendance.js +188 -0
  47. package/dist/models/attendance.js.map +1 -0
  48. package/dist/models/auth.d.ts +153 -0
  49. package/dist/models/auth.d.ts.map +1 -0
  50. package/dist/models/auth.js +217 -0
  51. package/dist/models/auth.js.map +1 -0
  52. package/dist/models/class.d.ts +439 -0
  53. package/dist/models/class.d.ts.map +1 -0
  54. package/dist/models/class.js +546 -0
  55. package/dist/models/class.js.map +1 -0
  56. package/dist/models/comment.d.ts +171 -0
  57. package/dist/models/comment.d.ts.map +1 -0
  58. package/dist/models/comment.js +138 -0
  59. package/dist/models/comment.js.map +1 -0
  60. package/dist/models/conversation.d.ts +164 -0
  61. package/dist/models/conversation.d.ts.map +1 -0
  62. package/dist/models/conversation.js +175 -0
  63. package/dist/models/conversation.js.map +1 -0
  64. package/dist/models/event.d.ts +295 -0
  65. package/dist/models/event.d.ts.map +1 -0
  66. package/dist/models/event.js +145 -0
  67. package/dist/models/event.js.map +1 -0
  68. package/dist/models/file.d.ts +536 -0
  69. package/dist/models/file.d.ts.map +1 -0
  70. package/dist/models/file.js +126 -0
  71. package/dist/models/file.js.map +1 -0
  72. package/dist/models/folder.d.ts +295 -0
  73. package/dist/models/folder.d.ts.map +1 -0
  74. package/dist/models/folder.js +202 -0
  75. package/dist/models/folder.js.map +1 -0
  76. package/dist/models/labChat.d.ts +243 -0
  77. package/dist/models/labChat.d.ts.map +1 -0
  78. package/dist/models/labChat.js +204 -0
  79. package/dist/models/labChat.js.map +1 -0
  80. package/dist/models/marketing.d.ts +72 -0
  81. package/dist/models/marketing.d.ts.map +1 -0
  82. package/dist/models/marketing.js +26 -0
  83. package/dist/models/marketing.js.map +1 -0
  84. package/dist/models/message.d.ts +100 -0
  85. package/dist/models/message.d.ts.map +1 -0
  86. package/dist/models/message.js +131 -0
  87. package/dist/models/message.js.map +1 -0
  88. package/dist/models/newtonChat.d.ts +72 -0
  89. package/dist/models/newtonChat.d.ts.map +1 -0
  90. package/dist/models/newtonChat.js +61 -0
  91. package/dist/models/newtonChat.js.map +1 -0
  92. package/dist/models/notification.d.ts +65 -0
  93. package/dist/models/notification.d.ts.map +1 -0
  94. package/dist/models/notification.js +46 -0
  95. package/dist/models/notification.js.map +1 -0
  96. package/dist/models/section.d.ts +102 -0
  97. package/dist/models/section.d.ts.map +1 -0
  98. package/dist/models/section.js +83 -0
  99. package/dist/models/section.js.map +1 -0
  100. package/dist/models/user.d.ts +39 -0
  101. package/dist/models/user.d.ts.map +1 -0
  102. package/dist/models/user.js +38 -0
  103. package/dist/models/user.js.map +1 -0
  104. package/dist/models/worksheet.d.ts +460 -0
  105. package/dist/models/worksheet.d.ts.map +1 -0
  106. package/dist/models/worksheet.js +200 -0
  107. package/dist/models/worksheet.js.map +1 -0
  108. package/dist/pipelines/aiLabChat.d.ts +21 -0
  109. package/dist/pipelines/aiLabChat.d.ts.map +1 -0
  110. package/dist/pipelines/aiLabChat.js +460 -0
  111. package/dist/pipelines/aiLabChat.js.map +1 -0
  112. package/dist/pipelines/aiNewtonChat.d.ts +30 -0
  113. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -0
  114. package/dist/pipelines/aiNewtonChat.js +289 -0
  115. package/dist/pipelines/aiNewtonChat.js.map +1 -0
  116. package/dist/pipelines/gradeWorksheet.d.ts +30 -0
  117. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -0
  118. package/dist/pipelines/gradeWorksheet.js +252 -0
  119. package/dist/pipelines/gradeWorksheet.js.map +1 -0
  120. package/dist/routers/_app.d.ts +1393 -1267
  121. package/dist/routers/_app.d.ts.map +1 -1
  122. package/dist/routers/agenda.d.ts +22 -22
  123. package/dist/routers/agenda.d.ts.map +1 -1
  124. package/dist/routers/agenda.js +4 -65
  125. package/dist/routers/agenda.js.map +1 -1
  126. package/dist/routers/announcement.d.ts +16 -16
  127. package/dist/routers/announcement.d.ts.map +1 -1
  128. package/dist/routers/announcement.js +37 -446
  129. package/dist/routers/announcement.js.map +1 -1
  130. package/dist/routers/assignment.d.ts +300 -378
  131. package/dist/routers/assignment.d.ts.map +1 -1
  132. package/dist/routers/assignment.js +78 -1868
  133. package/dist/routers/assignment.js.map +1 -1
  134. package/dist/routers/attendance.d.ts +19 -9
  135. package/dist/routers/attendance.d.ts.map +1 -1
  136. package/dist/routers/attendance.js +7 -264
  137. package/dist/routers/attendance.js.map +1 -1
  138. package/dist/routers/auth.d.ts +2 -2
  139. package/dist/routers/auth.d.ts.map +1 -1
  140. package/dist/routers/auth.js +29 -354
  141. package/dist/routers/auth.js.map +1 -1
  142. package/dist/routers/class.d.ts +139 -68
  143. package/dist/routers/class.d.ts.map +1 -1
  144. package/dist/routers/class.js +82 -1052
  145. package/dist/routers/class.js.map +1 -1
  146. package/dist/routers/comment.d.ts +6 -42
  147. package/dist/routers/comment.d.ts.map +1 -1
  148. package/dist/routers/comment.js +24 -244
  149. package/dist/routers/comment.js.map +1 -1
  150. package/dist/routers/conversation.d.ts +45 -7
  151. package/dist/routers/conversation.d.ts.map +1 -1
  152. package/dist/routers/conversation.js +19 -327
  153. package/dist/routers/conversation.js.map +1 -1
  154. package/dist/routers/event.d.ts +36 -36
  155. package/dist/routers/event.d.ts.map +1 -1
  156. package/dist/routers/event.js +13 -433
  157. package/dist/routers/event.js.map +1 -1
  158. package/dist/routers/file.d.ts +2 -2
  159. package/dist/routers/file.d.ts.map +1 -1
  160. package/dist/routers/file.js +9 -323
  161. package/dist/routers/file.js.map +1 -1
  162. package/dist/routers/folder.d.ts +21 -14
  163. package/dist/routers/folder.d.ts.map +1 -1
  164. package/dist/routers/folder.js +34 -745
  165. package/dist/routers/folder.js.map +1 -1
  166. package/dist/routers/labChat.d.ts +11 -10
  167. package/dist/routers/labChat.d.ts.map +1 -1
  168. package/dist/routers/labChat.js +19 -570
  169. package/dist/routers/labChat.js.map +1 -1
  170. package/dist/routers/marketing.d.ts +1 -1
  171. package/dist/routers/marketing.d.ts.map +1 -1
  172. package/dist/routers/marketing.js +7 -56
  173. package/dist/routers/marketing.js.map +1 -1
  174. package/dist/routers/message.d.ts +2 -2
  175. package/dist/routers/message.d.ts.map +1 -1
  176. package/dist/routers/message.js +27 -522
  177. package/dist/routers/message.js.map +1 -1
  178. package/dist/routers/newtonChat.d.ts +1 -1
  179. package/dist/routers/newtonChat.d.ts.map +1 -1
  180. package/dist/routers/newtonChat.js +7 -246
  181. package/dist/routers/newtonChat.js.map +1 -1
  182. package/dist/routers/notifications.d.ts +4 -4
  183. package/dist/routers/notifications.d.ts.map +1 -1
  184. package/dist/routers/notifications.js +18 -83
  185. package/dist/routers/notifications.js.map +1 -1
  186. package/dist/routers/section.d.ts +4 -4
  187. package/dist/routers/section.d.ts.map +1 -1
  188. package/dist/routers/section.js +14 -286
  189. package/dist/routers/section.js.map +1 -1
  190. package/dist/routers/user.d.ts +1 -1
  191. package/dist/routers/user.d.ts.map +1 -1
  192. package/dist/routers/user.js +32 -207
  193. package/dist/routers/user.js.map +1 -1
  194. package/dist/routers/worksheet.d.ts +51 -38
  195. package/dist/routers/worksheet.d.ts.map +1 -1
  196. package/dist/routers/worksheet.js +79 -394
  197. package/dist/routers/worksheet.js.map +1 -1
  198. package/dist/seedDatabase.d.ts +1 -1
  199. package/dist/server/pipelines/gradeWorksheet.d.ts +6 -6
  200. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
  201. package/dist/server/pipelines/gradeWorksheet.js +12 -5
  202. package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
  203. package/dist/services/agenda.d.ts +100 -0
  204. package/dist/services/agenda.d.ts.map +1 -0
  205. package/dist/services/agenda.js +21 -0
  206. package/dist/services/agenda.js.map +1 -0
  207. package/dist/services/announcement.d.ts +135 -0
  208. package/dist/services/announcement.d.ts.map +1 -0
  209. package/dist/services/announcement.js +223 -0
  210. package/dist/services/announcement.js.map +1 -0
  211. package/dist/services/assignment.d.ts +1462 -0
  212. package/dist/services/assignment.d.ts.map +1 -0
  213. package/dist/services/assignment.js +898 -0
  214. package/dist/services/assignment.js.map +1 -0
  215. package/dist/services/attendance.d.ts +93 -0
  216. package/dist/services/attendance.d.ts.map +1 -0
  217. package/dist/services/attendance.js +61 -0
  218. package/dist/services/attendance.js.map +1 -0
  219. package/dist/services/auth.d.ts +68 -0
  220. package/dist/services/auth.d.ts.map +1 -0
  221. package/dist/services/auth.js +218 -0
  222. package/dist/services/auth.js.map +1 -0
  223. package/dist/services/class.d.ts +621 -0
  224. package/dist/services/class.d.ts.map +1 -0
  225. package/dist/services/class.js +474 -0
  226. package/dist/services/class.js.map +1 -0
  227. package/dist/services/comment.d.ts +100 -0
  228. package/dist/services/comment.d.ts.map +1 -0
  229. package/dist/services/comment.js +83 -0
  230. package/dist/services/comment.js.map +1 -0
  231. package/dist/services/conversation.d.ts +159 -0
  232. package/dist/services/conversation.d.ts.map +1 -0
  233. package/dist/services/conversation.js +138 -0
  234. package/dist/services/conversation.js.map +1 -0
  235. package/dist/services/event.d.ts +216 -0
  236. package/dist/services/event.d.ts.map +1 -0
  237. package/dist/services/event.js +168 -0
  238. package/dist/services/event.js.map +1 -0
  239. package/dist/services/file.d.ts +74 -0
  240. package/dist/services/file.d.ts.map +1 -0
  241. package/dist/services/file.js +133 -0
  242. package/dist/services/file.js.map +1 -0
  243. package/dist/services/folder.d.ts +239 -0
  244. package/dist/services/folder.d.ts.map +1 -0
  245. package/dist/services/folder.js +248 -0
  246. package/dist/services/folder.js.map +1 -0
  247. package/dist/services/labChat.d.ts +165 -0
  248. package/dist/services/labChat.d.ts.map +1 -0
  249. package/dist/services/labChat.js +289 -0
  250. package/dist/services/labChat.js.map +1 -0
  251. package/dist/services/marketing.d.ts +50 -0
  252. package/dist/services/marketing.d.ts.map +1 -0
  253. package/dist/services/marketing.js +32 -0
  254. package/dist/services/marketing.js.map +1 -0
  255. package/dist/services/message.d.ts +95 -0
  256. package/dist/services/message.d.ts.map +1 -0
  257. package/dist/services/message.js +350 -0
  258. package/dist/services/message.js.map +1 -0
  259. package/dist/services/newtonChat.d.ts +22 -0
  260. package/dist/services/newtonChat.d.ts.map +1 -0
  261. package/dist/services/newtonChat.js +174 -0
  262. package/dist/services/newtonChat.js.map +1 -0
  263. package/dist/services/notification.d.ts +65 -0
  264. package/dist/services/notification.d.ts.map +1 -0
  265. package/dist/services/notification.js +33 -0
  266. package/dist/services/notification.js.map +1 -0
  267. package/dist/services/section.d.ts +53 -0
  268. package/dist/services/section.d.ts.map +1 -0
  269. package/dist/services/section.js +199 -0
  270. package/dist/services/section.js.map +1 -0
  271. package/dist/services/user.d.ts +48 -0
  272. package/dist/services/user.d.ts.map +1 -0
  273. package/dist/services/user.js +141 -0
  274. package/dist/services/user.js.map +1 -0
  275. package/dist/services/worksheet.d.ts +239 -0
  276. package/dist/services/worksheet.d.ts.map +1 -0
  277. package/dist/services/worksheet.js +235 -0
  278. package/dist/services/worksheet.js.map +1 -0
  279. package/dist/utils/aiUser.d.ts +1 -3
  280. package/dist/utils/aiUser.d.ts.map +1 -1
  281. package/dist/utils/aiUser.js +6 -5
  282. package/dist/utils/aiUser.js.map +1 -1
  283. package/dist/utils/email.d.ts +3 -0
  284. package/dist/utils/email.d.ts.map +1 -1
  285. package/dist/utils/email.js +7 -4
  286. package/dist/utils/email.js.map +1 -1
  287. package/dist/utils/generateInviteCode.d.ts +1 -2
  288. package/dist/utils/generateInviteCode.d.ts.map +1 -1
  289. package/dist/utils/generateInviteCode.js +3 -4
  290. package/dist/utils/generateInviteCode.js.map +1 -1
  291. package/dist/utils/inference.d.ts +3 -0
  292. package/dist/utils/inference.d.ts.map +1 -1
  293. package/dist/utils/inference.js +7 -4
  294. package/dist/utils/inference.js.map +1 -1
  295. package/dist/utils/logger.d.ts +3 -0
  296. package/dist/utils/logger.d.ts.map +1 -1
  297. package/dist/utils/logger.js +5 -2
  298. package/dist/utils/logger.js.map +1 -1
  299. package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
  300. package/dist/utils/prismaErrorHandler.js +5 -2
  301. package/dist/utils/prismaErrorHandler.js.map +1 -1
  302. package/dist/utils/prismaWrapper.d.ts +1 -0
  303. package/dist/utils/prismaWrapper.d.ts.map +1 -1
  304. package/dist/utils/prismaWrapper.js +6 -2
  305. package/dist/utils/prismaWrapper.js.map +1 -1
  306. package/docker-compose.yml +5 -0
  307. package/package.json +4 -3
  308. package/src/index.ts +119 -12
  309. package/src/lib/config/env.ts +6 -0
  310. package/src/lib/fileUpload.ts +0 -1
  311. package/src/lib/googleCloudStorage.ts +17 -0
  312. package/src/lib/pusher.ts +5 -1
  313. package/src/lib/redis.ts +56 -0
  314. package/src/lib/thumbnailGenerator.ts +170 -168
  315. package/src/middleware/auth.ts +80 -137
  316. package/src/models/agenda.ts +46 -0
  317. package/src/models/announcement.ts +134 -0
  318. package/src/models/assignment.ts +322 -0
  319. package/src/models/attendance.ts +208 -0
  320. package/src/models/auth.ts +247 -0
  321. package/src/models/class.ts +598 -0
  322. package/src/models/comment.ts +152 -0
  323. package/src/models/conversation.ts +200 -0
  324. package/src/models/event.ts +177 -0
  325. package/src/models/file.ts +129 -0
  326. package/src/models/folder.ts +225 -0
  327. package/src/models/labChat.ts +213 -0
  328. package/src/models/marketing.ts +45 -0
  329. package/src/models/message.ts +153 -0
  330. package/src/models/newtonChat.ts +70 -0
  331. package/src/models/notification.ts +54 -0
  332. package/src/models/section.ts +98 -0
  333. package/src/models/user.ts +47 -0
  334. package/src/models/worksheet.ts +294 -0
  335. package/src/{server/pipelines → pipelines}/aiLabChat.ts +11 -7
  336. package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +9 -5
  337. package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
  338. package/src/routers/agenda.ts +3 -66
  339. package/src/routers/announcement.ts +54 -495
  340. package/src/routers/assignment.ts +126 -2018
  341. package/src/routers/attendance.ts +15 -276
  342. package/src/routers/auth.ts +79 -442
  343. package/src/routers/class.ts +263 -1187
  344. package/src/routers/comment.ts +61 -288
  345. package/src/routers/conversation.ts +51 -360
  346. package/src/routers/event.ts +50 -481
  347. package/src/routers/file.ts +45 -368
  348. package/src/routers/folder.ts +107 -836
  349. package/src/routers/labChat.ts +29 -605
  350. package/src/routers/marketing.ts +35 -77
  351. package/src/routers/message.ts +45 -571
  352. package/src/routers/newtonChat.ts +17 -278
  353. package/src/routers/notifications.ts +32 -82
  354. package/src/routers/section.ts +46 -330
  355. package/src/routers/user.ts +49 -227
  356. package/src/routers/worksheet.ts +215 -503
  357. package/src/services/agenda.ts +21 -0
  358. package/src/services/announcement.ts +290 -0
  359. package/src/services/assignment.ts +1198 -0
  360. package/src/services/attendance.ts +85 -0
  361. package/src/services/auth.ts +277 -0
  362. package/src/services/class.ts +622 -0
  363. package/src/services/comment.ts +106 -0
  364. package/src/services/conversation.ts +213 -0
  365. package/src/services/event.ts +231 -0
  366. package/src/services/file.ts +167 -0
  367. package/src/services/folder.ts +316 -0
  368. package/src/services/labChat.ts +352 -0
  369. package/src/services/marketing.ts +57 -0
  370. package/src/services/message.ts +461 -0
  371. package/src/services/newtonChat.ts +222 -0
  372. package/src/services/notification.ts +50 -0
  373. package/src/services/section.ts +283 -0
  374. package/src/services/user.ts +172 -0
  375. package/src/services/worksheet.ts +358 -0
  376. package/src/utils/aiUser.ts +4 -3
  377. package/src/utils/email.ts +5 -3
  378. package/src/utils/generateInviteCode.ts +1 -3
  379. package/src/utils/inference.ts +5 -2
  380. package/src/utils/logger.ts +3 -1
  381. package/src/utils/prismaErrorHandler.ts +3 -0
  382. package/src/utils/prismaWrapper.ts +4 -0
  383. package/tests/globalSetup.ts +62 -0
  384. package/tests/helpers.ts +22 -0
  385. package/tests/middleware/security.test.ts +42 -0
  386. package/tests/routers/agenda.test.ts +138 -0
  387. package/tests/routers/announcement.test.ts +490 -0
  388. package/tests/routers/assignment.test.ts +837 -0
  389. package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
  390. package/tests/routers/auth.test.ts +171 -0
  391. package/tests/{class.test.ts → routers/class.test.ts} +131 -85
  392. package/tests/routers/comment.test.ts +126 -0
  393. package/tests/routers/conversation.test.ts +145 -0
  394. package/tests/{event.test.ts → routers/event.test.ts} +93 -32
  395. package/tests/routers/folder.test.ts +178 -0
  396. package/tests/routers/labChat.test.ts +115 -0
  397. package/tests/routers/marketing.test.ts +59 -0
  398. package/tests/routers/message.test.ts +123 -0
  399. package/tests/routers/notification.test.ts +69 -0
  400. package/tests/{section.test.ts → routers/section.test.ts} +5 -13
  401. package/tests/server/rateLimit.test.ts +73 -0
  402. package/tests/setup.ts +18 -92
  403. package/tests/user.test.ts +9 -31
  404. package/tests/utils/aiUser.test.ts +22 -0
  405. package/tests/utils/generateInviteCode.test.ts +24 -0
  406. package/tests/utils/logger.test.ts +74 -0
  407. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  408. package/tests/utils/prismaWrapper.test.ts +82 -0
  409. package/tests/worksheet.test.ts +181 -0
  410. package/vitest.config.ts +6 -3
  411. package/vitest.unit.config.ts +21 -0
  412. package/TODO.md +0 -2
  413. package/coverage/base.css +0 -224
  414. package/coverage/block-navigation.js +0 -87
  415. package/coverage/clover.xml +0 -12110
  416. package/coverage/coverage-final.json +0 -44
  417. package/coverage/favicon.png +0 -0
  418. package/coverage/index.html +0 -221
  419. package/coverage/prettify.css +0 -1
  420. package/coverage/prettify.js +0 -2
  421. package/coverage/server/index.html +0 -116
  422. package/coverage/server/src/exportType.ts.html +0 -109
  423. package/coverage/server/src/index.html +0 -161
  424. package/coverage/server/src/index.ts.html +0 -1702
  425. package/coverage/server/src/instrument.ts.html +0 -130
  426. package/coverage/server/src/lib/config/env.ts.html +0 -448
  427. package/coverage/server/src/lib/config/index.html +0 -116
  428. package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
  429. package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
  430. package/coverage/server/src/lib/index.html +0 -206
  431. package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
  432. package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
  433. package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
  434. package/coverage/server/src/lib/pusher.ts.html +0 -121
  435. package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
  436. package/coverage/server/src/middleware/auth.ts.html +0 -646
  437. package/coverage/server/src/middleware/index.html +0 -146
  438. package/coverage/server/src/middleware/logging.ts.html +0 -244
  439. package/coverage/server/src/middleware/security.ts.html +0 -271
  440. package/coverage/server/src/routers/_app.ts.html +0 -232
  441. package/coverage/server/src/routers/agenda.ts.html +0 -319
  442. package/coverage/server/src/routers/announcement.ts.html +0 -3481
  443. package/coverage/server/src/routers/assignment.ts.html +0 -7633
  444. package/coverage/server/src/routers/attendance.ts.html +0 -1030
  445. package/coverage/server/src/routers/auth.ts.html +0 -1081
  446. package/coverage/server/src/routers/class.ts.html +0 -3535
  447. package/coverage/server/src/routers/comment.ts.html +0 -991
  448. package/coverage/server/src/routers/conversation.ts.html +0 -982
  449. package/coverage/server/src/routers/event.ts.html +0 -1609
  450. package/coverage/server/src/routers/file.ts.html +0 -1144
  451. package/coverage/server/src/routers/folder.ts.html +0 -2797
  452. package/coverage/server/src/routers/index.html +0 -386
  453. package/coverage/server/src/routers/labChat.ts.html +0 -3073
  454. package/coverage/server/src/routers/marketing.ts.html +0 -340
  455. package/coverage/server/src/routers/message.ts.html +0 -1912
  456. package/coverage/server/src/routers/notifications.ts.html +0 -364
  457. package/coverage/server/src/routers/section.ts.html +0 -1120
  458. package/coverage/server/src/routers/user.ts.html +0 -862
  459. package/coverage/server/src/routers/worksheet.ts.html +0 -1729
  460. package/coverage/server/src/trpc.ts.html +0 -397
  461. package/coverage/server/src/types/index.html +0 -116
  462. package/coverage/server/src/types/trpc.ts.html +0 -127
  463. package/coverage/server/src/utils/aiUser.ts.html +0 -280
  464. package/coverage/server/src/utils/email.ts.html +0 -121
  465. package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
  466. package/coverage/server/src/utils/index.html +0 -206
  467. package/coverage/server/src/utils/inference.ts.html +0 -709
  468. package/coverage/server/src/utils/logger.ts.html +0 -664
  469. package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
  470. package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
  471. package/coverage/server/vitest.config.ts.html +0 -196
  472. package/coverage/sort-arrow-sprite.png +0 -0
  473. package/coverage/sorter.js +0 -210
  474. package/src/lib/notificationHandler.ts +0 -36
  475. package/tests/announcement.test.ts +0 -164
  476. package/tests/assignment.test.ts +0 -296
  477. package/tests/auth.test.ts +0 -48
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Conversation service – list, create, and manage DM/group conversations.
3
+ * Handles member management and unread counts.
4
+ */
5
+ import { TRPCError } from "@trpc/server";
6
+ import {
7
+ findConversationsByUserId,
8
+ countUnreadMessages,
9
+ countUnreadMentions,
10
+ findUserByUsername,
11
+ findDmConversationsByUserId,
12
+ findUsersByIdsOrUsernames,
13
+ createConversation,
14
+ findConversationByIdAndMember,
15
+ createConversationMember,
16
+ deleteConversationMember,
17
+ } from "../models/conversation.js";
18
+
19
+ /** List user's conversations with unread and mention counts. */
20
+ export async function listConversations(userId: string) {
21
+ const conversations = await findConversationsByUserId(userId);
22
+
23
+ const conversationsWithUnread = await Promise.all(
24
+ conversations.map(async (conversation) => {
25
+ const userMembership = conversation.members.find(
26
+ (m) => m.userId === userId
27
+ );
28
+ const lastViewedAt = userMembership?.lastViewedAt;
29
+ const lastViewedMentionAt = userMembership?.lastViewedMentionAt;
30
+
31
+ const unreadCount = await countUnreadMessages(
32
+ conversation.id,
33
+ userId,
34
+ lastViewedAt ?? undefined
35
+ );
36
+
37
+ const mentionCutoffTime =
38
+ lastViewedMentionAt && lastViewedAt
39
+ ? lastViewedMentionAt > lastViewedAt
40
+ ? lastViewedMentionAt
41
+ : lastViewedAt
42
+ : lastViewedMentionAt || lastViewedAt;
43
+
44
+ const unreadMentionCount = await countUnreadMentions(
45
+ conversation.id,
46
+ userId,
47
+ mentionCutoffTime ?? undefined
48
+ );
49
+
50
+ return {
51
+ id: conversation.id,
52
+ type: conversation.type,
53
+ name: conversation.name,
54
+ createdAt: conversation.createdAt,
55
+ updatedAt: conversation.updatedAt,
56
+ labChat: conversation.labChat,
57
+ members: conversation.members,
58
+ lastMessage: conversation.messages[0] || null,
59
+ unreadCount,
60
+ unreadMentionCount,
61
+ };
62
+ })
63
+ );
64
+
65
+ return conversationsWithUnread;
66
+ }
67
+
68
+ /** Create a DM or group conversation with the given members. */
69
+ export async function createConversationRecord(
70
+ userId: string,
71
+ type: "DM" | "GROUP",
72
+ name: string | undefined,
73
+ memberIds: string[]
74
+ ) {
75
+ if (type === "GROUP" && !name) {
76
+ throw new TRPCError({
77
+ code: "BAD_REQUEST",
78
+ message: "Group conversations must have a name",
79
+ });
80
+ }
81
+
82
+ if (type === "DM" && memberIds.length !== 1) {
83
+ throw new TRPCError({
84
+ code: "BAD_REQUEST",
85
+ message: "DM conversations must have exactly one other member",
86
+ });
87
+ }
88
+
89
+ if (type === "DM") {
90
+ const targetUser = await findUserByUsername(memberIds[0]);
91
+ if (!targetUser) {
92
+ throw new TRPCError({
93
+ code: "BAD_REQUEST",
94
+ message: `User "${memberIds[0]}" not found`,
95
+ });
96
+ }
97
+
98
+ const existingDMs = await findDmConversationsByUserId(userId);
99
+ const existingDM = existingDMs.find((conv) => {
100
+ const memberUserIds = conv.members.map((m) => m.userId);
101
+ return (
102
+ memberUserIds.length === 2 &&
103
+ memberUserIds.includes(userId) &&
104
+ memberUserIds.includes(targetUser.id)
105
+ );
106
+ });
107
+
108
+ if (existingDM) {
109
+ throw new TRPCError({
110
+ code: "BAD_REQUEST",
111
+ message: `A conversation with ${targetUser.username} already exists`,
112
+ });
113
+ }
114
+ }
115
+
116
+ const members = await findUsersByIdsOrUsernames(memberIds);
117
+ type Member = (typeof members)[number];
118
+ const uniqueMembers = Array.from(
119
+ new Map(members.map((m) => [m.id, m] as [string, Member])).values()
120
+ );
121
+
122
+ if (uniqueMembers.length !== memberIds.length) {
123
+ throw new TRPCError({
124
+ code: "BAD_REQUEST",
125
+ message: "One or more members not found",
126
+ });
127
+ }
128
+
129
+ const toAddIds = memberIds
130
+ .map((id) =>
131
+ uniqueMembers.find((m) => m.id === id || m.username === id)?.id
132
+ )
133
+ .filter(Boolean) as string[];
134
+
135
+ return createConversation({
136
+ type,
137
+ name,
138
+ members: [
139
+ { userId, role: type === "GROUP" ? "ADMIN" : "MEMBER" },
140
+ ...toAddIds.map((uid) => ({ userId: uid, role: "MEMBER" })),
141
+ ],
142
+ });
143
+ }
144
+
145
+ export async function getConversation(
146
+ userId: string,
147
+ conversationId: string
148
+ ) {
149
+ const conversation = await findConversationByIdAndMember(
150
+ conversationId,
151
+ userId
152
+ );
153
+ if (!conversation) {
154
+ throw new TRPCError({
155
+ code: "NOT_FOUND",
156
+ message: "Conversation not found or access denied",
157
+ });
158
+ }
159
+ return conversation;
160
+ }
161
+
162
+ export async function addMemberToConversation(
163
+ userId: string,
164
+ conversationId: string,
165
+ memberUsername: string
166
+ ) {
167
+ const conversation = await findConversationByIdAndMember(
168
+ conversationId,
169
+ userId
170
+ );
171
+ if (!conversation) {
172
+ throw new TRPCError({
173
+ code: "NOT_FOUND",
174
+ message: "Conversation not found or access denied",
175
+ });
176
+ }
177
+
178
+ const member = await findUserByUsername(memberUsername);
179
+ if (!member) {
180
+ throw new TRPCError({
181
+ code: "NOT_FOUND",
182
+ message: "Member not found",
183
+ });
184
+ }
185
+
186
+ await createConversationMember({
187
+ userId: member.id,
188
+ conversationId,
189
+ role: "MEMBER",
190
+ });
191
+
192
+ return conversation;
193
+ }
194
+
195
+ export async function removeMemberFromConversation(
196
+ userId: string,
197
+ conversationId: string,
198
+ memberId: string
199
+ ) {
200
+ const conversation = await findConversationByIdAndMember(
201
+ conversationId,
202
+ userId
203
+ );
204
+ if (!conversation) {
205
+ throw new TRPCError({
206
+ code: "NOT_FOUND",
207
+ message: "Conversation not found or access denied",
208
+ });
209
+ }
210
+
211
+ await deleteConversationMember(memberId, conversationId);
212
+ return conversation;
213
+ }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Event service – CRUD for calendar events. Supports personal and class events.
3
+ * Handles assignment attachment/detachment.
4
+ */
5
+ import { TRPCError } from "@trpc/server";
6
+ import { parseISO } from "date-fns";
7
+ import {
8
+ findEventById,
9
+ findClassWithTeachers,
10
+ createEvent,
11
+ updateEvent,
12
+ deleteEventById,
13
+ findEventWithClass,
14
+ findAssignmentWithClass,
15
+ attachAssignmentToEvent,
16
+ detachAssignmentFromEvent,
17
+ findAvailableAssignmentsForClass,
18
+ } from "../models/event.js";
19
+
20
+ /** Get a single event. Personal events require ownership. */
21
+ export async function getEvent(userId: string, id: string) {
22
+ const event = await findEventById(id);
23
+ if (!event) {
24
+ throw new TRPCError({
25
+ code: "NOT_FOUND",
26
+ message: "Event not found",
27
+ });
28
+ }
29
+ if (event.userId !== userId) {
30
+ throw new TRPCError({
31
+ code: "UNAUTHORIZED",
32
+ message: "You are not authorized to view this event",
33
+ });
34
+ }
35
+ return { event };
36
+ }
37
+
38
+ /** Create a personal or class event. */
39
+ export async function createEventRecord(
40
+ userId: string,
41
+ input: {
42
+ name?: string;
43
+ location?: string;
44
+ remarks?: string;
45
+ startTime: string;
46
+ endTime: string;
47
+ classId?: string;
48
+ color?: string;
49
+ }
50
+ ) {
51
+ if (input.classId) {
52
+ const classData = await findClassWithTeachers(input.classId, userId);
53
+ if (!classData) {
54
+ throw new TRPCError({
55
+ code: "UNAUTHORIZED",
56
+ message: "You are not authorized to create events for this class",
57
+ });
58
+ }
59
+ }
60
+
61
+ const event = await createEvent({
62
+ name: input.name,
63
+ location: input.location,
64
+ remarks: input.remarks,
65
+ startTime: parseISO(input.startTime),
66
+ endTime: parseISO(input.endTime),
67
+ userId,
68
+ color: input.color,
69
+ classId: input.classId,
70
+ });
71
+
72
+ return { event };
73
+ }
74
+
75
+ export async function updateEventRecord(
76
+ userId: string,
77
+ id: string,
78
+ data: {
79
+ name?: string;
80
+ location?: string;
81
+ remarks?: string;
82
+ startTime?: string;
83
+ endTime?: string;
84
+ color?: string;
85
+ classId?: string;
86
+ }
87
+ ) {
88
+ const event = await findEventById(id);
89
+ if (!event) {
90
+ throw new TRPCError({
91
+ code: "NOT_FOUND",
92
+ message: "Event not found",
93
+ });
94
+ }
95
+ if (event.userId !== userId) {
96
+ throw new TRPCError({
97
+ code: "UNAUTHORIZED",
98
+ message: "You are not authorized to update this event",
99
+ });
100
+ }
101
+
102
+ const updatedEvent = await updateEvent(id, {
103
+ name: data.name,
104
+ location: data.location,
105
+ remarks: data.remarks,
106
+ startTime: data.startTime ? parseISO(data.startTime) : undefined,
107
+ endTime: data.endTime ? parseISO(data.endTime) : undefined,
108
+ color: data.color,
109
+ classId: data.classId,
110
+ });
111
+
112
+ return { event: updatedEvent };
113
+ }
114
+
115
+ export async function deleteEventRecord(userId: string, id: string) {
116
+ const event = await findEventById(id);
117
+ if (!event) {
118
+ throw new TRPCError({
119
+ code: "NOT_FOUND",
120
+ message: "Event not found",
121
+ });
122
+ }
123
+ if (event.userId !== userId) {
124
+ throw new TRPCError({
125
+ code: "UNAUTHORIZED",
126
+ message: "You are not authorized to delete this event",
127
+ });
128
+ }
129
+
130
+ await deleteEventById(id);
131
+ return { success: true };
132
+ }
133
+
134
+ function canModifyEvent(
135
+ event: { userId: string | null; class?: { teachers: { id: string }[] } | null },
136
+ userId: string
137
+ ) {
138
+ if (event.userId === userId) return true;
139
+ return event.class?.teachers.some((t) => t.id === userId) ?? false;
140
+ }
141
+
142
+ export async function attachAssignment(
143
+ userId: string,
144
+ eventId: string,
145
+ assignmentId: string
146
+ ) {
147
+ const event = await findEventWithClass(eventId);
148
+ if (!event) {
149
+ throw new TRPCError({
150
+ code: "NOT_FOUND",
151
+ message: "Event not found",
152
+ });
153
+ }
154
+ if (!canModifyEvent(event, userId)) {
155
+ throw new TRPCError({
156
+ code: "UNAUTHORIZED",
157
+ message: "You are not authorized to modify this event",
158
+ });
159
+ }
160
+
161
+ const assignment = await findAssignmentWithClass(assignmentId);
162
+ if (!assignment) {
163
+ throw new TRPCError({
164
+ code: "NOT_FOUND",
165
+ message: "Assignment not found",
166
+ });
167
+ }
168
+ if (!assignment.class.teachers.some((t) => t.id === userId)) {
169
+ throw new TRPCError({
170
+ code: "UNAUTHORIZED",
171
+ message: "You are not authorized to modify this assignment",
172
+ });
173
+ }
174
+ if (event.classId !== assignment.classId) {
175
+ throw new TRPCError({
176
+ code: "BAD_REQUEST",
177
+ message: "Event and assignment must belong to the same class",
178
+ });
179
+ }
180
+
181
+ const updatedAssignment = await attachAssignmentToEvent(
182
+ assignmentId,
183
+ eventId
184
+ );
185
+ return { assignment: updatedAssignment };
186
+ }
187
+
188
+ export async function detachAssignment(
189
+ userId: string,
190
+ eventId: string,
191
+ assignmentId: string
192
+ ) {
193
+ const event = await findEventWithClass(eventId);
194
+ if (!event) {
195
+ throw new TRPCError({
196
+ code: "NOT_FOUND",
197
+ message: "Event not found",
198
+ });
199
+ }
200
+ if (!canModifyEvent(event, userId)) {
201
+ throw new TRPCError({
202
+ code: "UNAUTHORIZED",
203
+ message: "You are not authorized to modify this event",
204
+ });
205
+ }
206
+
207
+ const updatedAssignment = await detachAssignmentFromEvent(assignmentId);
208
+ return { assignment: updatedAssignment };
209
+ }
210
+
211
+ export async function getAvailableAssignments(
212
+ userId: string,
213
+ eventId: string
214
+ ) {
215
+ const event = await findEventWithClass(eventId);
216
+ if (!event || !event.classId) {
217
+ throw new TRPCError({
218
+ code: "NOT_FOUND",
219
+ message: "Event not found",
220
+ });
221
+ }
222
+ if (!canModifyEvent(event, userId)) {
223
+ throw new TRPCError({
224
+ code: "UNAUTHORIZED",
225
+ message: "You are not authorized to access this event",
226
+ });
227
+ }
228
+
229
+ const assignments = await findAvailableAssignmentsForClass(event.classId);
230
+ return { assignments };
231
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * File service – signed URLs, move, rename, delete. Handles permission checks
3
+ * for assignment, submission, announcement, folder, and user-owned files.
4
+ */
5
+ import { TRPCError } from "@trpc/server";
6
+ import { getSignedUrl, deleteFile } from "../lib/googleCloudStorage.js";
7
+ import { logger } from "../utils/logger.js";
8
+ import {
9
+ findFileById,
10
+ findFileWithFolder,
11
+ findFileWithFolderAndThumbnail,
12
+ findFolderById,
13
+ updateFileFolder,
14
+ updateFileName,
15
+ deleteFileById,
16
+ } from "../models/file.js";
17
+
18
+ /** Generate a signed URL for file download. Validates user has access. */
19
+ export async function getFileSignedUrl(userId: string, fileId: string) {
20
+ const file = await findFileById(fileId);
21
+ if (!file) {
22
+ throw new TRPCError({
23
+ code: "NOT_FOUND",
24
+ message: "File does not exist",
25
+ });
26
+ }
27
+
28
+ let hasAccess = false;
29
+
30
+ if (file.assignment?.class) {
31
+ const isTeacher = file.assignment.class.teachers.some(
32
+ (t) => t.id === userId
33
+ );
34
+ const isStudent = file.assignment.class.students.some(
35
+ (s) => s.id === userId
36
+ );
37
+ logger.info(
38
+ `Assignment file access check - userId: ${userId}, isTeacher: ${isTeacher}, isStudent: ${isStudent}`
39
+ );
40
+ hasAccess = isTeacher || isStudent;
41
+ }
42
+
43
+ if ((file as any).announcement?.class) {
44
+ const ann = (file as any).announcement;
45
+ const isTeacher = ann.class.teachers.some((t: any) => t.id === userId);
46
+ const isStudent = ann.class.students.some((s: any) => s.id === userId);
47
+ hasAccess = hasAccess || isTeacher || isStudent;
48
+ }
49
+
50
+ if (file.submission?.assignment?.classId) {
51
+ hasAccess =
52
+ hasAccess ||
53
+ file.submission.studentId === userId ||
54
+ file.submission.assignment.class.teachers.some((t) => t.id === userId);
55
+ }
56
+
57
+ if (file.annotations?.assignment?.classId) {
58
+ hasAccess =
59
+ hasAccess ||
60
+ file.annotations.studentId === userId ||
61
+ file.annotations.assignment.class.teachers.some((t) => t.id === userId);
62
+ }
63
+
64
+ if (file.userId === userId) {
65
+ hasAccess = true;
66
+ }
67
+
68
+ if (file.folder?.class) {
69
+ const isTeacher = file.folder.class.teachers.some((t) => t.id === userId);
70
+ const isStudent = file.folder.class.students.some((s) => s.id === userId);
71
+ hasAccess = hasAccess || isTeacher || isStudent;
72
+ }
73
+
74
+ if (!hasAccess) {
75
+ throw new TRPCError({
76
+ code: "FORBIDDEN",
77
+ message: "You do not have access to this file",
78
+ });
79
+ }
80
+
81
+ try {
82
+ const signedUrl = await getSignedUrl(file.path);
83
+ return { url: signedUrl };
84
+ } catch (error) {
85
+ logger.error("Error generating signed URL:", error as Record<string, unknown>);
86
+ throw new TRPCError({
87
+ code: "INTERNAL_SERVER_ERROR",
88
+ message: "Failed to generate download URL",
89
+ });
90
+ }
91
+ }
92
+
93
+ export async function moveFile(
94
+ fileId: string,
95
+ targetFolderId: string
96
+ ) {
97
+ const file = await findFileWithFolder(fileId);
98
+ if (!file) {
99
+ throw new TRPCError({
100
+ code: "NOT_FOUND",
101
+ message: "File not found",
102
+ });
103
+ }
104
+
105
+ const targetFolder = await findFolderById(targetFolderId);
106
+ if (!targetFolder) {
107
+ throw new TRPCError({
108
+ code: "NOT_FOUND",
109
+ message: "Target folder not found",
110
+ });
111
+ }
112
+
113
+ return updateFileFolder(fileId, targetFolderId);
114
+ }
115
+
116
+ export async function renameFile(
117
+ fileId: string,
118
+ newName: string
119
+ ) {
120
+ const file = await findFileWithFolder(fileId);
121
+ if (!file) {
122
+ throw new TRPCError({
123
+ code: "NOT_FOUND",
124
+ message: "File not found",
125
+ });
126
+ }
127
+
128
+ if (!newName.trim()) {
129
+ throw new TRPCError({
130
+ code: "BAD_REQUEST",
131
+ message: "File name cannot be empty",
132
+ });
133
+ }
134
+
135
+ return updateFileName(fileId, newName.trim());
136
+ }
137
+
138
+ export async function deleteFileRecord(fileId: string, classId: string) {
139
+ const file = await findFileWithFolderAndThumbnail(fileId);
140
+ if (!file) {
141
+ throw new TRPCError({
142
+ code: "NOT_FOUND",
143
+ message: "File not found",
144
+ });
145
+ }
146
+
147
+ if (file.folder?.classId !== classId) {
148
+ throw new TRPCError({
149
+ code: "FORBIDDEN",
150
+ message: "File does not belong to this class",
151
+ });
152
+ }
153
+
154
+ try {
155
+ if (file.uploadStatus === "COMPLETED") {
156
+ await deleteFile(file.path);
157
+ if (file.thumbnail) {
158
+ await deleteFile(file.thumbnail.path);
159
+ }
160
+ }
161
+ } catch (error) {
162
+ logger.warn(`Failed to delete file ${file.path}:`, error as Record<string, unknown>);
163
+ }
164
+
165
+ await deleteFileById(fileId);
166
+ return { success: true };
167
+ }