@studious-lms/server 1.1.24 → 1.2.6

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 (485) hide show
  1. package/.coderabbit.yaml +9 -0
  2. package/.env.example +53 -0
  3. package/.env.test.example +37 -0
  4. package/README.md +34 -7
  5. package/dist/exportType.d.ts.map +1 -1
  6. package/dist/exportType.js +4 -0
  7. package/dist/exportType.js.map +1 -0
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +212 -51
  11. package/dist/index.js.map +1 -0
  12. package/dist/instrument.d.ts +2 -0
  13. package/dist/instrument.d.ts.map +1 -0
  14. package/dist/instrument.js +18 -0
  15. package/dist/instrument.js.map +1 -0
  16. package/dist/lib/config/env.d.ts +190 -0
  17. package/dist/lib/config/env.d.ts.map +1 -0
  18. package/dist/lib/config/env.js +121 -0
  19. package/dist/lib/config/env.js.map +1 -0
  20. package/dist/lib/fileUpload.d.ts +2 -2
  21. package/dist/lib/fileUpload.d.ts.map +1 -1
  22. package/dist/lib/fileUpload.js +82 -15
  23. package/dist/lib/fileUpload.js.map +1 -0
  24. package/dist/lib/googleCloudStorage.d.ts +13 -0
  25. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  26. package/dist/lib/googleCloudStorage.js +45 -6
  27. package/dist/lib/googleCloudStorage.js.map +1 -0
  28. package/dist/lib/jsonConversion.d.ts.map +1 -1
  29. package/dist/lib/jsonConversion.js +16 -14
  30. package/dist/lib/jsonConversion.js.map +1 -0
  31. package/dist/lib/jsonStyles.d.ts.map +1 -1
  32. package/dist/lib/jsonStyles.js +4 -0
  33. package/dist/lib/jsonStyles.js.map +1 -0
  34. package/dist/lib/notificationHandler.d.ts +25 -0
  35. package/dist/lib/notificationHandler.d.ts.map +1 -0
  36. package/dist/lib/notificationHandler.js +32 -0
  37. package/dist/lib/notificationHandler.js.map +1 -0
  38. package/dist/lib/prisma.d.ts +2 -2
  39. package/dist/lib/prisma.d.ts.map +1 -1
  40. package/dist/lib/prisma.js +24 -1
  41. package/dist/lib/prisma.js.map +1 -0
  42. package/dist/lib/pusher.d.ts +4 -1
  43. package/dist/lib/pusher.d.ts.map +1 -1
  44. package/dist/lib/pusher.js +14 -6
  45. package/dist/lib/pusher.js.map +1 -0
  46. package/dist/lib/redis.d.ts +5 -0
  47. package/dist/lib/redis.d.ts.map +1 -0
  48. package/dist/lib/redis.js +53 -0
  49. package/dist/lib/redis.js.map +1 -0
  50. package/dist/lib/thumbnailGenerator.d.ts +0 -21
  51. package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
  52. package/dist/lib/thumbnailGenerator.js +159 -158
  53. package/dist/lib/thumbnailGenerator.js.map +1 -0
  54. package/dist/middleware/auth.d.ts.map +1 -1
  55. package/dist/middleware/auth.js +41 -93
  56. package/dist/middleware/auth.js.map +1 -0
  57. package/dist/middleware/logging.d.ts.map +1 -1
  58. package/dist/middleware/logging.js +4 -0
  59. package/dist/middleware/logging.js.map +1 -0
  60. package/dist/middleware/security.d.ts +5 -0
  61. package/dist/middleware/security.d.ts.map +1 -0
  62. package/dist/middleware/security.js +77 -0
  63. package/dist/middleware/security.js.map +1 -0
  64. package/dist/models/agenda.d.ts +97 -0
  65. package/dist/models/agenda.d.ts.map +1 -0
  66. package/dist/models/agenda.js +40 -0
  67. package/dist/models/agenda.js.map +1 -0
  68. package/dist/models/announcement.d.ts +223 -0
  69. package/dist/models/announcement.d.ts.map +1 -0
  70. package/dist/models/announcement.js +120 -0
  71. package/dist/models/announcement.js.map +1 -0
  72. package/dist/models/assignment.d.ts +1292 -0
  73. package/dist/models/assignment.d.ts.map +1 -0
  74. package/dist/models/assignment.js +309 -0
  75. package/dist/models/assignment.js.map +1 -0
  76. package/dist/models/attendance.d.ts +180 -0
  77. package/dist/models/attendance.d.ts.map +1 -0
  78. package/dist/models/attendance.js +188 -0
  79. package/dist/models/attendance.js.map +1 -0
  80. package/dist/models/auth.d.ts +153 -0
  81. package/dist/models/auth.d.ts.map +1 -0
  82. package/dist/models/auth.js +217 -0
  83. package/dist/models/auth.js.map +1 -0
  84. package/dist/models/class.d.ts +439 -0
  85. package/dist/models/class.d.ts.map +1 -0
  86. package/dist/models/class.js +546 -0
  87. package/dist/models/class.js.map +1 -0
  88. package/dist/models/comment.d.ts +171 -0
  89. package/dist/models/comment.d.ts.map +1 -0
  90. package/dist/models/comment.js +138 -0
  91. package/dist/models/comment.js.map +1 -0
  92. package/dist/models/conversation.d.ts +164 -0
  93. package/dist/models/conversation.d.ts.map +1 -0
  94. package/dist/models/conversation.js +175 -0
  95. package/dist/models/conversation.js.map +1 -0
  96. package/dist/models/event.d.ts +295 -0
  97. package/dist/models/event.d.ts.map +1 -0
  98. package/dist/models/event.js +145 -0
  99. package/dist/models/event.js.map +1 -0
  100. package/dist/models/file.d.ts +536 -0
  101. package/dist/models/file.d.ts.map +1 -0
  102. package/dist/models/file.js +126 -0
  103. package/dist/models/file.js.map +1 -0
  104. package/dist/models/folder.d.ts +295 -0
  105. package/dist/models/folder.d.ts.map +1 -0
  106. package/dist/models/folder.js +202 -0
  107. package/dist/models/folder.js.map +1 -0
  108. package/dist/models/labChat.d.ts +243 -0
  109. package/dist/models/labChat.d.ts.map +1 -0
  110. package/dist/models/labChat.js +204 -0
  111. package/dist/models/labChat.js.map +1 -0
  112. package/dist/models/marketing.d.ts +72 -0
  113. package/dist/models/marketing.d.ts.map +1 -0
  114. package/dist/models/marketing.js +26 -0
  115. package/dist/models/marketing.js.map +1 -0
  116. package/dist/models/message.d.ts +100 -0
  117. package/dist/models/message.d.ts.map +1 -0
  118. package/dist/models/message.js +131 -0
  119. package/dist/models/message.js.map +1 -0
  120. package/dist/models/newtonChat.d.ts +72 -0
  121. package/dist/models/newtonChat.d.ts.map +1 -0
  122. package/dist/models/newtonChat.js +61 -0
  123. package/dist/models/newtonChat.js.map +1 -0
  124. package/dist/models/notification.d.ts +65 -0
  125. package/dist/models/notification.d.ts.map +1 -0
  126. package/dist/models/notification.js +46 -0
  127. package/dist/models/notification.js.map +1 -0
  128. package/dist/models/section.d.ts +102 -0
  129. package/dist/models/section.d.ts.map +1 -0
  130. package/dist/models/section.js +83 -0
  131. package/dist/models/section.js.map +1 -0
  132. package/dist/models/user.d.ts +39 -0
  133. package/dist/models/user.d.ts.map +1 -0
  134. package/dist/models/user.js +38 -0
  135. package/dist/models/user.js.map +1 -0
  136. package/dist/models/worksheet.d.ts +460 -0
  137. package/dist/models/worksheet.d.ts.map +1 -0
  138. package/dist/models/worksheet.js +200 -0
  139. package/dist/models/worksheet.js.map +1 -0
  140. package/dist/pipelines/aiLabChat.d.ts +21 -0
  141. package/dist/pipelines/aiLabChat.d.ts.map +1 -0
  142. package/dist/pipelines/aiLabChat.js +460 -0
  143. package/dist/pipelines/aiLabChat.js.map +1 -0
  144. package/dist/pipelines/aiNewtonChat.d.ts +30 -0
  145. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -0
  146. package/dist/pipelines/aiNewtonChat.js +289 -0
  147. package/dist/pipelines/aiNewtonChat.js.map +1 -0
  148. package/dist/pipelines/gradeWorksheet.d.ts +30 -0
  149. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -0
  150. package/dist/pipelines/gradeWorksheet.js +252 -0
  151. package/dist/pipelines/gradeWorksheet.js.map +1 -0
  152. package/dist/routers/_app.d.ts +6403 -3741
  153. package/dist/routers/_app.d.ts.map +1 -1
  154. package/dist/routers/_app.js +10 -0
  155. package/dist/routers/_app.js.map +1 -0
  156. package/dist/routers/agenda.d.ts +58 -6
  157. package/dist/routers/agenda.d.ts.map +1 -1
  158. package/dist/routers/agenda.js +6 -58
  159. package/dist/routers/agenda.js.map +1 -0
  160. package/dist/routers/announcement.d.ts +325 -6
  161. package/dist/routers/announcement.d.ts.map +1 -1
  162. package/dist/routers/announcement.js +547 -57
  163. package/dist/routers/announcement.js.map +1 -0
  164. package/dist/routers/assignment.d.ts +431 -318
  165. package/dist/routers/assignment.d.ts.map +1 -1
  166. package/dist/routers/assignment.js +104 -1559
  167. package/dist/routers/assignment.js.map +1 -0
  168. package/dist/routers/attendance.d.ts +20 -9
  169. package/dist/routers/attendance.d.ts.map +1 -1
  170. package/dist/routers/attendance.js +10 -263
  171. package/dist/routers/attendance.js.map +1 -0
  172. package/dist/routers/auth.d.ts +21 -1
  173. package/dist/routers/auth.d.ts.map +1 -1
  174. package/dist/routers/auth.js +37 -241
  175. package/dist/routers/auth.js.map +1 -0
  176. package/dist/routers/class.d.ts +198 -68
  177. package/dist/routers/class.d.ts.map +1 -1
  178. package/dist/routers/class.js +88 -909
  179. package/dist/routers/class.js.map +1 -0
  180. package/dist/routers/comment.d.ts +153 -0
  181. package/dist/routers/comment.d.ts.map +1 -0
  182. package/dist/routers/comment.js +58 -0
  183. package/dist/routers/comment.js.map +1 -0
  184. package/dist/routers/conversation.d.ts +73 -3
  185. package/dist/routers/conversation.d.ts.map +1 -1
  186. package/dist/routers/conversation.js +23 -265
  187. package/dist/routers/conversation.js.map +1 -0
  188. package/dist/routers/event.d.ts +46 -37
  189. package/dist/routers/event.d.ts.map +1 -1
  190. package/dist/routers/event.js +15 -431
  191. package/dist/routers/event.js.map +1 -0
  192. package/dist/routers/file.d.ts +4 -2
  193. package/dist/routers/file.d.ts.map +1 -1
  194. package/dist/routers/file.js +11 -295
  195. package/dist/routers/file.js.map +1 -0
  196. package/dist/routers/folder.d.ts +21 -14
  197. package/dist/routers/folder.d.ts.map +1 -1
  198. package/dist/routers/folder.js +36 -743
  199. package/dist/routers/folder.js.map +1 -0
  200. package/dist/routers/labChat.d.ts +12 -9
  201. package/dist/routers/labChat.d.ts.map +1 -1
  202. package/dist/routers/labChat.js +21 -877
  203. package/dist/routers/labChat.js.map +1 -0
  204. package/dist/routers/marketing.d.ts +2 -2
  205. package/dist/routers/marketing.d.ts.map +1 -1
  206. package/dist/routers/marketing.js +9 -54
  207. package/dist/routers/marketing.js.map +1 -0
  208. package/dist/routers/message.d.ts +2 -1
  209. package/dist/routers/message.d.ts.map +1 -1
  210. package/dist/routers/message.js +29 -519
  211. package/dist/routers/message.js.map +1 -0
  212. package/dist/routers/newtonChat.d.ts +55 -0
  213. package/dist/routers/newtonChat.d.ts.map +1 -0
  214. package/dist/routers/newtonChat.js +22 -0
  215. package/dist/routers/newtonChat.js.map +1 -0
  216. package/dist/routers/notifications.d.ts +8 -8
  217. package/dist/routers/notifications.d.ts.map +1 -1
  218. package/dist/routers/notifications.js +20 -81
  219. package/dist/routers/notifications.js.map +1 -0
  220. package/dist/routers/section.d.ts +37 -6
  221. package/dist/routers/section.d.ts.map +1 -1
  222. package/dist/routers/section.js +26 -167
  223. package/dist/routers/section.js.map +1 -0
  224. package/dist/routers/user.d.ts +1 -1
  225. package/dist/routers/user.d.ts.map +1 -1
  226. package/dist/routers/user.js +34 -204
  227. package/dist/routers/user.js.map +1 -0
  228. package/dist/routers/worksheet.d.ts +362 -0
  229. package/dist/routers/worksheet.d.ts.map +1 -0
  230. package/dist/routers/worksheet.js +153 -0
  231. package/dist/routers/worksheet.js.map +1 -0
  232. package/dist/seedDatabase.d.ts +2 -3
  233. package/dist/seedDatabase.d.ts.map +1 -1
  234. package/dist/seedDatabase.js +311 -289
  235. package/dist/seedDatabase.js.map +1 -0
  236. package/dist/server/pipelines/aiLabChat.d.ts +21 -0
  237. package/dist/server/pipelines/aiLabChat.d.ts.map +1 -0
  238. package/dist/server/pipelines/aiLabChat.js +456 -0
  239. package/dist/server/pipelines/aiLabChat.js.map +1 -0
  240. package/dist/server/pipelines/aiNewtonChat.d.ts +30 -0
  241. package/dist/server/pipelines/aiNewtonChat.d.ts.map +1 -0
  242. package/dist/server/pipelines/aiNewtonChat.js +285 -0
  243. package/dist/server/pipelines/aiNewtonChat.js.map +1 -0
  244. package/dist/server/pipelines/gradeWorksheet.d.ts +30 -0
  245. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -0
  246. package/dist/server/pipelines/gradeWorksheet.js +248 -0
  247. package/dist/server/pipelines/gradeWorksheet.js.map +1 -0
  248. package/dist/services/agenda.d.ts +100 -0
  249. package/dist/services/agenda.d.ts.map +1 -0
  250. package/dist/services/agenda.js +21 -0
  251. package/dist/services/agenda.js.map +1 -0
  252. package/dist/services/announcement.d.ts +135 -0
  253. package/dist/services/announcement.d.ts.map +1 -0
  254. package/dist/services/announcement.js +223 -0
  255. package/dist/services/announcement.js.map +1 -0
  256. package/dist/services/assignment.d.ts +1462 -0
  257. package/dist/services/assignment.d.ts.map +1 -0
  258. package/dist/services/assignment.js +898 -0
  259. package/dist/services/assignment.js.map +1 -0
  260. package/dist/services/attendance.d.ts +93 -0
  261. package/dist/services/attendance.d.ts.map +1 -0
  262. package/dist/services/attendance.js +61 -0
  263. package/dist/services/attendance.js.map +1 -0
  264. package/dist/services/auth.d.ts +68 -0
  265. package/dist/services/auth.d.ts.map +1 -0
  266. package/dist/services/auth.js +218 -0
  267. package/dist/services/auth.js.map +1 -0
  268. package/dist/services/class.d.ts +621 -0
  269. package/dist/services/class.d.ts.map +1 -0
  270. package/dist/services/class.js +474 -0
  271. package/dist/services/class.js.map +1 -0
  272. package/dist/services/comment.d.ts +100 -0
  273. package/dist/services/comment.d.ts.map +1 -0
  274. package/dist/services/comment.js +83 -0
  275. package/dist/services/comment.js.map +1 -0
  276. package/dist/services/conversation.d.ts +159 -0
  277. package/dist/services/conversation.d.ts.map +1 -0
  278. package/dist/services/conversation.js +138 -0
  279. package/dist/services/conversation.js.map +1 -0
  280. package/dist/services/event.d.ts +216 -0
  281. package/dist/services/event.d.ts.map +1 -0
  282. package/dist/services/event.js +168 -0
  283. package/dist/services/event.js.map +1 -0
  284. package/dist/services/file.d.ts +74 -0
  285. package/dist/services/file.d.ts.map +1 -0
  286. package/dist/services/file.js +133 -0
  287. package/dist/services/file.js.map +1 -0
  288. package/dist/services/folder.d.ts +239 -0
  289. package/dist/services/folder.d.ts.map +1 -0
  290. package/dist/services/folder.js +248 -0
  291. package/dist/services/folder.js.map +1 -0
  292. package/dist/services/labChat.d.ts +165 -0
  293. package/dist/services/labChat.d.ts.map +1 -0
  294. package/dist/services/labChat.js +289 -0
  295. package/dist/services/labChat.js.map +1 -0
  296. package/dist/services/marketing.d.ts +50 -0
  297. package/dist/services/marketing.d.ts.map +1 -0
  298. package/dist/services/marketing.js +32 -0
  299. package/dist/services/marketing.js.map +1 -0
  300. package/dist/services/message.d.ts +95 -0
  301. package/dist/services/message.d.ts.map +1 -0
  302. package/dist/services/message.js +350 -0
  303. package/dist/services/message.js.map +1 -0
  304. package/dist/services/newtonChat.d.ts +22 -0
  305. package/dist/services/newtonChat.d.ts.map +1 -0
  306. package/dist/services/newtonChat.js +174 -0
  307. package/dist/services/newtonChat.js.map +1 -0
  308. package/dist/services/notification.d.ts +65 -0
  309. package/dist/services/notification.d.ts.map +1 -0
  310. package/dist/services/notification.js +33 -0
  311. package/dist/services/notification.js.map +1 -0
  312. package/dist/services/section.d.ts +53 -0
  313. package/dist/services/section.d.ts.map +1 -0
  314. package/dist/services/section.js +199 -0
  315. package/dist/services/section.js.map +1 -0
  316. package/dist/services/user.d.ts +48 -0
  317. package/dist/services/user.d.ts.map +1 -0
  318. package/dist/services/user.js +141 -0
  319. package/dist/services/user.js.map +1 -0
  320. package/dist/services/worksheet.d.ts +239 -0
  321. package/dist/services/worksheet.d.ts.map +1 -0
  322. package/dist/services/worksheet.js +235 -0
  323. package/dist/services/worksheet.js.map +1 -0
  324. package/dist/socket/handlers.d.ts.map +1 -1
  325. package/dist/socket/handlers.js +4 -0
  326. package/dist/socket/handlers.js.map +1 -0
  327. package/dist/trpc.d.ts.map +1 -1
  328. package/dist/trpc.js +4 -0
  329. package/dist/trpc.js.map +1 -0
  330. package/dist/types/trpc.d.ts.map +1 -1
  331. package/dist/types/trpc.js +4 -0
  332. package/dist/types/trpc.js.map +1 -0
  333. package/dist/utils/aiUser.d.ts +1 -3
  334. package/dist/utils/aiUser.d.ts.map +1 -1
  335. package/dist/utils/aiUser.js +8 -3
  336. package/dist/utils/aiUser.js.map +1 -0
  337. package/dist/utils/email.d.ts +12 -1
  338. package/dist/utils/email.d.ts.map +1 -1
  339. package/dist/utils/email.js +26 -4
  340. package/dist/utils/email.js.map +1 -0
  341. package/dist/utils/generateInviteCode.d.ts +1 -2
  342. package/dist/utils/generateInviteCode.d.ts.map +1 -1
  343. package/dist/utils/generateInviteCode.js +5 -2
  344. package/dist/utils/generateInviteCode.js.map +1 -0
  345. package/dist/utils/inference.d.ts +8 -0
  346. package/dist/utils/inference.d.ts.map +1 -1
  347. package/dist/utils/inference.js +78 -10
  348. package/dist/utils/inference.js.map +1 -0
  349. package/dist/utils/logger.d.ts +4 -0
  350. package/dist/utils/logger.d.ts.map +1 -1
  351. package/dist/utils/logger.js +35 -3
  352. package/dist/utils/logger.js.map +1 -0
  353. package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
  354. package/dist/utils/prismaErrorHandler.js +7 -0
  355. package/dist/utils/prismaErrorHandler.js.map +1 -0
  356. package/dist/utils/prismaWrapper.d.ts +1 -0
  357. package/dist/utils/prismaWrapper.d.ts.map +1 -1
  358. package/dist/utils/prismaWrapper.js +8 -0
  359. package/dist/utils/prismaWrapper.js.map +1 -0
  360. package/docker-compose.yml +19 -0
  361. package/package.json +21 -4
  362. package/prisma/migrations/20251109122857_annuoncements_comments/migration.sql +30 -0
  363. package/prisma/migrations/20251109135555_reactions_announcements_comments/migration.sql +35 -0
  364. package/prisma/schema.prisma +180 -12
  365. package/scripts/test-pre-push.ts +14 -0
  366. package/src/index.ts +247 -52
  367. package/src/instrument.ts +15 -0
  368. package/src/lib/config/env.ts +132 -0
  369. package/src/lib/fileUpload.ts +81 -16
  370. package/src/lib/googleCloudStorage.ts +42 -6
  371. package/src/lib/jsonConversion.ts +12 -14
  372. package/src/lib/prisma.ts +23 -2
  373. package/src/lib/pusher.ts +11 -6
  374. package/src/lib/redis.ts +56 -0
  375. package/src/lib/thumbnailGenerator.ts +170 -168
  376. package/src/middleware/auth.ts +86 -137
  377. package/src/middleware/security.ts +80 -0
  378. package/src/models/agenda.ts +46 -0
  379. package/src/models/announcement.ts +134 -0
  380. package/src/models/assignment.ts +322 -0
  381. package/src/models/attendance.ts +208 -0
  382. package/src/models/auth.ts +247 -0
  383. package/src/models/class.ts +598 -0
  384. package/src/models/comment.ts +152 -0
  385. package/src/models/conversation.ts +200 -0
  386. package/src/models/event.ts +177 -0
  387. package/src/models/file.ts +129 -0
  388. package/src/models/folder.ts +225 -0
  389. package/src/models/labChat.ts +213 -0
  390. package/src/models/marketing.ts +45 -0
  391. package/src/models/message.ts +153 -0
  392. package/src/models/newtonChat.ts +70 -0
  393. package/src/models/notification.ts +54 -0
  394. package/src/models/section.ts +98 -0
  395. package/src/models/user.ts +47 -0
  396. package/src/models/worksheet.ts +294 -0
  397. package/src/pipelines/aiLabChat.ts +511 -0
  398. package/src/pipelines/aiNewtonChat.ts +347 -0
  399. package/src/pipelines/gradeWorksheet.ts +286 -0
  400. package/src/routers/_app.ts +6 -0
  401. package/src/routers/agenda.ts +3 -61
  402. package/src/routers/announcement.ts +622 -57
  403. package/src/routers/assignment.ts +157 -1688
  404. package/src/routers/attendance.ts +16 -277
  405. package/src/routers/auth.ts +79 -313
  406. package/src/routers/class.ts +265 -1038
  407. package/src/routers/comment.ts +76 -0
  408. package/src/routers/conversation.ts +53 -284
  409. package/src/routers/event.ts +50 -481
  410. package/src/routers/file.ts +45 -341
  411. package/src/routers/folder.ts +107 -836
  412. package/src/routers/labChat.ts +29 -960
  413. package/src/routers/marketing.ts +35 -77
  414. package/src/routers/message.ts +45 -571
  415. package/src/routers/newtonChat.ts +36 -0
  416. package/src/routers/notifications.ts +32 -82
  417. package/src/routers/section.ts +58 -200
  418. package/src/routers/user.ts +49 -226
  419. package/src/routers/worksheet.ts +252 -0
  420. package/src/seedDatabase.ts +330 -290
  421. package/src/services/agenda.ts +21 -0
  422. package/src/services/announcement.ts +290 -0
  423. package/src/services/assignment.ts +1198 -0
  424. package/src/services/attendance.ts +85 -0
  425. package/src/services/auth.ts +277 -0
  426. package/src/services/class.ts +622 -0
  427. package/src/services/comment.ts +106 -0
  428. package/src/services/conversation.ts +213 -0
  429. package/src/services/event.ts +231 -0
  430. package/src/services/file.ts +167 -0
  431. package/src/services/folder.ts +316 -0
  432. package/src/services/labChat.ts +352 -0
  433. package/src/services/marketing.ts +57 -0
  434. package/src/services/message.ts +461 -0
  435. package/src/services/newtonChat.ts +222 -0
  436. package/src/services/notification.ts +50 -0
  437. package/src/services/section.ts +283 -0
  438. package/src/services/user.ts +172 -0
  439. package/src/services/worksheet.ts +358 -0
  440. package/src/trpc.ts +4 -0
  441. package/src/utils/aiUser.ts +4 -3
  442. package/src/utils/email.ts +33 -4
  443. package/src/utils/generateInviteCode.ts +1 -3
  444. package/src/utils/inference.ts +89 -10
  445. package/src/utils/logger.ts +33 -3
  446. package/src/utils/prismaErrorHandler.ts +3 -0
  447. package/src/utils/prismaWrapper.ts +4 -0
  448. package/tests/globalSetup.ts +62 -0
  449. package/tests/helpers.ts +22 -0
  450. package/tests/middleware/security.test.ts +42 -0
  451. package/tests/routers/agenda.test.ts +138 -0
  452. package/tests/routers/announcement.test.ts +490 -0
  453. package/tests/routers/assignment.test.ts +837 -0
  454. package/tests/routers/attendance.test.ts +160 -0
  455. package/tests/routers/auth.test.ts +171 -0
  456. package/tests/{class.test.ts → routers/class.test.ts} +163 -92
  457. package/tests/routers/comment.test.ts +126 -0
  458. package/tests/routers/conversation.test.ts +145 -0
  459. package/tests/routers/event.test.ts +289 -0
  460. package/tests/routers/folder.test.ts +178 -0
  461. package/tests/routers/labChat.test.ts +115 -0
  462. package/tests/routers/marketing.test.ts +59 -0
  463. package/tests/routers/message.test.ts +123 -0
  464. package/tests/routers/notification.test.ts +69 -0
  465. package/tests/routers/section.test.ts +208 -0
  466. package/tests/server/rateLimit.test.ts +73 -0
  467. package/tests/setup.ts +39 -65
  468. package/tests/user.test.ts +136 -0
  469. package/tests/utils/aiUser.test.ts +22 -0
  470. package/tests/utils/generateInviteCode.test.ts +24 -0
  471. package/tests/utils/logger.test.ts +74 -0
  472. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  473. package/tests/utils/prismaWrapper.test.ts +82 -0
  474. package/tests/worksheet.test.ts +181 -0
  475. package/tsconfig.json +9 -2
  476. package/vitest.config.ts +30 -1
  477. package/vitest.unit.config.ts +21 -0
  478. package/API_SPECIFICATION.md +0 -1597
  479. package/BASE64_REMOVAL_SUMMARY.md +0 -164
  480. package/CHAT_API_SPEC.md +0 -579
  481. package/LAB_CHAT_API_SPEC.md +0 -518
  482. package/dist/routers/school.d.ts +0 -208
  483. package/dist/routers/school.d.ts.map +0 -1
  484. package/dist/routers/school.js +0 -481
  485. package/tests/auth.test.ts +0 -25
