@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
@@ -1,13 +1,14 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a7728c55-4567-50bb-acc9-7f592bac0ec1")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a4b6352a-4a02-5817-b542-3f6ab5651e28")}catch(e){}}();
3
3
  import { z } from "zod";
4
4
  import { createTRPCRouter, protectedClassMemberProcedure, protectedTeacherProcedure, protectedProcedure } from "../trpc.js";
5
5
  import { prisma } from "../lib/prisma.js";
6
6
  import { TRPCError } from "@trpc/server";
7
- import { sendNotifications } from "../lib/notificationHandler.js";
8
- import { logger } from "../utils/logger.js";
7
+ import { getAllAnnouncements, getAnnouncement, createAnnouncementRecord, updateAnnouncementRecord, deleteAnnouncementRecord, } from "../services/announcement.js";
8
+ import { findAnnouncementByIdAndClass } from "../models/announcement.js";
9
+ import { findCommentWithAnnouncement, findReactionByUserAndComment, upsertReaction, deleteReactionById, } from "../models/comment.js";
10
+ import { getReactions as getCommentReactions } from "../services/comment.js";
9
11
  import { createDirectUploadFiles, confirmDirectUpload } from "../lib/fileUpload.js";
10
- import { deleteFile } from "../lib/googleCloudStorage.js";
11
12
  // Schema for direct file uploads (no base64 data)
