@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
@@ -2,10 +2,26 @@ import { z } from "zod";
2
2
  import { createTRPCRouter, protectedClassMemberProcedure, protectedTeacherProcedure, protectedProcedure } from "../trpc.js";
3
3
  import { prisma } from "../lib/prisma.js";
4
4
  import { TRPCError } from "@trpc/server";
5
- import { sendNotifications } from "../lib/notificationHandler.js";
5
+ import {
6
+ getAllAnnouncements,
7
+ getAnnouncement,
8
+ createAnnouncementRecord,
9
+ updateAnnouncementRecord,
10
+ deleteAnnouncementRecord,
11
+ } from "../services/announcement.js";
12
+ import { findAnnouncementByIdAndClass } from "../models/announcement.js";
13
+ import {
14
+ findCommentWithAnnouncement,
15
+ findReactionByUserAndComment,
16
+ upsertReaction,
17
+ deleteReactionById,
18
+ } from "../models/comment.js";
19
+ import { getReactions as getCommentReactions } from "../services/comment.js";
20
+
6
21
  import { logger } from "../utils/logger.js";
7
22
  import { createDirectUploadFiles, type UploadedFile, type DirectUploadFile, confirmDirectUpload } from "../lib/fileUpload.js";
8
23
  import { deleteFile } from "../lib/googleCloudStorage.js";
24
+ import { sendToMultiple } from "../services/notification.js";
9
25
 
10
26
  // Schema for direct file uploads (no base64 data)
