@studious-lms/server 1.2.53 → 1.4.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 (479) 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 +461 -0
  53. package/dist/models/class.d.ts.map +1 -0
  54. package/dist/models/class.js +645 -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 +76 -0
  109. package/dist/pipelines/aiLabChat.d.ts.map +1 -0
  110. package/dist/pipelines/aiLabChat.js +599 -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 +1523 -1315
  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 +160 -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 +21 -11
  167. package/dist/routers/labChat.d.ts.map +1 -1
  168. package/dist/routers/labChat.js +22 -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 +13 -2
  175. package/dist/routers/message.d.ts.map +1 -1
  176. package/dist/routers/message.js +32 -520
  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 +68 -55
  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 +643 -0
  224. package/dist/services/class.d.ts.map +1 -0
  225. package/dist/services/class.js +486 -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 +169 -0
  248. package/dist/services/labChat.d.ts.map +1 -0
  249. package/dist/services/labChat.js +381 -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 +103 -0
  256. package/dist/services/message.d.ts.map +1 -0
  257. package/dist/services/message.js +422 -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/prisma/schema.prisma +1 -1
  309. package/src/index.ts +119 -12
  310. package/src/lib/config/env.ts +6 -0
  311. package/src/lib/fileUpload.ts +0 -1
  312. package/src/lib/googleCloudStorage.ts +17 -0
  313. package/src/lib/pusher.ts +5 -1
  314. package/src/lib/redis.ts +56 -0
  315. package/src/lib/thumbnailGenerator.ts +170 -168
  316. package/src/middleware/auth.ts +80 -137
  317. package/src/models/agenda.ts +46 -0
  318. package/src/models/announcement.ts +134 -0
  319. package/src/models/assignment.ts +322 -0
  320. package/src/models/attendance.ts +208 -0
  321. package/src/models/auth.ts +247 -0
  322. package/src/models/class.ts +703 -0
  323. package/src/models/comment.ts +152 -0
  324. package/src/models/conversation.ts +200 -0
  325. package/src/models/event.ts +177 -0
  326. package/src/models/file.ts +129 -0
  327. package/src/models/folder.ts +225 -0
  328. package/src/models/labChat.ts +213 -0
  329. package/src/models/marketing.ts +45 -0
  330. package/src/models/message.ts +153 -0
  331. package/src/models/newtonChat.ts +70 -0
  332. package/src/models/notification.ts +54 -0
  333. package/src/models/section.ts +98 -0
  334. package/src/models/user.ts +47 -0
  335. package/src/models/worksheet.ts +294 -0
  336. package/src/pipelines/aiLabChat.ts +684 -0
  337. package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +9 -5
  338. package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
  339. package/src/routers/agenda.ts +3 -66
  340. package/src/routers/announcement.ts +54 -495
  341. package/src/routers/assignment.ts +126 -2018
  342. package/src/routers/attendance.ts +15 -276
  343. package/src/routers/auth.ts +79 -442
  344. package/src/routers/class.ts +263 -1187
  345. package/src/routers/comment.ts +61 -288
  346. package/src/routers/conversation.ts +51 -360
  347. package/src/routers/event.ts +50 -481
  348. package/src/routers/file.ts +45 -368
  349. package/src/routers/folder.ts +107 -836
  350. package/src/routers/labChat.ts +35 -604
  351. package/src/routers/marketing.ts +35 -77
  352. package/src/routers/message.ts +54 -567
  353. package/src/routers/newtonChat.ts +17 -278
  354. package/src/routers/notifications.ts +32 -82
  355. package/src/routers/section.ts +46 -330
  356. package/src/routers/user.ts +49 -227
  357. package/src/routers/worksheet.ts +215 -503
  358. package/src/services/agenda.ts +21 -0
  359. package/src/services/announcement.ts +290 -0
  360. package/src/services/assignment.ts +1198 -0
  361. package/src/services/attendance.ts +85 -0
  362. package/src/services/auth.ts +277 -0
  363. package/src/services/class.ts +629 -0
  364. package/src/services/comment.ts +106 -0
  365. package/src/services/conversation.ts +213 -0
  366. package/src/services/event.ts +231 -0
  367. package/src/services/file.ts +167 -0
  368. package/src/services/folder.ts +316 -0
  369. package/src/services/labChat.ts +458 -0
  370. package/src/services/marketing.ts +57 -0
  371. package/src/services/message.ts +554 -0
  372. package/src/services/newtonChat.ts +222 -0
  373. package/src/services/notification.ts +50 -0
  374. package/src/services/section.ts +283 -0
  375. package/src/services/user.ts +172 -0
  376. package/src/services/worksheet.ts +358 -0
  377. package/src/utils/aiUser.ts +4 -3
  378. package/src/utils/email.ts +5 -3
  379. package/src/utils/generateInviteCode.ts +1 -3
  380. package/src/utils/inference.ts +5 -2
  381. package/src/utils/logger.ts +3 -1
  382. package/src/utils/prismaErrorHandler.ts +3 -0
  383. package/src/utils/prismaWrapper.ts +4 -0
  384. package/tests/globalSetup.ts +62 -0
  385. package/tests/helpers.ts +22 -0
  386. package/tests/middleware/security.test.ts +42 -0
  387. package/tests/routers/agenda.test.ts +138 -0
  388. package/tests/routers/announcement.test.ts +490 -0
  389. package/tests/routers/assignment.test.ts +837 -0
  390. package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
  391. package/tests/routers/auth.test.ts +171 -0
  392. package/tests/{class.test.ts → routers/class.test.ts} +131 -85
  393. package/tests/routers/comment.test.ts +126 -0
  394. package/tests/routers/conversation.test.ts +145 -0
  395. package/tests/{event.test.ts → routers/event.test.ts} +93 -32
  396. package/tests/routers/folder.test.ts +178 -0
  397. package/tests/routers/labChat.test.ts +115 -0
  398. package/tests/routers/marketing.test.ts +59 -0
  399. package/tests/routers/message.test.ts +123 -0
  400. package/tests/routers/notification.test.ts +69 -0
  401. package/tests/{section.test.ts → routers/section.test.ts} +5 -13
  402. package/tests/server/rateLimit.test.ts +73 -0
  403. package/tests/setup.ts +18 -92
  404. package/tests/user.test.ts +9 -31
  405. package/tests/utils/aiUser.test.ts +22 -0
  406. package/tests/utils/generateInviteCode.test.ts +24 -0
  407. package/tests/utils/logger.test.ts +74 -0
  408. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  409. package/tests/utils/prismaWrapper.test.ts +82 -0
  410. package/tests/worksheet.test.ts +181 -0
  411. package/vitest.config.ts +6 -3
  412. package/vitest.unit.config.ts +21 -0
  413. package/TODO.md +0 -2
  414. package/coverage/base.css +0 -224
  415. package/coverage/block-navigation.js +0 -87
  416. package/coverage/clover.xml +0 -12110
  417. package/coverage/coverage-final.json +0 -44
  418. package/coverage/favicon.png +0 -0
  419. package/coverage/index.html +0 -221
  420. package/coverage/prettify.css +0 -1
  421. package/coverage/prettify.js +0 -2
  422. package/coverage/server/index.html +0 -116
  423. package/coverage/server/src/exportType.ts.html +0 -109
  424. package/coverage/server/src/index.html +0 -161
  425. package/coverage/server/src/index.ts.html +0 -1702
  426. package/coverage/server/src/instrument.ts.html +0 -130
  427. package/coverage/server/src/lib/config/env.ts.html +0 -448
  428. package/coverage/server/src/lib/config/index.html +0 -116
  429. package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
  430. package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
  431. package/coverage/server/src/lib/index.html +0 -206
  432. package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
  433. package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
  434. package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
  435. package/coverage/server/src/lib/pusher.ts.html +0 -121
  436. package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
  437. package/coverage/server/src/middleware/auth.ts.html +0 -646
  438. package/coverage/server/src/middleware/index.html +0 -146
  439. package/coverage/server/src/middleware/logging.ts.html +0 -244
  440. package/coverage/server/src/middleware/security.ts.html +0 -271
  441. package/coverage/server/src/routers/_app.ts.html +0 -232
  442. package/coverage/server/src/routers/agenda.ts.html +0 -319
  443. package/coverage/server/src/routers/announcement.ts.html +0 -3481
  444. package/coverage/server/src/routers/assignment.ts.html +0 -7633
  445. package/coverage/server/src/routers/attendance.ts.html +0 -1030
  446. package/coverage/server/src/routers/auth.ts.html +0 -1081
  447. package/coverage/server/src/routers/class.ts.html +0 -3535
  448. package/coverage/server/src/routers/comment.ts.html +0 -991
  449. package/coverage/server/src/routers/conversation.ts.html +0 -982
  450. package/coverage/server/src/routers/event.ts.html +0 -1609
  451. package/coverage/server/src/routers/file.ts.html +0 -1144
  452. package/coverage/server/src/routers/folder.ts.html +0 -2797
  453. package/coverage/server/src/routers/index.html +0 -386
  454. package/coverage/server/src/routers/labChat.ts.html +0 -3073
  455. package/coverage/server/src/routers/marketing.ts.html +0 -340
  456. package/coverage/server/src/routers/message.ts.html +0 -1912
  457. package/coverage/server/src/routers/notifications.ts.html +0 -364
  458. package/coverage/server/src/routers/section.ts.html +0 -1120
  459. package/coverage/server/src/routers/user.ts.html +0 -862
  460. package/coverage/server/src/routers/worksheet.ts.html +0 -1729
  461. package/coverage/server/src/trpc.ts.html +0 -397
  462. package/coverage/server/src/types/index.html +0 -116
  463. package/coverage/server/src/types/trpc.ts.html +0 -127
  464. package/coverage/server/src/utils/aiUser.ts.html +0 -280
  465. package/coverage/server/src/utils/email.ts.html +0 -121
  466. package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
  467. package/coverage/server/src/utils/index.html +0 -206
  468. package/coverage/server/src/utils/inference.ts.html +0 -709
  469. package/coverage/server/src/utils/logger.ts.html +0 -664
  470. package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
  471. package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
  472. package/coverage/server/vitest.config.ts.html +0 -196
  473. package/coverage/sort-arrow-sprite.png +0 -0
  474. package/coverage/sorter.js +0 -210
  475. package/src/lib/notificationHandler.ts +0 -36
  476. package/src/server/pipelines/aiLabChat.ts +0 -507
  477. package/tests/announcement.test.ts +0 -164
  478. package/tests/assignment.test.ts +0 -296
  479. 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({