12
13
  const directFileSchema = z.object({
13
14
  name: z.string(),
@@ -25,91 +26,13 @@ const confirmAnnouncementUploadSchema = z.object({
25
26
  uploadSuccess: z.boolean(),
26
27
  errorMessage: z.string().optional(),
27
28
  });
28
- const AnnouncementSelect = {
29
- id: true,
30
- teacher: {
31
- select: {
32
- id: true,
33
- username: true,
34
- profile: {
35
- select: {
36
- displayName: true,
37
- profilePicture: true,
38
- profilePictureThumbnail: true,
39
- },
40
- },
41
- },
42
- },
43
- remarks: true,
44
- createdAt: true,
45
- modifiedAt: true,
46
- attachments: {
47
- select: {
48
- id: true,
49
- name: true,
50
- type: true,
51
- size: true,
52
- path: true,
53
- uploadedAt: true,
54
- thumbnailId: true,
55
- },
56
- },
57
- };
58
29
  export const announcementRouter = createTRPCRouter({
59
30
  getAll: protectedClassMemberProcedure
60
- .input(z.object({
61
- classId: z.string(),
62
- }))
63
- .query(async ({ ctx, input }) => {
64
- const announcements = await prisma.announcement.findMany({
65
- where: {
66
- classId: input.classId,
67
- },
68
- select: {
69
- ...AnnouncementSelect,
70
- _count: {
71
- select: {
72
- comments: true,
73
- },
74
- },
75
- },
76
- orderBy: {
77
- createdAt: 'desc',
78
- },
79
- });
80
- // Transform to include comment count
81
- const announcementsWithCounts = announcements.map(announcement => ({
82
- ...announcement,
83
- commentCount: announcement._count.comments,
84
- _count: undefined,
85
- }));
86
- return {
87
- announcements: announcementsWithCounts,
88
- };
89
- }),
31
+ .input(z.object({ classId: z.string() }))
32
+ .query(({ input }) => getAllAnnouncements(input.classId)),
90
33
  get: protectedClassMemberProcedure
91
- .input(z.object({
92
- id: z.string(),
93
- classId: z.string(),
94
- }))
95
- .query(async ({ ctx, input }) => {
96
- const announcement = await prisma.announcement.findUnique({
97
- where: {
98
- id: input.id,
99
- classId: input.classId,
100
- },
101
- select: AnnouncementSelect,
102
- });
103
- if (!announcement) {
104
- throw new TRPCError({
105
- code: "NOT_FOUND",
106
- message: "Announcement not found",
107
- });
108
- }
109
- return {
110
- announcement,
111
- };
112
- }),
34
+ .input(z.object({ id: z.string(), classId: z.string() }))
35
+ .query(({ input }) => getAnnouncement(input.id, input.classId)),
113
36
  create: protectedTeacherProcedure
114
37
  .input(z.object({
115
38
  classId: z.string(),
@@ -117,83 +40,12 @@ export const announcementRouter = createTRPCRouter({
117
40
  files: z.array(directFileSchema).optional(),
118
41
  existingFileIds: z.array(z.string()).optional(),
119
42
  }))
120
- .mutation(async ({ ctx, input }) => {
121
- const { classId, remarks, files, existingFileIds } = input;
122
- if (!ctx.user) {
123
- throw new TRPCError({
124
- code: "UNAUTHORIZED",
125
- message: "User must be authenticated",
126
- });
127
- }
128
- const classData = await prisma.class.findUnique({
129
- where: { id: classId },
130
- include: {
131
- students: {
132
- select: { id: true }
133
- }
134
- }
135
- });
136
- if (!classData) {
137
- throw new TRPCError({
138
- code: "NOT_FOUND",
139
- message: "Class not found",
140
- });
141
- }
142
- const announcement = await prisma.announcement.create({
143
- data: {
144
- remarks: remarks,
145
- teacher: {
146
- connect: {
147
- id: ctx.user.id,
148
- },
149
- },
150
- class: {
151
- connect: {
152
- id: classId,
153
- },
154
- },
155
- },
156
- select: AnnouncementSelect,
157
- });
158
- // Handle file attachments
159
- // NOTE: Files are now handled via direct upload endpoints
160
- // The files field in the schema is for metadata only
161
- // Actual file uploads should use getAnnouncementUploadUrls endpoint
162
- // However, if files are provided here, we create the file records and return upload URLs
163
- let directUploadFiles = [];
164
- if (files && files.length > 0) {
165
- // Create direct upload files - this creates file records with upload URLs
166
- // Files are automatically connected to the announcement via announcementId
167
- directUploadFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, undefined, undefined, announcement.id);
168
- }
169
- // Connect existing files if provided
170
- if (existingFileIds && existingFileIds.length > 0) {
171
- await prisma.announcement.update({
172
- where: { id: announcement.id },
173
- data: {
174
- attachments: {
175
- connect: existingFileIds.map(fileId => ({ id: fileId }))
176
- }
177
- }
178
- });
179
- }
180
- // Fetch announcement with attachments
181
- const announcementWithAttachments = await prisma.announcement.findUnique({
182
- where: { id: announcement.id },
183
- select: AnnouncementSelect,
184
- });
185
- sendNotifications(classData.students.map(student => student.id), {
186
- title: `🔔 Announcement for ${classData.name}`,
187
- content: remarks
188
- }).catch(error => {
189
- logger.error('Failed to send announcement notifications:', error);
190
- });
191
- return {
192
- announcement: announcementWithAttachments || announcement,
193
- // Return upload URLs if files were provided
194
- uploadFiles: directUploadFiles.length > 0 ? directUploadFiles : undefined,
195
- };
196
- }),
43
+ .mutation(({ ctx, input }) => createAnnouncementRecord(ctx.user.id, {
44
+ classId: input.classId,
45
+ remarks: input.remarks,
46
+ files: input.files,
47
+ existingFileIds: input.existingFileIds,
48
+ })),
197
49
  update: protectedTeacherProcedure
198
50
  .input(z.object({
199
51
  id: z.string(),
@@ -205,158 +57,14 @@ export const announcementRouter = createTRPCRouter({
205
57
  removedAttachments: z.array(z.string()).optional(),
206
58
  }),
207
59
  }))
208
- .mutation(async ({ ctx, input }) => {
209
- if (!ctx.user) {
210
- throw new TRPCError({
211
- code: "UNAUTHORIZED",
212
- message: "User must be authenticated",
213
- });
214
- }
215
- const announcement = await prisma.announcement.findUnique({
216
- where: { id: input.id },
217
- include: {
218
- class: {
219
- include: {
220
- teachers: true,
221
- },
222
- },
223
- attachments: {
224
- select: {
225
- id: true,
226
- name: true,
227
- type: true,
228
- path: true,
229
- size: true,
230
- uploadStatus: true,
231
- thumbnail: {
232
- select: {
233
- path: true
234
- }
235
- }
236
- },
237
- },
238
- },
239
- });
240
- if (!announcement) {
241
- throw new TRPCError({
242
- code: "NOT_FOUND",
243
- message: "Announcement not found",
244
- });
245
- }
246
- // Authorization check: user must be the creator OR a teacher in the class
247
- const userId = ctx.user.id;
248
- const isCreator = announcement.teacherId === userId;
249
- const isClassTeacher = announcement.class.teachers.some((teacher) => teacher.id === userId);
250
- if (!isCreator && !isClassTeacher) {
251
- throw new TRPCError({
252
- code: "FORBIDDEN",
253
- message: "Only the announcement creator or class teachers can update announcements",
254
- });
255
- }
256
- // Handle file attachments
257
- // NOTE: Files are now handled via direct upload endpoints
258
- let directUploadFiles = [];
259
- if (input.data.files && input.data.files.length > 0) {
260
- // Create direct upload files - this creates file records with upload URLs
261
- // Files are automatically connected to the announcement via announcementId
262
- directUploadFiles = await createDirectUploadFiles(input.data.files, userId, undefined, undefined, undefined, input.id);
263
- }
264
- // Delete removed attachments from storage before updating database
265
- if (input.data.removedAttachments && input.data.removedAttachments.length > 0) {
266
- const filesToDelete = announcement.attachments.filter((file) => input.data.removedAttachments.includes(file.id));
267
- // Delete files from storage (only if they were actually uploaded)
268
- await Promise.all(filesToDelete.map(async (file) => {
269
- try {
270
- // Only delete from GCS if the file was successfully uploaded
271
- if (file.uploadStatus === 'COMPLETED') {
272
- // Delete the main file
273
- await deleteFile(file.path);
274
- // Delete thumbnail if it exists
275
- if (file.thumbnail?.path) {
276
- await deleteFile(file.thumbnail.path);
277
- }
278
- }
279
- }
280
- catch (error) {
281
- logger.warn(`Failed to delete file ${file.path}:`, {
282
- error: error instanceof Error ? {
283
- name: error.name,
284
- message: error.message,
285
- stack: error.stack,
286
- } : error
287
- });
288
- }
289
- }));
290
- }
291
- const updatedAnnouncement = await prisma.announcement.update({
292
- where: { id: input.id },
293
- data: {
294
- ...(input.data.remarks && { remarks: input.data.remarks }),
295
- // Note: directUploadFiles are already connected via createDirectUploadFiles
296
- ...(input.data.existingFileIds && input.data.existingFileIds.length > 0 && {
297
- attachments: {
298
- connect: input.data.existingFileIds.map(fileId => ({ id: fileId }))
299
- }
300
- }),
301
- ...(input.data.removedAttachments && input.data.removedAttachments.length > 0 && {
302
- attachments: {
303
- deleteMany: {
304
- id: { in: input.data.removedAttachments }
305
- }
306
- }
307
- }),
308
- },
309
- select: AnnouncementSelect,
310
- });
311
- return {
312
- announcement: updatedAnnouncement,
313
- // Return upload URLs if new files were provided
314
- uploadFiles: directUploadFiles.length > 0 ? directUploadFiles : undefined,
315
- };
316
- }),
60
+ .mutation(({ ctx, input }) => updateAnnouncementRecord(ctx.user.id, {
61
+ id: input.id,
62
+ classId: input.classId,
63
+ data: input.data,
64
+ })),
317
65
  delete: protectedTeacherProcedure
318
- .input(z.object({
319
- id: z.string(),
320
- classId: z.string(),
321
- }))
322
- .mutation(async ({ ctx, input }) => {
323
- if (!ctx.user) {
324
- throw new TRPCError({
325
- code: "UNAUTHORIZED",
326
- message: "User must be authenticated",
327
- });
328
- }
329
- const announcement = await prisma.announcement.findUnique({
330
- where: { id: input.id },
331
- include: {
332
- class: {
333
- include: {
334
- teachers: true,
335
- },
336
- },
337
- },
338
- });
339
- if (!announcement) {
340
- throw new TRPCError({
341
- code: "NOT_FOUND",
342
- message: "Announcement not found",
343
- });
344
- }
345
- // Authorization check: user must be the creator OR a teacher in the class
346
- const userId = ctx.user.id;
347
- const isCreator = announcement.teacherId === userId;
348
- const isClassTeacher = announcement.class.teachers.some((teacher) => teacher.id === userId);
349
- if (!isCreator && !isClassTeacher) {
350
- throw new TRPCError({
351
- code: "FORBIDDEN",
352
- message: "Only the announcement creator or class teachers can delete announcements",
353
- });
354
- }
355
- await prisma.announcement.delete({
356
- where: { id: input.id },
357
- });
358
- return { success: true };
359
- }),
66
+ .input(z.object({ id: z.string(), classId: z.string() }))
67
+ .mutation(({ ctx, input }) => deleteAnnouncementRecord(ctx.user.id, input.id, input.classId)),
360
68
  getAnnouncementUploadUrls: protectedTeacherProcedure
361
69
  .input(getAnnouncementUploadUrlsSchema)
362
70
  .mutation(async ({ ctx, input }) => {
@@ -384,13 +92,7 @@ export const announcementRouter = createTRPCRouter({
384
92
  message: "Class not found or you are not a teacher",
385
93
  });
386
94
  }
387
- // Verify announcement exists and belongs to the class
388
- const announcement = await prisma.announcement.findFirst({
389
- where: {
390
- id: announcementId,
391
- classId: classId,
392
- },
393
- });
95
+ const announcement = await findAnnouncementByIdAndClass(announcementId, classId);
394
96
  if (!announcement) {
395
97
  throw new TRPCError({
396
98
  code: "NOT_FOUND",
@@ -454,13 +156,7 @@ export const announcementRouter = createTRPCRouter({
454
156
  message: "User must be authenticated",
455
157
  });
456
158
  }
457
- // Verify announcement exists and belongs to the class
458
- const announcement = await prisma.announcement.findFirst({
459
- where: {
460
- id: input.announcementId,
461
- classId: input.classId,
462
- },
463
- });
159
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
464
160
  if (!announcement) {
465
161
  throw new TRPCError({
466
162
  code: "NOT_FOUND",
@@ -618,13 +314,7 @@ export const announcementRouter = createTRPCRouter({
618
314
  classId: z.string(),
619
315
  }))
620
316
  .query(async ({ ctx, input }) => {
621
- // Verify announcement exists and belongs to the class
622
- const announcement = await prisma.announcement.findFirst({
623
- where: {
624
- id: input.announcementId,
625
- classId: input.classId,
626
- },
627
- });
317
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
628
318
  if (!announcement) {
629
319
  throw new TRPCError({
630
320
  code: "NOT_FOUND",
@@ -707,14 +397,8 @@ export const announcementRouter = createTRPCRouter({
707
397
  });
708
398
  }
709
399
  const userId = ctx.user.id;
710
- // Verify the announcement or comment exists and belongs to the class
711
400
  if (input.announcementId) {
712
- const announcement = await prisma.announcement.findFirst({
713
- where: {
714
- id: input.announcementId,
715
- classId: input.classId,
716
- },
717
- });
401
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
718
402
  if (!announcement) {
719
403
  throw new TRPCError({
720
404
  code: "NOT_FOUND",
@@ -756,17 +440,7 @@ export const announcementRouter = createTRPCRouter({
756
440
  return { reaction };
757
441
  }
758
442
  else if (input.commentId) {
759
- // Verify comment exists and get its announcement to check class
760
- const comment = await prisma.comment.findUnique({
761
- where: { id: input.commentId },
762
- include: {
763
- announcement: {
764
- select: {
765
- classId: true,
766
- },
767
- },
768
- },
769
- });
443
+ const comment = await findCommentWithAnnouncement(input.commentId);
770
444
  if (!comment) {
771
445
  throw new TRPCError({
772
446
  code: "NOT_FOUND",
@@ -779,37 +453,10 @@ export const announcementRouter = createTRPCRouter({
779
453
  message: "Comment does not belong to this class",
780
454
  });
781
455
  }
782
- // Upsert reaction: update if exists, create if not
783
- const reaction = await prisma.reaction.upsert({
784
- where: {
785
- userId_commentId: {
786
- userId,
787
- commentId: input.commentId,
788
- },
789
- },
790
- update: {
791
- type: input.type,
792
- },
793
- create: {
794
- type: input.type,
795
- userId,
796
- commentId: input.commentId,
797
- },
798
- include: {
799
- user: {
800
- select: {
801
- id: true,
802
- username: true,
803
- profile: {
804
- select: {
805
- displayName: true,
806
- profilePicture: true,
807
- profilePictureThumbnail: true,
808
- },
809
- },
810
- },
811
- },
812
- },
456
+ const reaction = await upsertReaction({
457
+ userId,
458
+ commentId: input.commentId,
459
+ type: input.type,
813
460
  });
814
461
  return { reaction };
815
462
  }
@@ -859,23 +506,14 @@ export const announcementRouter = createTRPCRouter({
859
506
  return { success: true };
860
507
  }
861
508
  else if (input.commentId) {
862
- const reaction = await prisma.reaction.findUnique({
863
- where: {
864
- userId_commentId: {
865
- userId,
866
- commentId: input.commentId,
867
- },
868
- },
869
- });
509
+ const reaction = await findReactionByUserAndComment(userId, input.commentId);
870
510
  if (!reaction) {
871
511
  throw new TRPCError({
872
512
  code: "NOT_FOUND",
873
513
  message: "Reaction not found",
874
514
  });
875
515
  }
876
- await prisma.reaction.delete({
877
- where: { id: reaction.id },
878
- });
516
+ await deleteReactionById(reaction.id);
879
517
  return { success: true };
880
518
  }
881
519
  throw new TRPCError({
@@ -905,13 +543,7 @@ export const announcementRouter = createTRPCRouter({
905
543
  }
906
544
  const userId = ctx.user.id;
907
545
  if (input.announcementId) {
908
- // Verify announcement exists
909
- const announcement = await prisma.announcement.findFirst({
910
- where: {
911
- id: input.announcementId,
912
- classId: input.classId,
913
- },
914
- });
546
+ const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
915
547
  if (!announcement) {
916
548
  throw new TRPCError({
917
549
  code: "NOT_FOUND",
@@ -952,17 +584,7 @@ export const announcementRouter = createTRPCRouter({
952
584
  };
953
585
  }
954
586
  else if (input.commentId) {
955
- // Verify comment exists
956
- const comment = await prisma.comment.findUnique({
957
- where: { id: input.commentId },
958
- include: {
959
- announcement: {
960
- select: {
961
- classId: true,
962
- },
963
- },
964
- },
965
- });
587
+ const comment = await findCommentWithAnnouncement(input.commentId);
966
588
  if (!comment) {
967
589
  throw new TRPCError({
968
590
  code: "NOT_FOUND",
@@ -975,38 +597,7 @@ export const announcementRouter = createTRPCRouter({
975
597
  message: "Comment does not belong to this class",
976
598
  });
977
599
  }
978
- // Get reaction counts by type
979
- const reactionCounts = await prisma.reaction.groupBy({
980
- by: ['type'],
981
- where: { commentId: input.commentId },
982
- _count: { type: true },
983
- });
984
- // Get current user's reaction
985
- const userReaction = await prisma.reaction.findUnique({
986
- where: {
987
- userId_commentId: {
988
- userId,
989
- commentId: input.commentId,
990
- },
991
- },
992
- });
993
- // Format counts
994
- const counts = {
995
- THUMBSUP: 0,
996
- CELEBRATE: 0,
997
- CARE: 0,
998
- HEART: 0,
999
- IDEA: 0,
1000
- HAPPY: 0,
1001
- };
1002
- reactionCounts.forEach((item) => {
1003
- counts[item.type] = item._count.type;
1004
- });
1005
- return {
1006
- counts,
1007
- userReaction: userReaction?.type || null,
1008
- total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),
1009
- };
600
+ return getCommentReactions(userId, input.commentId);
1010
601
  }
1011
602
  throw new TRPCError({
1012
603
  code: "INTERNAL_SERVER_ERROR",
@@ -1015,4 +606,4 @@ export const announcementRouter = createTRPCRouter({
1015
606
  }),
1016
607
  });
1017
608
  //# sourceMappingURL=announcement.js.map
1018
- //# debugId=a7728c55-4567-50bb-acc9-7f592bac0ec1
609
+ //# debugId=a4b6352a-4a02-5817-b542-3f6ab5651e28