11
27
  const directFileSchema = z.object({
@@ -27,97 +43,14 @@ const confirmAnnouncementUploadSchema = z.object({
27
43
  errorMessage: z.string().optional(),
28
44
  });
29
45
 
30
- const AnnouncementSelect = {
31
- id: true,
32
- teacher: {
33
- select: {
34
- id: true,
35
- username: true,
36
- profile: {
37
- select: {
38
- displayName: true,
39
- profilePicture: true,
40
- profilePictureThumbnail: true,
41
- },
42
- },
43
- },
44
- },
45
- remarks: true,
46
- createdAt: true,
47
- modifiedAt: true,
48
- attachments: {
49
- select: {
50
- id: true,
51
- name: true,
52
- type: true,
53
- size: true,
54
- path: true,
55
- uploadedAt: true,
56
- thumbnailId: true,
57
- },
58
- },
59
- };
60
-
61
46
  export const announcementRouter = createTRPCRouter({
62
47
  getAll: protectedClassMemberProcedure
63
- .input(z.object({
64
- classId: z.string(),
65
- }))
66
- .query(async ({ ctx, input }) => {
67
- const announcements = await prisma.announcement.findMany({
68
- where: {
69
- classId: input.classId,
70
- },
71
- select: {
72
- ...AnnouncementSelect,
73
- _count: {
74
- select: {
75
- comments: true,
76
- },
77
- },
78
- },
79
- orderBy: {
80
- createdAt: 'desc',
81
- },
82
- });
83
-
84
- // Transform to include comment count
85
- const announcementsWithCounts = announcements.map(announcement => ({
86
- ...announcement,
87
- commentCount: announcement._count.comments,
88
- _count: undefined,
89
- }));
90
-
91
- return {
92
- announcements: announcementsWithCounts,
93
- };
94
- }),
48
+ .input(z.object({ classId: z.string() }))
49
+ .query(({ input }) => getAllAnnouncements(input.classId)),
95
50
 
96
51
  get: protectedClassMemberProcedure
97
- .input(z.object({
98
- id: z.string(),
99
- classId: z.string(),
100
- }))
101
- .query(async ({ ctx, input }) => {
102
- const announcement = await prisma.announcement.findUnique({
103
- where: {
104
- id: input.id,
105
- classId: input.classId,
106
- },
107
- select: AnnouncementSelect,
108
- });
109
-
110
- if (!announcement) {
111
- throw new TRPCError({
112
- code: "NOT_FOUND",
113
- message: "Announcement not found",
114
- });
115
- }
116
-
117
- return {
118
- announcement,
119
- };
120
- }),
52
+ .input(z.object({ id: z.string(), classId: z.string() }))
53
+ .query(({ input }) => getAnnouncement(input.id, input.classId)),
121
54
 
122
55
  create: protectedTeacherProcedure
123
56
  .input(z.object({
@@ -126,92 +59,14 @@ export const announcementRouter = createTRPCRouter({
126
59
  files: z.array(directFileSchema).optional(),
127
60
  existingFileIds: z.array(z.string()).optional(),
128
61
  }))
129
- .mutation(async ({ ctx, input }) => {
130
- const { classId, remarks, files, existingFileIds } = input;
131
-
132
- if (!ctx.user) {
133
- throw new TRPCError({
134
- code: "UNAUTHORIZED",
135
- message: "User must be authenticated",
136
- });
137
- }
138
-
139
- const classData = await prisma.class.findUnique({
140
- where: { id: classId },
141
- include: {
142
- students: {
143
- select: { id: true }
144
- }
145
- }
146
- });
147
-
148
- if (!classData) {
149
- throw new TRPCError({
150
- code: "NOT_FOUND",
151
- message: "Class not found",
152
- });
153
- }
154
-
155
- const announcement = await prisma.announcement.create({
156
- data: {
157
- remarks: remarks,
158
- teacher: {
159
- connect: {
160
- id: ctx.user.id,
161
- },
162
- },
163
- class: {
164
- connect: {
165
- id: classId,
166
- },
167
- },
168
- },
169
- select: AnnouncementSelect,
170
- });
171
-
172
- // Handle file attachments
173
- // NOTE: Files are now handled via direct upload endpoints
174
- // The files field in the schema is for metadata only
175
- // Actual file uploads should use getAnnouncementUploadUrls endpoint
176
- // However, if files are provided here, we create the file records and return upload URLs
177
- let directUploadFiles: DirectUploadFile[] = [];
178
- if (files && files.length > 0) {
179
- // Create direct upload files - this creates file records with upload URLs
180
- // Files are automatically connected to the announcement via announcementId
181
- directUploadFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, undefined, undefined, announcement.id);
182
- }
183
-
184
- // Connect existing files if provided
185
- if (existingFileIds && existingFileIds.length > 0) {
186
- await prisma.announcement.update({
187
- where: { id: announcement.id },
188
- data: {
189
- attachments: {
190
- connect: existingFileIds.map(fileId => ({ id: fileId }))
191
- }
192
- }
193
- });
194
- }
195
-
196
- // Fetch announcement with attachments
197
- const announcementWithAttachments = await prisma.announcement.findUnique({
198
- where: { id: announcement.id },
199
- select: AnnouncementSelect,
200
- });
201
-
202
- sendNotifications(classData.students.map(student => student.id), {
203
- title: `🔔 Announcement for ${classData.name}`,
204
- content: remarks
205
- }).catch(error => {
206
- logger.error('Failed to send announcement notifications:', error);
207
- });
208
-
209
- return {
210
- announcement: announcementWithAttachments || announcement,
211
- // Return upload URLs if files were provided
212
- uploadFiles: directUploadFiles.length > 0 ? directUploadFiles : undefined,
213
- };
214
- }),
62
+ .mutation(({ ctx, input }) =>
63
+ createAnnouncementRecord(ctx.user!.id, {
64
+ classId: input.classId,
65
+ remarks: input.remarks,
66
+ files: input.files,
67
+ existingFileIds: input.existingFileIds,
68
+ })
69
+ ),
215
70
 
216
71
  update: protectedTeacherProcedure
217
72
  .input(z.object({
@@ -224,180 +79,19 @@ export const announcementRouter = createTRPCRouter({
224
79
  removedAttachments: z.array(z.string()).optional(),
225
80
  }),
226
81
  }))
227
- .mutation(async ({ ctx, input }) => {
228
- if (!ctx.user) {
229
- throw new TRPCError({
230
- code: "UNAUTHORIZED",
231
- message: "User must be authenticated",
232
- });
233
- }
234
-
235
- const announcement = await prisma.announcement.findUnique({
236
- where: { id: input.id },
237
- include: {
238
- class: {
239
- include: {
240
- teachers: true,
241
- },
242
- },
243
- attachments: {
244
- select: {
245
- id: true,
246
- name: true,
247
- type: true,
248
- path: true,
249
- size: true,
250
- uploadStatus: true,
251
- thumbnail: {
252
- select: {
253
- path: true
254
- }
255
- }
256
- },
257
- },
258
- },
259
- });
260
-
261
- if (!announcement) {
262
- throw new TRPCError({
263
- code: "NOT_FOUND",
264
- message: "Announcement not found",
265
- });
266
- }
267
-
268
- // Authorization check: user must be the creator OR a teacher in the class
269
- const userId = ctx.user.id;
270
- const isCreator = announcement.teacherId === userId;
271
- const isClassTeacher = announcement.class.teachers.some(
272
- (teacher) => teacher.id === userId
273
- );
274
-
275
- if (!isCreator && !isClassTeacher) {
276
- throw new TRPCError({
277
- code: "FORBIDDEN",
278
- message: "Only the announcement creator or class teachers can update announcements",
279
- });
280
- }
281
-
282
- // Handle file attachments
283
- // NOTE: Files are now handled via direct upload endpoints
284
- let directUploadFiles: DirectUploadFile[] = [];
285
- if (input.data.files && input.data.files.length > 0) {
286
- // Create direct upload files - this creates file records with upload URLs
287
- // Files are automatically connected to the announcement via announcementId
288
- directUploadFiles = await createDirectUploadFiles(input.data.files, userId, undefined, undefined, undefined, input.id);
289
- }
290
-
291
- // Delete removed attachments from storage before updating database
292
- if (input.data.removedAttachments && input.data.removedAttachments.length > 0) {
293
- const filesToDelete = announcement.attachments.filter((file) =>
294
- input.data.removedAttachments!.includes(file.id)
295
- );
296
-
297
- // Delete files from storage (only if they were actually uploaded)
298
- await Promise.all(filesToDelete.map(async (file) => {
299
- try {
300
- // Only delete from GCS if the file was successfully uploaded
301
- if (file.uploadStatus === 'COMPLETED') {
302
- // Delete the main file
303
- await deleteFile(file.path);
304
-
305
- // Delete thumbnail if it exists
306
- if (file.thumbnail?.path) {
307
- await deleteFile(file.thumbnail.path);
308
- }
309
- }
310
- } catch (error) {
311
- logger.warn(`Failed to delete file ${file.path}:`, {
312
- error: error instanceof Error ? {
313
- name: error.name,
314
- message: error.message,
315
- stack: error.stack,
316
- } : error
317
- });
318
- }
319
- }));
320
- }
321
-
322
- const updatedAnnouncement = await prisma.announcement.update({
323
- where: { id: input.id },
324
- data: {
325
- ...(input.data.remarks && { remarks: input.data.remarks }),
326
- // Note: directUploadFiles are already connected via createDirectUploadFiles
327
- ...(input.data.existingFileIds && input.data.existingFileIds.length > 0 && {
328
- attachments: {
329
- connect: input.data.existingFileIds.map(fileId => ({ id: fileId }))
330
- }
331
- }),
332
- ...(input.data.removedAttachments && input.data.removedAttachments.length > 0 && {
333
- attachments: {
334
- deleteMany: {
335
- id: { in: input.data.removedAttachments }
336
- }
337
- }
338
- }),
339
- },
340
- select: AnnouncementSelect,
341
- });
342
-
343
- return {
344
- announcement: updatedAnnouncement,
345
- // Return upload URLs if new files were provided
346
- uploadFiles: directUploadFiles.length > 0 ? directUploadFiles : undefined,
347
- };
348
- }),
82
+ .mutation(({ ctx, input }) =>
83
+ updateAnnouncementRecord(ctx.user!.id, {
84
+ id: input.id,
85
+ classId: input.classId,
86
+ data: input.data,
87
+ })
88
+ ),
349
89
 
350
90
  delete: protectedTeacherProcedure
351
- .input(z.object({
352
- id: z.string(),
353
- classId: z.string(),
354
- }))
355
- .mutation(async ({ ctx, input }) => {
356
- if (!ctx.user) {
357
- throw new TRPCError({
358
- code: "UNAUTHORIZED",
359
- message: "User must be authenticated",
360
- });
361
- }
362
-
363
- const announcement = await prisma.announcement.findUnique({
364
- where: { id: input.id },
365
- include: {
366
- class: {
367
- include: {
368
- teachers: true,
369
- },
370
- },
371
- },
372
- });
373
-
374
- if (!announcement) {
375
- throw new TRPCError({
376
- code: "NOT_FOUND",
377
- message: "Announcement not found",
378
- });
379
- }
380
-
381
- // Authorization check: user must be the creator OR a teacher in the class
382
- const userId = ctx.user.id;
383
- const isCreator = announcement.teacherId === userId;
384
- const isClassTeacher = announcement.class.teachers.some(
385
- (teacher) => teacher.id === userId
386
- );
387
-
388
- if (!isCreator && !isClassTeacher) {
389
- throw new TRPCError({
390
- code: "FORBIDDEN",
391
- message: "Only the announcement creator or class teachers can delete announcements",
392
- });
393
- }
394
-
395
- await prisma.announcement.delete({
396
- where: { id: input.id },
397
- });
398
-
399
- return { success: true };
400
- }),
91
+ .input(z.object({ id: z.string(), classId: z.string() }))
92
+ .mutation(({ ctx, input }) =>
93
+ deleteAnnouncementRecord(ctx.user!.id, input.id, input.classId)
94
+ ),
401
95
 
402
96
  getAnnouncementUploadUrls: protectedTeacherProcedure
403
97
  .input(getAnnouncementUploadUrlsSchema)
@@ -430,14 +124,7 @@ export const announcementRouter = createTRPCRouter({
430
124
  });
431
125
  }
432
126
 
433
- // Verify announcement exists and belongs to the class
434
- const announcement = await prisma.announcement.findFirst({
435
- where: {
436
- id: announcementId,
437
- classId: classId,
438
- },
439
- });
440
-
127
+ const announcement = await findAnnouncementByIdAndClass(announcementId, classId);
441
128
  if (!announcement) {
442
129
  throw new TRPCError({
443
130
  code: "NOT_FOUND",
@@ -515,14 +202,7 @@ export const announcementRouter = createTRPCRouter({
515
202
  });
516
203
  }
517
204
 
518
- // Verify announcement exists and belongs to the class
519
- const announcement = await prisma.announcement.findFirst({
520
- where: {
521
- id: input.announcementId,
522
- classId: input.classId,
523
- },
524
- });
525
-
205
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
526
206
  if (!announcement) {
527
207
  throw new TRPCError({
528
208
  code: "NOT_FOUND",
@@ -700,14 +380,7 @@ export const announcementRouter = createTRPCRouter({
700
380
  classId: z.string(),
701
381
  }))
702
382
  .query(async ({ ctx, input }) => {
703
- // Verify announcement exists and belongs to the class
704
- const announcement = await prisma.announcement.findFirst({
705
- where: {
706
- id: input.announcementId,
707
- classId: input.classId,
708
- },
709
- });
710
-
383
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
711
384
  if (!announcement) {
712
385
  throw new TRPCError({
713
386
  code: "NOT_FOUND",
@@ -797,15 +470,8 @@ export const announcementRouter = createTRPCRouter({
797
470
 
798
471
  const userId = ctx.user.id;
799
472
 
800
- // Verify the announcement or comment exists and belongs to the class
801
473
  if (input.announcementId) {
802
- const announcement = await prisma.announcement.findFirst({
803
- where: {
804
- id: input.announcementId,
805
- classId: input.classId,
806
- },
807
- });
808
-
474
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
809
475
  if (!announcement) {
810
476
  throw new TRPCError({
811
477
  code: "NOT_FOUND",
@@ -848,25 +514,13 @@ export const announcementRouter = createTRPCRouter({
848
514
 
849
515
  return { reaction };
850
516
  } else if (input.commentId) {
851
- // Verify comment exists and get its announcement to check class
852
- const comment = await prisma.comment.findUnique({
853
- where: { id: input.commentId },
854
- include: {
855
- announcement: {
856
- select: {
857
- classId: true,
858
- },
859
- },
860
- },
861
- });
862
-
517
+ const comment = await findCommentWithAnnouncement(input.commentId);
863
518
  if (!comment) {
864
519
  throw new TRPCError({
865
520
  code: "NOT_FOUND",
866
521
  message: "Comment not found",
867
522
  });
868
523
  }
869
-
870
524
  if (comment.announcement!.classId !== input.classId) {
871
525
  throw new TRPCError({
872
526
  code: "FORBIDDEN",
@@ -874,39 +528,11 @@ export const announcementRouter = createTRPCRouter({
874
528
  });
875
529
  }
876
530
 
877
- // Upsert reaction: update if exists, create if not
878
- const reaction = await prisma.reaction.upsert({
879
- where: {
880
- userId_commentId: {
881
- userId,
882
- commentId: input.commentId,
883
- },
884
- },
885
- update: {
886
- type: input.type,
887
- },
888
- create: {
889
- type: input.type,
890
- userId,
891
- commentId: input.commentId,
892
- },
893
- include: {
894
- user: {
895
- select: {
896
- id: true,
897
- username: true,
898
- profile: {
899
- select: {
900
- displayName: true,
901
- profilePicture: true,
902
- profilePictureThumbnail: true,
903
- },
904
- },
905
- },
906
- },
907
- },
531
+ const reaction = await upsertReaction({
532
+ userId,
533
+ commentId: input.commentId,
534
+ type: input.type,
908
535
  });
909
-
910
536
  return { reaction };
911
537
  }
912
538
 
@@ -962,26 +588,14 @@ export const announcementRouter = createTRPCRouter({
962
588
 
963
589
  return { success: true };
964
590
  } else if (input.commentId) {
965
- const reaction = await prisma.reaction.findUnique({
966
- where: {
967
- userId_commentId: {
968
- userId,
969
- commentId: input.commentId,
970
- },
971
- },
972
- });
973
-
591
+ const reaction = await findReactionByUserAndComment(userId, input.commentId);
974
592
  if (!reaction) {
975
593
  throw new TRPCError({
976
594
  code: "NOT_FOUND",
977
595
  message: "Reaction not found",
978
596
  });
979
597
  }
980
-
981
- await prisma.reaction.delete({
982
- where: { id: reaction.id },
983
- });
984
-
598
+ await deleteReactionById(reaction.id);
985
599
  return { success: true };
986
600
  }
987
601
 
@@ -1016,14 +630,7 @@ export const announcementRouter = createTRPCRouter({
1016
630
  const userId = ctx.user.id;
1017
631
 
1018
632
  if (input.announcementId) {
1019
- // Verify announcement exists
1020
- const announcement = await prisma.announcement.findFirst({
1021
- where: {
1022
- id: input.announcementId,
1023
- classId: input.classId,
1024
- },
1025
- });
1026
-
633
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
1027
634
  if (!announcement) {
1028
635
  throw new TRPCError({
1029
636
  code: "NOT_FOUND",
@@ -1068,68 +675,20 @@ export const announcementRouter = createTRPCRouter({
1068
675
  total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),
1069
676
  };
1070
677
  } else if (input.commentId) {
1071
- // Verify comment exists
1072
- const comment = await prisma.comment.findUnique({
1073
- where: { id: input.commentId },
1074
- include: {
1075
- announcement: {
1076
- select: {
1077
- classId: true,
1078
- },
1079
- },
1080
- },
1081
- });
1082
-
678
+ const comment = await findCommentWithAnnouncement(input.commentId);
1083
679
  if (!comment) {
1084
680
  throw new TRPCError({
1085
681
  code: "NOT_FOUND",
1086
682
  message: "Comment not found",
1087
683
  });
1088
684
  }
1089
-
1090
685
  if (comment.announcement!.classId !== input.classId) {
1091
686
  throw new TRPCError({
1092
687
  code: "FORBIDDEN",
1093
688
  message: "Comment does not belong to this class",
1094
689
  });
1095
690
  }
1096
-
1097
- // Get reaction counts by type
1098
- const reactionCounts = await prisma.reaction.groupBy({
1099
- by: ['type'],
1100
- where: { commentId: input.commentId },
1101
- _count: { type: true },
1102
- });
1103
-
1104
- // Get current user's reaction
1105
- const userReaction = await prisma.reaction.findUnique({
1106
- where: {
1107
- userId_commentId: {
1108
- userId,
1109
- commentId: input.commentId,
1110
- },
1111
- },
1112
- });
1113
-
1114
- // Format counts
1115
- const counts = {
1116
- THUMBSUP: 0,
1117
- CELEBRATE: 0,
1118
- CARE: 0,
1119
- HEART: 0,
1120
- IDEA: 0,
1121
- HAPPY: 0,
1122
- };
1123
-
1124
- reactionCounts.forEach((item) => {
1125
- counts[item.type as keyof typeof counts] = item._count.type;
1126
- });
1127
-
1128
- return {
1129
- counts,
1130
- userReaction: userReaction?.type || null,
1131
- total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),
1132
- };
691
+ return getCommentReactions(userId, input.commentId);
1133
692
  }
1134
693
 
1135
694
  throw new TRPCError({