@@ -0,0 +1,65 @@
1
+ /** List notifications for a user. */
2
+ export declare function listNotifications(userId: string): Promise<({
3
+ sender: {
4
+ username: string;
5
+ } | null;
6
+ receiver: {
7
+ username: string;
8
+ };
9
+ } & {
10
+ id: string;
11
+ createdAt: Date;
12
+ title: string;
13
+ content: string;
14
+ read: boolean;
15
+ senderId: string | null;
16
+ receiverId: string;
17
+ })[]>;
18
+ /** Get a single notification by ID. */
19
+ export declare function getNotification(id: string): Promise<({
20
+ sender: {
21
+ username: string;
22
+ } | null;
23
+ receiver: {
24
+ username: string;
25
+ };
26
+ } & {
27
+ id: string;
28
+ createdAt: Date;
29
+ title: string;
30
+ content: string;
31
+ read: boolean;
32
+ senderId: string | null;
33
+ receiverId: string;
34
+ }) | null>;
35
+ /** Send a notification to one user. */
36
+ export declare function sendNotification(input: {
37
+ receiverId: string;
38
+ title: string;
39
+ content: string;
40
+ }): Promise<{
41
+ id: string;
42
+ createdAt: Date;
43
+ title: string;
44
+ content: string;
45
+ read: boolean;
46
+ senderId: string | null;
47
+ receiverId: string;
48
+ }>;
49
+ /** Send the same notification to multiple users. */
50
+ export declare function sendToMultiple(input: {
51
+ receiverIds: string[];
52
+ title: string;
53
+ content: string;
54
+ }): Promise<import(".prisma/client").Prisma.BatchPayload>;
55
+ /** Mark a notification as read. */
56
+ export declare function markAsRead(id: string): Promise<{
57
+ id: string;
58
+ createdAt: Date;
59
+ title: string;
60
+ content: string;
61
+ read: boolean;
62
+ senderId: string | null;
63
+ receiverId: string;
64
+ }>;
65
+ //# sourceMappingURL=notification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.d.ts","sourceRoot":"/","sources":["services/notification.ts"],"names":[],"mappings":"AAWA,qCAAqC;AACrC,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAErD;AAED,uCAAuC;AACvC,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;WAE/C;AAED,uCAAuC;AACvC,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;;;;GAEA;AAED,oDAAoD;AACpD,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,yDASA;AAED,mCAAmC;AACnC,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM;;;;;;;;GAE1C"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Notification service – list, send, and mark notifications as read.
3
+ */
4
+
5
+ !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]="5a0ab2a2-7806-5031-8d1e-f60eb3ade5dc")}catch(e){}}();
6
+ import { findNotificationsByReceiverId, findNotificationById, createNotification, createManyNotifications, markNotificationAsRead, } from "../models/notification.js";
7
+ /** List notifications for a user. */
8
+ export async function listNotifications(userId) {
9
+ return findNotificationsByReceiverId(userId);
10
+ }
11
+ /** Get a single notification by ID. */
12
+ export async function getNotification(id) {
13
+ return findNotificationById(id);
14
+ }
15
+ /** Send a notification to one user. */
16
+ export async function sendNotification(input) {
17
+ return createNotification(input);
18
+ }
19
+ /** Send the same notification to multiple users. */
20
+ export async function sendToMultiple(input) {
21
+ const result = await createManyNotifications(input.receiverIds.map((receiverId) => ({
22
+ receiverId,
23
+ title: input.title,
24
+ content: input.content,
25
+ })));
26
+ return result;
27
+ }
28
+ /** Mark a notification as read. */
29
+ export async function markAsRead(id) {
30
+ return markNotificationAsRead(id);
31
+ }
32
+ //# sourceMappingURL=notification.js.map
33
+ //# debugId=5a0ab2a2-7806-5031-8d1e-f60eb3ade5dc
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.js","sources":["services/notification.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Notification service – list, send, and mark notifications as read.\n */\nimport {\n findNotificationsByReceiverId,\n findNotificationById,\n createNotification,\n createManyNotifications,\n markNotificationAsRead,\n} from \"../models/notification.js\";\n\n/** List notifications for a user. */\nexport async function listNotifications(userId: string) {\n return findNotificationsByReceiverId(userId);\n}\n\n/** Get a single notification by ID. */\nexport async function getNotification(id: string) {\n return findNotificationById(id);\n}\n\n/** Send a notification to one user. */\nexport async function sendNotification(input: {\n receiverId: string;\n title: string;\n content: string;\n}) {\n return createNotification(input);\n}\n\n/** Send the same notification to multiple users. */\nexport async function sendToMultiple(input: {\n receiverIds: string[];\n title: string;\n content: string;\n}) {\n const result = await createManyNotifications(\n input.receiverIds.map((receiverId) => ({\n receiverId,\n title: input.title,\n content: input.content,\n }))\n );\n return result;\n}\n\n/** Mark a notification as read. */\nexport async function markAsRead(id: string) {\n return markNotificationAsRead(id);\n}\n"],"names":[],"mappings":"AAAA;;GAEG;;;AACH,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AAEnC,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,OAAO,6BAA6B,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAU;IAC9C,OAAO,oBAAoB,CAAC,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAItC;IACC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAIpC;IACC,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAC1C,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACrC,UAAU;QACV,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC,CACJ,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mCAAmC;AACnC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAU;IACzC,OAAO,sBAAsB,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC","debug_id":"5a0ab2a2-7806-5031-8d1e-f60eb3ade5dc"}
@@ -0,0 +1,53 @@
1
+ /** Check if a section exists. */
2
+ export declare function sectionExists(id: string): Promise<boolean>;
3
+ /** Create a section and reorder the unified section/assignment list. */
4
+ export declare function createSectionRecord(userId: string, input: {
5
+ classId: string;
6
+ id?: string;
7
+ name: string;
8
+ color?: string;
9
+ }): Promise<{
10
+ id: string;
11
+ classId: string;
12
+ name: string;
13
+ color: string | null;
14
+ order: number | null;
15
+ }>;
16
+ export declare function reorderSection(userId: string, input: {
17
+ classId: string;
18
+ movedId: string;
19
+ position: "start" | "end" | "before" | "after";
20
+ targetId?: string;
21
+ }): Promise<{
22
+ id: string;
23
+ classId: string;
24
+ name: string;
25
+ color: string | null;
26
+ order: number | null;
27
+ } | null>;
28
+ export declare function updateSectionRecord(userId: string, input: {
29
+ id: string;
30
+ classId: string;
31
+ name: string;
32
+ color?: string;
33
+ }): Promise<{
34
+ id: string;
35
+ classId: string;
36
+ name: string;
37
+ color: string | null;
38
+ order: number | null;
39
+ }>;
40
+ export declare function reOrderSection(userId: string, input: {
41
+ id: string;
42
+ classId: string;
43
+ order: number;
44
+ }): Promise<{
45
+ id: string;
46
+ }>;
47
+ export declare function deleteSectionRecord(userId: string, input: {
48
+ id: string;
49
+ classId: string;
50
+ }): Promise<{
51
+ id: string;
52
+ }>;
53
+ //# sourceMappingURL=section.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section.d.ts","sourceRoot":"/","sources":["services/section.ts"],"names":[],"mappings":"AAmBA,iCAAiC;AACjC,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,oBAG7C;AAED,wEAAwE;AACxE,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;GA6CF;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;;;;;;UA8GF;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;GAcF;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;;GA0CtD;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;GAYvC"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Section service – CRUD for class sections. Handles reordering of sections
3
+ * and assignments in a unified list.
4
+ */
5
+
6
+ !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]="038ebd27-6e21-59a9-8d2f-cc4153311ae2")}catch(e){}}();
7
+ import { TRPCError } from "@trpc/server";
8
+ import { findSectionById, findClassWithTeacher, createSection, findSectionsByClassId, findAssignmentsByClassId, updateSectionOrder, updateAssignmentOrder, updateSection, deleteSectionById, findSectionByClassAndId, } from "../models/section.js";
9
+ import { prisma } from "../lib/prisma.js";
10
+ /** Check if a section exists. */
11
+ export async function sectionExists(id) {
12
+ const section = await findSectionById(id);
13
+ return !!section;
14
+ }
15
+ /** Create a section and reorder the unified section/assignment list. */
16
+ export async function createSectionRecord(userId, input) {
17
+ const classData = await findClassWithTeacher(input.classId, userId);
18
+ if (!classData) {
19
+ throw new TRPCError({
20
+ code: "NOT_FOUND",
21
+ message: "Class not found or you are not a teacher",
22
+ });
23
+ }
24
+ const section = await createSection({
25
+ id: input.id,
26
+ name: input.name,
27
+ order: 0,
28
+ classId: input.classId,
29
+ color: input.color,
30
+ });
31
+ const [sections, assignments] = await Promise.all([
32
+ findSectionsByClassId(input.classId),
33
+ findAssignmentsByClassId(input.classId),
34
+ ]);
35
+ const unified = [
36
+ ...sections.map((s) => ({ id: s.id, order: s.order, type: "section" })),
37
+ ...assignments.map((a) => ({ id: a.id, order: a.order, type: "assignment" })),
38
+ ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
39
+ const withoutNew = unified.filter((item) => !(item.id === section.id && item.type === "section"));
40
+ const reindexed = [
41
+ { id: section.id, type: "section" },
42
+ ...withoutNew.map((item) => ({ id: item.id, type: item.type })),
43
+ ];
44
+ await Promise.all(reindexed.map((item, index) => item.type === "section"
45
+ ? updateSectionOrder(item.id, index + 1)
46
+ : updateAssignmentOrder(item.id, index + 1)));
47
+ return section;
48
+ }
49
+ export async function reorderSection(userId, input) {
50
+ const classData = await findClassWithTeacher(input.classId, userId);
51
+ if (!classData) {
52
+ throw new TRPCError({
53
+ code: "UNAUTHORIZED",
54
+ message: "Not authorized",
55
+ });
56
+ }
57
+ const moved = await findSectionByClassAndId(input.movedId, input.classId);
58
+ if (!moved) {
59
+ throw new TRPCError({
60
+ code: "NOT_FOUND",
61
+ message: "Section not found",
62
+ });
63
+ }
64
+ if ((input.position === "before" || input.position === "after") &&
65
+ !input.targetId) {
66
+ throw new TRPCError({
67
+ code: "BAD_REQUEST",
68
+ message: "targetId required for before/after",
69
+ });
70
+ }
71
+ const result = await prisma.$transaction(async (tx) => {
72
+ const [sections, assignments] = await Promise.all([
73
+ tx.section.findMany({
74
+ where: { classId: input.classId },
75
+ select: { id: true, order: true },
76
+ }),
77
+ tx.assignment.findMany({
78
+ where: { classId: input.classId },
79
+ select: { id: true, order: true },
80
+ }),
81
+ ]);
82
+ const unified = [
83
+ ...sections.map((s) => ({ id: s.id, order: s.order, type: "section" })),
84
+ ...assignments.map((a) => ({ id: a.id, order: a.order, type: "assignment" })),
85
+ ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
86
+ const movedIdx = unified.findIndex((item) => item.id === input.movedId && item.type === "section");
87
+ if (movedIdx === -1) {
88
+ throw new TRPCError({
89
+ code: "NOT_FOUND",
90
+ message: "Section not found in unified list",
91
+ });
92
+ }
93
+ const withoutMoved = unified.filter((item) => !(item.id === input.movedId && item.type === "section"));
94
+ let next = [];
95
+ if (input.position === "start") {
96
+ next = [
97
+ { id: input.movedId, type: "section" },
98
+ ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),
99
+ ];
100
+ }
101
+ else if (input.position === "end") {
102
+ next = [
103
+ ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),
104
+ { id: input.movedId, type: "section" },
105
+ ];
106
+ }
107
+ else {
108
+ const targetIdx = withoutMoved.findIndex((item) => item.id === input.targetId);
109
+ if (targetIdx === -1) {
110
+ throw new TRPCError({
111
+ code: "BAD_REQUEST",
112
+ message: "targetId not found in unified list",
113
+ });
114
+ }
115
+ if (input.position === "before") {
116
+ next = [
117
+ ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id, type: item.type })),
118
+ { id: input.movedId, type: "section" },
119
+ ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id, type: item.type })),
120
+ ];
121
+ }
122
+ else {
123
+ next = [
124
+ ...withoutMoved
125
+ .slice(0, targetIdx + 1)
126
+ .map((item) => ({ id: item.id, type: item.type })),
127
+ { id: input.movedId, type: "section" },
128
+ ...withoutMoved
129
+ .slice(targetIdx + 1)
130
+ .map((item) => ({ id: item.id, type: item.type })),
131
+ ];
132
+ }
133
+ }
134
+ await Promise.all(next.map((item, index) => item.type === "section"
135
+ ? tx.section.update({ where: { id: item.id }, data: { order: index + 1 } })
136
+ : tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } })));
137
+ return tx.section.findUnique({ where: { id: input.movedId } });
138
+ });
139
+ return result;
140
+ }
141
+ export async function updateSectionRecord(userId, input) {
142
+ const classData = await findClassWithTeacher(input.classId, userId);
143
+ if (!classData) {
144
+ throw new TRPCError({
145
+ code: "NOT_FOUND",
146
+ message: "Class not found or you are not a teacher",
147
+ });
148
+ }
149
+ return updateSection(input.id, {
150
+ name: input.name,
151
+ color: input.color,
152
+ });
153
+ }
154
+ export async function reOrderSection(userId, input) {
155
+ const classData = await findClassWithTeacher(input.classId, userId);
156
+ if (!classData) {
157
+ throw new TRPCError({
158
+ code: "NOT_FOUND",
159
+ message: "Class not found or you are not a teacher",
160
+ });
161
+ }
162
+ await prisma.$transaction(async (tx) => {
163
+ await tx.section.update({
164
+ where: { id: input.id },
165
+ data: { order: input.order },
166
+ });
167
+ const [sections, assignments] = await Promise.all([
168
+ tx.section.findMany({
169
+ where: { classId: input.classId },
170
+ select: { id: true, order: true },
171
+ }),
172
+ tx.assignment.findMany({
173
+ where: { classId: input.classId },
174
+ select: { id: true, order: true },
175
+ }),
176
+ ]);
177
+ const unified = [
178
+ ...sections.map((s) => ({ id: s.id, order: s.order, type: "section" })),
179
+ ...assignments.map((a) => ({ id: a.id, order: a.order, type: "assignment" })),
180
+ ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
181
+ await Promise.all(unified.map((item, index) => item.type === "section"
182
+ ? tx.section.update({ where: { id: item.id }, data: { order: index + 1 } })
183
+ : tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } })));
184
+ });
185
+ return { id: input.id };
186
+ }
187
+ export async function deleteSectionRecord(userId, input) {
188
+ const classData = await findClassWithTeacher(input.classId, userId);
189
+ if (!classData) {
190
+ throw new TRPCError({
191
+ code: "NOT_FOUND",
192
+ message: "Class not found or you are not a teacher",
193
+ });
194
+ }
195
+ await deleteSectionById(input.id);
196
+ return { id: input.id };
197
+ }
198
+ //# sourceMappingURL=section.js.map
199
+ //# debugId=038ebd27-6e21-59a9-8d2f-cc4153311ae2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section.js","sources":["services/section.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Section service – CRUD for class sections. Handles reordering of sections\n * and assignments in a unified list.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findSectionById,\n findClassWithTeacher,\n createSection,\n findSectionsByClassId,\n findAssignmentsByClassId,\n updateSectionOrder,\n updateAssignmentOrder,\n updateSection,\n deleteSectionById,\n findSectionByClassAndId,\n} from \"../models/section.js\";\nimport { prisma } from \"../lib/prisma.js\";\n\n/** Check if a section exists. */\nexport async function sectionExists(id: string) {\n const section = await findSectionById(id);\n return !!section;\n}\n\n/** Create a section and reorder the unified section/assignment list. */\nexport async function createSectionRecord(\n userId: string,\n input: {\n classId: string;\n id?: string;\n name: string;\n color?: string;\n }\n) {\n const classData = await findClassWithTeacher(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a teacher\",\n });\n }\n\n const section = await createSection({\n id: input.id,\n name: input.name,\n order: 0,\n classId: input.classId,\n color: input.color,\n });\n\n const [sections, assignments] = await Promise.all([\n findSectionsByClassId(input.classId),\n findAssignmentsByClassId(input.classId),\n ]);\n\n const unified = [\n ...sections.map((s) => ({ id: s.id, order: s.order, type: \"section\" as const })),\n ...assignments.map((a) => ({ id: a.id, order: a.order, type: \"assignment\" as const })),\n ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));\n\n const withoutNew = unified.filter(\n (item) => !(item.id === section.id && item.type === \"section\")\n );\n const reindexed = [\n { id: section.id, type: \"section\" as const },\n ...withoutNew.map((item) => ({ id: item.id, type: item.type })),\n ];\n\n await Promise.all(\n reindexed.map((item, index) =>\n item.type === \"section\"\n ? updateSectionOrder(item.id, index + 1)\n : updateAssignmentOrder(item.id, index + 1)\n )\n );\n\n return section;\n}\n\nexport async function reorderSection(\n userId: string,\n input: {\n classId: string;\n movedId: string;\n position: \"start\" | \"end\" | \"before\" | \"after\";\n targetId?: string;\n }\n) {\n const classData = await findClassWithTeacher(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authorized\",\n });\n }\n\n const moved = await findSectionByClassAndId(input.movedId, input.classId);\n if (!moved) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Section not found\",\n });\n }\n\n if (\n (input.position === \"before\" || input.position === \"after\") &&\n !input.targetId\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"targetId required for before/after\",\n });\n }\n\n const result = await prisma.$transaction(async (tx) => {\n const [sections, assignments] = await Promise.all([\n tx.section.findMany({\n where: { classId: input.classId },\n select: { id: true, order: true },\n }),\n tx.assignment.findMany({\n where: { classId: input.classId },\n select: { id: true, order: true },\n }),\n ]);\n\n const unified = [\n ...sections.map((s) => ({ id: s.id, order: s.order, type: \"section\" as const })),\n ...assignments.map((a) => ({ id: a.id, order: a.order, type: \"assignment\" as const })),\n ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));\n\n const movedIdx = unified.findIndex(\n (item) => item.id === input.movedId && item.type === \"section\"\n );\n if (movedIdx === -1) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Section not found in unified list\",\n });\n }\n\n const withoutMoved = unified.filter(\n (item) => !(item.id === input.movedId && item.type === \"section\")\n );\n\n let next: Array<{ id: string; type: \"section\" | \"assignment\" }> = [];\n\n if (input.position === \"start\") {\n next = [\n { id: input.movedId, type: \"section\" },\n ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),\n ];\n } else if (input.position === \"end\") {\n next = [\n ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),\n { id: input.movedId, type: \"section\" },\n ];\n } else {\n const targetIdx = withoutMoved.findIndex((item) => item.id === input.targetId);\n if (targetIdx === -1) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"targetId not found in unified list\",\n });\n }\n if (input.position === \"before\") {\n next = [\n ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id, type: item.type })),\n { id: input.movedId, type: \"section\" },\n ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id, type: item.type })),\n ];\n } else {\n next = [\n ...withoutMoved\n .slice(0, targetIdx + 1)\n .map((item) => ({ id: item.id, type: item.type })),\n { id: input.movedId, type: \"section\" },\n ...withoutMoved\n .slice(targetIdx + 1)\n .map((item) => ({ id: item.id, type: item.type })),\n ];\n }\n }\n\n await Promise.all(\n next.map((item, index) =>\n item.type === \"section\"\n ? tx.section.update({ where: { id: item.id }, data: { order: index + 1 } })\n : tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } })\n )\n );\n\n return tx.section.findUnique({ where: { id: input.movedId } });\n });\n\n return result;\n}\n\nexport async function updateSectionRecord(\n userId: string,\n input: {\n id: string;\n classId: string;\n name: string;\n color?: string;\n }\n) {\n const classData = await findClassWithTeacher(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a teacher\",\n });\n }\n\n return updateSection(input.id, {\n name: input.name,\n color: input.color,\n });\n}\n\nexport async function reOrderSection(\n userId: string,\n input: { id: string; classId: string; order: number }\n) {\n const classData = await findClassWithTeacher(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a teacher\",\n });\n }\n\n await prisma.$transaction(async (tx) => {\n await tx.section.update({\n where: { id: input.id },\n data: { order: input.order },\n });\n\n const [sections, assignments] = await Promise.all([\n tx.section.findMany({\n where: { classId: input.classId },\n select: { id: true, order: true },\n }),\n tx.assignment.findMany({\n where: { classId: input.classId },\n select: { id: true, order: true },\n }),\n ]);\n\n const unified = [\n ...sections.map((s) => ({ id: s.id, order: s.order, type: \"section\" as const })),\n ...assignments.map((a) => ({ id: a.id, order: a.order, type: \"assignment\" as const })),\n ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));\n\n await Promise.all(\n unified.map((item, index) =>\n item.type === \"section\"\n ? tx.section.update({ where: { id: item.id }, data: { order: index + 1 } })\n : tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } })\n )\n );\n });\n\n return { id: input.id };\n}\n\nexport async function deleteSectionRecord(\n userId: string,\n input: { id: string; classId: string }\n) {\n const classData = await findClassWithTeacher(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a teacher\",\n });\n }\n\n await deleteSectionById(input.id);\n return { id: input.id };\n}\n"],"names":[],"mappings":"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","debug_id":"038ebd27-6e21-59a9-8d2f-cc4153311ae2"}
@@ -0,0 +1,48 @@
1
+ /** Get user profile with display info and profile picture URLs. */
2
+ export declare function getProfile(userId: string): Promise<{
3
+ id: string;
4
+ username: string;
5
+ profile: {
6
+ displayName: any;
7
+ bio: any;
8
+ location: any;
9
+ website: any;
10
+ profilePicture: string | null;
11
+ profilePictureThumbnail: string | null;
12
+ };
13
+ }>;
14
+ /** Update user profile (display name, bio, etc.) and/or profile picture. */
15
+ export declare function updateProfile(userId: string, input: {
16
+ profile?: {
17
+ displayName?: string | null;
18
+ bio?: string | null;
19
+ location?: string | null;
20
+ website?: string | null;
21
+ };
22
+ profilePicture?: {
23
+ filePath: string;
24
+ fileName: string;
25
+ fileType: string;
26
+ fileSize: number;
27
+ };
28
+ dicebearAvatar?: {
29
+ url: string;
30
+ };
31
+ }): Promise<{
32
+ id: string;
33
+ username: string;
34
+ profile: {
35
+ displayName: any;
36
+ bio: any;
37
+ location: any;
38
+ website: any;
39
+ profilePicture: string | null;
40
+ profilePictureThumbnail: string | null;
41
+ };
42
+ }>;
43
+ export declare function getUploadUrl(userId: string, fileName: string, fileType: string): Promise<{
44
+ uploadUrl: string;
45
+ filePath: string;
46
+ fileName: string;
47
+ }>;
48
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"/","sources":["services/user.ts"],"names":[],"mappings":"AAiBA,mEAAmE;AACnE,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;GAkC9C;AAED,4EAA4E;AAC5E,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,cAAc,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAClC;;;;;;;;;;;GA8DF;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;;;;GAiCjB"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * User service – profile get/update, profile picture upload URLs.
3
+ */
4
+
5
+ !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]="f3493347-4093-53f8-993b-382ee260ab8f")}catch(e){}}();
6
+ import { TRPCError } from "@trpc/server";
7
+ import { logger } from "../utils/logger.js";
8
+ import { env } from "../lib/config/env.js";
9
+ import { findUserById, findUserProfileByUserId, upsertUserProfile } from "../models/user.js";
10
+ function getFileUrl(filePath) {
11
+ if (!filePath)
12
+ return null;
13
+ if (filePath.startsWith("http")) {
14
+ return filePath;
15
+ }
16
+ const backendUrl = env.BACKEND_URL || "http://localhost:3001";
17
+ return `${backendUrl}/api/files/${encodeURIComponent(filePath)}`;
18
+ }
19
+ /** Get user profile with display info and profile picture URLs. */
20
+ export async function getProfile(userId) {
21
+ const user = await findUserById(userId);
22
+ if (!user) {
23
+ throw new TRPCError({
24
+ code: "NOT_FOUND",
25
+ message: "User not found",
26
+ });
27
+ }
28
+ const userProfile = await findUserProfileByUserId(userId);
29
+ return {
30
+ id: user.id,
31
+ username: user.username,
32
+ profile: userProfile
33
+ ? {
34
+ displayName: userProfile.displayName || null,
35
+ bio: userProfile.bio || null,
36
+ location: userProfile.location || null,
37
+ website: userProfile.website || null,
38
+ profilePicture: getFileUrl(userProfile.profilePicture),
39
+ profilePictureThumbnail: getFileUrl(userProfile.profilePictureThumbnail),
40
+ }
41
+ : {
42
+ displayName: null,
43
+ bio: null,
44
+ location: null,
45
+ website: null,
46
+ profilePicture: null,
47
+ profilePictureThumbnail: null,
48
+ },
49
+ };
50
+ }
51
+ /** Update user profile (display name, bio, etc.) and/or profile picture. */
52
+ export async function updateProfile(userId, input) {
53
+ let profilePictureUrl = null;
54
+ let profilePictureThumbnail = null;
55
+ if (input.profilePicture) {
56
+ try {
57
+ profilePictureUrl = input.profilePicture.filePath;
58
+ profilePictureThumbnail = null;
59
+ }
60
+ catch (error) {
61
+ logger.error("Profile picture processing failed", {
62
+ userId,
63
+ error: error instanceof Error ? error.message : "Unknown error",
64
+ });
65
+ throw new TRPCError({
66
+ code: "INTERNAL_SERVER_ERROR",
67
+ message: "Failed to process profile picture. Please try again.",
68
+ });
69
+ }
70
+ }
71
+ if (input.dicebearAvatar) {
72
+ profilePictureUrl = input.dicebearAvatar.url;
73
+ profilePictureThumbnail = null;
74
+ }
75
+ const updateData = {};
76
+ if (input.profile) {
77
+ if (input.profile.displayName !== undefined && input.profile.displayName !== null) {
78
+ updateData.displayName = input.profile.displayName;
79
+ }
80
+ if (input.profile.bio !== undefined && input.profile.bio !== null) {
81
+ updateData.bio = input.profile.bio;
82
+ }
83
+ if (input.profile.location !== undefined && input.profile.location !== null) {
84
+ updateData.location = input.profile.location;
85
+ }
86
+ if (input.profile.website !== undefined && input.profile.website !== null) {
87
+ updateData.website = input.profile.website;
88
+ }
89
+ }
90
+ if (profilePictureUrl !== null)
91
+ updateData.profilePicture = profilePictureUrl;
92
+ if (profilePictureThumbnail !== null)
93
+ updateData.profilePictureThumbnail = profilePictureThumbnail;
94
+ const updatedProfile = await upsertUserProfile(userId, updateData);
95
+ const user = await findUserById(userId);
96
+ return {
97
+ id: userId,
98
+ username: user?.username || "",
99
+ profile: {
100
+ displayName: updatedProfile.displayName || null,
101
+ bio: updatedProfile.bio || null,
102
+ location: updatedProfile.location || null,
103
+ website: updatedProfile.website || null,
104
+ profilePicture: getFileUrl(updatedProfile.profilePicture),
105
+ profilePictureThumbnail: getFileUrl(updatedProfile.profilePictureThumbnail),
106
+ },
107
+ };
108
+ }
109
+ export async function getUploadUrl(userId, fileName, fileType) {
110
+ try {
111
+ const fileExtension = fileName.split(".").pop();
112
+ const uniqueFilename = `${userId}-${Date.now()}.${fileExtension}`;
113
+ const filePath = `users/${userId}/profile/${uniqueFilename}`;
114
+ const backendUrl = env.BACKEND_URL || "http://localhost:3001";
115
+ const uploadUrl = `${backendUrl}/api/upload/${encodeURIComponent(filePath)}`;
116
+ logger.info("Generated upload URL", {
117
+ userId,
118
+ filePath,
119
+ fileName: uniqueFilename,
120
+ fileType,
121
+ uploadUrl,
122
+ });
123
+ return {
124
+ uploadUrl,
125
+ filePath,
126
+ fileName: uniqueFilename,
127
+ };
128
+ }
129
+ catch (error) {
130
+ logger.error("Failed to generate upload URL", {
131
+ userId,
132
+ error: error instanceof Error ? error.message : "Unknown error",
133
+ });
134
+ throw new TRPCError({
135
+ code: "INTERNAL_SERVER_ERROR",
136
+ message: "Failed to generate upload URL",
137
+ });
138
+ }
139
+ }
140
+ //# sourceMappingURL=user.js.map
141
+ //# debugId=f3493347-4093-53f8-993b-382ee260ab8f
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.js","sources":["services/user.ts"],"sourceRoot":"/","sourcesContent":["/**\n * User service – profile get/update, profile picture upload URLs.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { logger } from \"../utils/logger.js\";\nimport { env } from \"../lib/config/env.js\";\nimport { findUserById, findUserProfileByUserId, upsertUserProfile } from \"../models/user.js\";\n\nfunction getFileUrl(filePath: string | null): string | null {\n if (!filePath) return null;\n if (filePath.startsWith(\"http\")) {\n return filePath;\n }\n const backendUrl = env.BACKEND_URL || \"http://localhost:3001\";\n return `${backendUrl}/api/files/${encodeURIComponent(filePath)}`;\n}\n\n/** Get user profile with display info and profile picture URLs. */\nexport async function getProfile(userId: string) {\n const user = await findUserById(userId);\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n const userProfile = await findUserProfileByUserId(userId);\n\n return {\n id: user.id,\n username: user.username,\n profile: userProfile\n ? {\n displayName: (userProfile as any).displayName || null,\n bio: (userProfile as any).bio || null,\n location: (userProfile as any).location || null,\n website: (userProfile as any).website || null,\n profilePicture: getFileUrl((userProfile as any).profilePicture),\n profilePictureThumbnail: getFileUrl(\n (userProfile as any).profilePictureThumbnail\n ),\n }\n : {\n displayName: null,\n bio: null,\n location: null,\n website: null,\n profilePicture: null,\n profilePictureThumbnail: null,\n },\n };\n}\n\n/** Update user profile (display name, bio, etc.) and/or profile picture. */\nexport async function updateProfile(\n userId: string,\n input: {\n profile?: {\n displayName?: string | null;\n bio?: string | null;\n location?: string | null;\n website?: string | null;\n };\n profilePicture?: {\n filePath: string;\n fileName: string;\n fileType: string;\n fileSize: number;\n };\n dicebearAvatar?: { url: string };\n }\n) {\n let profilePictureUrl: string | null = null;\n let profilePictureThumbnail: string | null = null;\n\n if (input.profilePicture) {\n try {\n profilePictureUrl = input.profilePicture.filePath;\n profilePictureThumbnail = null;\n } catch (error) {\n logger.error(\"Profile picture processing failed\", {\n userId,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to process profile picture. Please try again.\",\n });\n }\n }\n\n if (input.dicebearAvatar) {\n profilePictureUrl = input.dicebearAvatar.url;\n profilePictureThumbnail = null;\n }\n\n const updateData: any = {};\n if (input.profile) {\n if (input.profile.displayName !== undefined && input.profile.displayName !== null) {\n updateData.displayName = input.profile.displayName;\n }\n if (input.profile.bio !== undefined && input.profile.bio !== null) {\n updateData.bio = input.profile.bio;\n }\n if (input.profile.location !== undefined && input.profile.location !== null) {\n updateData.location = input.profile.location;\n }\n if (input.profile.website !== undefined && input.profile.website !== null) {\n updateData.website = input.profile.website;\n }\n }\n if (profilePictureUrl !== null) updateData.profilePicture = profilePictureUrl;\n if (profilePictureThumbnail !== null)\n updateData.profilePictureThumbnail = profilePictureThumbnail;\n\n const updatedProfile = await upsertUserProfile(userId, updateData);\n const user = await findUserById(userId);\n\n return {\n id: userId,\n username: user?.username || \"\",\n profile: {\n displayName: (updatedProfile as any).displayName || null,\n bio: (updatedProfile as any).bio || null,\n location: (updatedProfile as any).location || null,\n website: (updatedProfile as any).website || null,\n profilePicture: getFileUrl((updatedProfile as any).profilePicture),\n profilePictureThumbnail: getFileUrl(\n (updatedProfile as any).profilePictureThumbnail\n ),\n },\n };\n}\n\nexport async function getUploadUrl(\n userId: string,\n fileName: string,\n fileType: string\n) {\n try {\n const fileExtension = fileName.split(\".\").pop();\n const uniqueFilename = `${userId}-${Date.now()}.${fileExtension}`;\n const filePath = `users/${userId}/profile/${uniqueFilename}`;\n\n const backendUrl = env.BACKEND_URL || \"http://localhost:3001\";\n const uploadUrl = `${backendUrl}/api/upload/${encodeURIComponent(filePath)}`;\n\n logger.info(\"Generated upload URL\", {\n userId,\n filePath,\n fileName: uniqueFilename,\n fileType,\n uploadUrl,\n });\n\n return {\n uploadUrl,\n filePath,\n fileName: uniqueFilename,\n };\n } catch (error) {\n logger.error(\"Failed to generate upload URL\", {\n userId,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to generate upload URL\",\n });\n }\n}\n"],"names":[],"mappings":"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","debug_id":"f3493347-4093-53f8-993b-382ee260ab8f"}