@studious-lms/server 1.2.52 → 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 (480) 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 +36 -94
  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 +1399 -1271
  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 +140 -68
  143. package/dist/routers/class.d.ts.map +1 -1
  144. package/dist/routers/class.js +82 -1051
  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/aiNewtonChat.d.ts.map +1 -1
  200. package/dist/server/pipelines/aiNewtonChat.js +8 -3
  201. package/dist/server/pipelines/aiNewtonChat.js.map +1 -1
  202. package/dist/server/pipelines/gradeWorksheet.d.ts +6 -6
  203. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
  204. package/dist/server/pipelines/gradeWorksheet.js +12 -5
  205. package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
  206. package/dist/services/agenda.d.ts +100 -0
  207. package/dist/services/agenda.d.ts.map +1 -0
  208. package/dist/services/agenda.js +21 -0
  209. package/dist/services/agenda.js.map +1 -0
  210. package/dist/services/announcement.d.ts +135 -0
  211. package/dist/services/announcement.d.ts.map +1 -0
  212. package/dist/services/announcement.js +223 -0
  213. package/dist/services/announcement.js.map +1 -0
  214. package/dist/services/assignment.d.ts +1462 -0
  215. package/dist/services/assignment.d.ts.map +1 -0
  216. package/dist/services/assignment.js +898 -0
  217. package/dist/services/assignment.js.map +1 -0
  218. package/dist/services/attendance.d.ts +93 -0
  219. package/dist/services/attendance.d.ts.map +1 -0
  220. package/dist/services/attendance.js +61 -0
  221. package/dist/services/attendance.js.map +1 -0
  222. package/dist/services/auth.d.ts +68 -0
  223. package/dist/services/auth.d.ts.map +1 -0
  224. package/dist/services/auth.js +218 -0
  225. package/dist/services/auth.js.map +1 -0
  226. package/dist/services/class.d.ts +621 -0
  227. package/dist/services/class.d.ts.map +1 -0
  228. package/dist/services/class.js +474 -0
  229. package/dist/services/class.js.map +1 -0
  230. package/dist/services/comment.d.ts +100 -0
  231. package/dist/services/comment.d.ts.map +1 -0
  232. package/dist/services/comment.js +83 -0
  233. package/dist/services/comment.js.map +1 -0
  234. package/dist/services/conversation.d.ts +159 -0
  235. package/dist/services/conversation.d.ts.map +1 -0
  236. package/dist/services/conversation.js +138 -0
  237. package/dist/services/conversation.js.map +1 -0
  238. package/dist/services/event.d.ts +216 -0
  239. package/dist/services/event.d.ts.map +1 -0
  240. package/dist/services/event.js +168 -0
  241. package/dist/services/event.js.map +1 -0
  242. package/dist/services/file.d.ts +74 -0
  243. package/dist/services/file.d.ts.map +1 -0
  244. package/dist/services/file.js +133 -0
  245. package/dist/services/file.js.map +1 -0
  246. package/dist/services/folder.d.ts +239 -0
  247. package/dist/services/folder.d.ts.map +1 -0
  248. package/dist/services/folder.js +248 -0
  249. package/dist/services/folder.js.map +1 -0
  250. package/dist/services/labChat.d.ts +165 -0
  251. package/dist/services/labChat.d.ts.map +1 -0
  252. package/dist/services/labChat.js +289 -0
  253. package/dist/services/labChat.js.map +1 -0
  254. package/dist/services/marketing.d.ts +50 -0
  255. package/dist/services/marketing.d.ts.map +1 -0
  256. package/dist/services/marketing.js +32 -0
  257. package/dist/services/marketing.js.map +1 -0
  258. package/dist/services/message.d.ts +95 -0
  259. package/dist/services/message.d.ts.map +1 -0
  260. package/dist/services/message.js +350 -0
  261. package/dist/services/message.js.map +1 -0
  262. package/dist/services/newtonChat.d.ts +22 -0
  263. package/dist/services/newtonChat.d.ts.map +1 -0
  264. package/dist/services/newtonChat.js +174 -0
  265. package/dist/services/newtonChat.js.map +1 -0
  266. package/dist/services/notification.d.ts +65 -0
  267. package/dist/services/notification.d.ts.map +1 -0
  268. package/dist/services/notification.js +33 -0
  269. package/dist/services/notification.js.map +1 -0
  270. package/dist/services/section.d.ts +53 -0
  271. package/dist/services/section.d.ts.map +1 -0
  272. package/dist/services/section.js +199 -0
  273. package/dist/services/section.js.map +1 -0
  274. package/dist/services/user.d.ts +48 -0
  275. package/dist/services/user.d.ts.map +1 -0
  276. package/dist/services/user.js +141 -0
  277. package/dist/services/user.js.map +1 -0
  278. package/dist/services/worksheet.d.ts +239 -0
  279. package/dist/services/worksheet.d.ts.map +1 -0
  280. package/dist/services/worksheet.js +235 -0
  281. package/dist/services/worksheet.js.map +1 -0
  282. package/dist/utils/aiUser.d.ts +1 -3
  283. package/dist/utils/aiUser.d.ts.map +1 -1
  284. package/dist/utils/aiUser.js +6 -5
  285. package/dist/utils/aiUser.js.map +1 -1
  286. package/dist/utils/email.d.ts +3 -0
  287. package/dist/utils/email.d.ts.map +1 -1
  288. package/dist/utils/email.js +7 -4
  289. package/dist/utils/email.js.map +1 -1
  290. package/dist/utils/generateInviteCode.d.ts +1 -2
  291. package/dist/utils/generateInviteCode.d.ts.map +1 -1
  292. package/dist/utils/generateInviteCode.js +3 -4
  293. package/dist/utils/generateInviteCode.js.map +1 -1
  294. package/dist/utils/inference.d.ts +3 -0
  295. package/dist/utils/inference.d.ts.map +1 -1
  296. package/dist/utils/inference.js +7 -4
  297. package/dist/utils/inference.js.map +1 -1
  298. package/dist/utils/logger.d.ts +3 -0
  299. package/dist/utils/logger.d.ts.map +1 -1
  300. package/dist/utils/logger.js +5 -2
  301. package/dist/utils/logger.js.map +1 -1
  302. package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
  303. package/dist/utils/prismaErrorHandler.js +5 -2
  304. package/dist/utils/prismaErrorHandler.js.map +1 -1
  305. package/dist/utils/prismaWrapper.d.ts +1 -0
  306. package/dist/utils/prismaWrapper.d.ts.map +1 -1
  307. package/dist/utils/prismaWrapper.js +6 -2
  308. package/dist/utils/prismaWrapper.js.map +1 -1
  309. package/docker-compose.yml +5 -0
  310. package/package.json +4 -3
  311. package/src/index.ts +119 -12
  312. package/src/lib/config/env.ts +6 -0
  313. package/src/lib/fileUpload.ts +0 -1
  314. package/src/lib/googleCloudStorage.ts +17 -0
  315. package/src/lib/pusher.ts +5 -1
  316. package/src/lib/redis.ts +56 -0
  317. package/src/lib/thumbnailGenerator.ts +170 -168
  318. package/src/middleware/auth.ts +83 -136
  319. package/src/models/agenda.ts +46 -0
  320. package/src/models/announcement.ts +134 -0
  321. package/src/models/assignment.ts +322 -0
  322. package/src/models/attendance.ts +208 -0
  323. package/src/models/auth.ts +247 -0
  324. package/src/models/class.ts +598 -0
  325. package/src/models/comment.ts +152 -0
  326. package/src/models/conversation.ts +200 -0
  327. package/src/models/event.ts +177 -0
  328. package/src/models/file.ts +129 -0
  329. package/src/models/folder.ts +225 -0
  330. package/src/models/labChat.ts +213 -0
  331. package/src/models/marketing.ts +45 -0
  332. package/src/models/message.ts +153 -0
  333. package/src/models/newtonChat.ts +70 -0
  334. package/src/models/notification.ts +54 -0
  335. package/src/models/section.ts +98 -0
  336. package/src/models/user.ts +47 -0
  337. package/src/models/worksheet.ts +294 -0
  338. package/src/{server/pipelines → pipelines}/aiLabChat.ts +11 -7
  339. package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +15 -6
  340. package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
  341. package/src/routers/agenda.ts +3 -66
  342. package/src/routers/announcement.ts +54 -495
  343. package/src/routers/assignment.ts +126 -2018
  344. package/src/routers/attendance.ts +15 -276
  345. package/src/routers/auth.ts +79 -442
  346. package/src/routers/class.ts +263 -1186
  347. package/src/routers/comment.ts +61 -288
  348. package/src/routers/conversation.ts +51 -360
  349. package/src/routers/event.ts +50 -481
  350. package/src/routers/file.ts +45 -368
  351. package/src/routers/folder.ts +107 -836
  352. package/src/routers/labChat.ts +29 -605
  353. package/src/routers/marketing.ts +35 -77
  354. package/src/routers/message.ts +45 -571
  355. package/src/routers/newtonChat.ts +17 -278
  356. package/src/routers/notifications.ts +32 -82
  357. package/src/routers/section.ts +46 -330
  358. package/src/routers/user.ts +49 -227
  359. package/src/routers/worksheet.ts +215 -503
  360. package/src/services/agenda.ts +21 -0
  361. package/src/services/announcement.ts +290 -0
  362. package/src/services/assignment.ts +1198 -0
  363. package/src/services/attendance.ts +85 -0
  364. package/src/services/auth.ts +277 -0
  365. package/src/services/class.ts +622 -0
  366. package/src/services/comment.ts +106 -0
  367. package/src/services/conversation.ts +213 -0
  368. package/src/services/event.ts +231 -0
  369. package/src/services/file.ts +167 -0
  370. package/src/services/folder.ts +316 -0
  371. package/src/services/labChat.ts +352 -0
  372. package/src/services/marketing.ts +57 -0
  373. package/src/services/message.ts +461 -0
  374. package/src/services/newtonChat.ts +222 -0
  375. package/src/services/notification.ts +50 -0
  376. package/src/services/section.ts +283 -0
  377. package/src/services/user.ts +172 -0
  378. package/src/services/worksheet.ts +358 -0
  379. package/src/utils/aiUser.ts +4 -3
  380. package/src/utils/email.ts +5 -3
  381. package/src/utils/generateInviteCode.ts +1 -3
  382. package/src/utils/inference.ts +5 -2
  383. package/src/utils/logger.ts +3 -1
  384. package/src/utils/prismaErrorHandler.ts +3 -0
  385. package/src/utils/prismaWrapper.ts +4 -0
  386. package/tests/globalSetup.ts +62 -0
  387. package/tests/helpers.ts +22 -0
  388. package/tests/middleware/security.test.ts +42 -0
  389. package/tests/routers/agenda.test.ts +138 -0
  390. package/tests/routers/announcement.test.ts +490 -0
  391. package/tests/routers/assignment.test.ts +837 -0
  392. package/tests/{attendance.test.ts → routers/attendance.test.ts} +6 -14
  393. package/tests/routers/auth.test.ts +171 -0
  394. package/tests/{class.test.ts → routers/class.test.ts} +131 -85
  395. package/tests/routers/comment.test.ts +126 -0
  396. package/tests/routers/conversation.test.ts +145 -0
  397. package/tests/{event.test.ts → routers/event.test.ts} +93 -32
  398. package/tests/routers/folder.test.ts +178 -0
  399. package/tests/routers/labChat.test.ts +115 -0
  400. package/tests/routers/marketing.test.ts +59 -0
  401. package/tests/routers/message.test.ts +123 -0
  402. package/tests/routers/notification.test.ts +69 -0
  403. package/tests/{section.test.ts → routers/section.test.ts} +5 -13
  404. package/tests/server/rateLimit.test.ts +73 -0
  405. package/tests/setup.ts +18 -92
  406. package/tests/user.test.ts +9 -31
  407. package/tests/utils/aiUser.test.ts +22 -0
  408. package/tests/utils/generateInviteCode.test.ts +24 -0
  409. package/tests/utils/logger.test.ts +74 -0
  410. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  411. package/tests/utils/prismaWrapper.test.ts +82 -0
  412. package/tests/worksheet.test.ts +181 -0
  413. package/vitest.config.ts +6 -3
  414. package/vitest.unit.config.ts +21 -0
  415. package/TODO.md +0 -2
  416. package/coverage/base.css +0 -224
  417. package/coverage/block-navigation.js +0 -87
  418. package/coverage/clover.xml +0 -12110
  419. package/coverage/coverage-final.json +0 -44
  420. package/coverage/favicon.png +0 -0
  421. package/coverage/index.html +0 -221
  422. package/coverage/prettify.css +0 -1
  423. package/coverage/prettify.js +0 -2
  424. package/coverage/server/index.html +0 -116
  425. package/coverage/server/src/exportType.ts.html +0 -109
  426. package/coverage/server/src/index.html +0 -161
  427. package/coverage/server/src/index.ts.html +0 -1702
  428. package/coverage/server/src/instrument.ts.html +0 -130
  429. package/coverage/server/src/lib/config/env.ts.html +0 -448
  430. package/coverage/server/src/lib/config/index.html +0 -116
  431. package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
  432. package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
  433. package/coverage/server/src/lib/index.html +0 -206
  434. package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
  435. package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
  436. package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
  437. package/coverage/server/src/lib/pusher.ts.html +0 -121
  438. package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
  439. package/coverage/server/src/middleware/auth.ts.html +0 -646
  440. package/coverage/server/src/middleware/index.html +0 -146
  441. package/coverage/server/src/middleware/logging.ts.html +0 -244
  442. package/coverage/server/src/middleware/security.ts.html +0 -271
  443. package/coverage/server/src/routers/_app.ts.html +0 -232
  444. package/coverage/server/src/routers/agenda.ts.html +0 -319
  445. package/coverage/server/src/routers/announcement.ts.html +0 -3481
  446. package/coverage/server/src/routers/assignment.ts.html +0 -7633
  447. package/coverage/server/src/routers/attendance.ts.html +0 -1030
  448. package/coverage/server/src/routers/auth.ts.html +0 -1081
  449. package/coverage/server/src/routers/class.ts.html +0 -3535
  450. package/coverage/server/src/routers/comment.ts.html +0 -991
  451. package/coverage/server/src/routers/conversation.ts.html +0 -982
  452. package/coverage/server/src/routers/event.ts.html +0 -1609
  453. package/coverage/server/src/routers/file.ts.html +0 -1144
  454. package/coverage/server/src/routers/folder.ts.html +0 -2797
  455. package/coverage/server/src/routers/index.html +0 -386
  456. package/coverage/server/src/routers/labChat.ts.html +0 -3073
  457. package/coverage/server/src/routers/marketing.ts.html +0 -340
  458. package/coverage/server/src/routers/message.ts.html +0 -1912
  459. package/coverage/server/src/routers/notifications.ts.html +0 -364
  460. package/coverage/server/src/routers/section.ts.html +0 -1120
  461. package/coverage/server/src/routers/user.ts.html +0 -862
  462. package/coverage/server/src/routers/worksheet.ts.html +0 -1729
  463. package/coverage/server/src/trpc.ts.html +0 -397
  464. package/coverage/server/src/types/index.html +0 -116
  465. package/coverage/server/src/types/trpc.ts.html +0 -127
  466. package/coverage/server/src/utils/aiUser.ts.html +0 -280
  467. package/coverage/server/src/utils/email.ts.html +0 -121
  468. package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
  469. package/coverage/server/src/utils/index.html +0 -206
  470. package/coverage/server/src/utils/inference.ts.html +0 -709
  471. package/coverage/server/src/utils/logger.ts.html +0 -664
  472. package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
  473. package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
  474. package/coverage/server/vitest.config.ts.html +0 -196
  475. package/coverage/sort-arrow-sprite.png +0 -0
  476. package/coverage/sorter.js +0 -210
  477. package/src/lib/notificationHandler.ts +0 -36
  478. package/tests/announcement.test.ts +0 -164
  479. package/tests/assignment.test.ts +0 -296
  480. 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({