@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,50 @@
1
+ /**
2
+ * Notification service – list, send, and mark notifications as read.
3
+ */
4
+ import {
5
+ findNotificationsByReceiverId,
6
+ findNotificationById,
7
+ createNotification,
8
+ createManyNotifications,
9
+ markNotificationAsRead,
10
+ } from "../models/notification.js";
11
+
12
+ /** List notifications for a user. */
13
+ export async function listNotifications(userId: string) {
14
+ return findNotificationsByReceiverId(userId);
15
+ }
16
+
17
+ /** Get a single notification by ID. */
18
+ export async function getNotification(id: string) {
19
+ return findNotificationById(id);
20
+ }
21
+
22
+ /** Send a notification to one user. */
23
+ export async function sendNotification(input: {
24
+ receiverId: string;
25
+ title: string;
26
+ content: string;
27
+ }) {
28
+ return createNotification(input);
29
+ }
30
+
31
+ /** Send the same notification to multiple users. */
32
+ export async function sendToMultiple(input: {
33
+ receiverIds: string[];
34
+ title: string;
35
+ content: string;
36
+ }) {
37
+ const result = await createManyNotifications(
38
+ input.receiverIds.map((receiverId) => ({
39
+ receiverId,
40
+ title: input.title,
41
+ content: input.content,
42
+ }))
43
+ );
44
+ return result;
45
+ }
46
+
47
+ /** Mark a notification as read. */
48
+ export async function markAsRead(id: string) {
49
+ return markNotificationAsRead(id);
50
+ }
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Section service – CRUD for class sections. Handles reordering of sections
3
+ * and assignments in a unified list.
4
+ */
5
+ import { TRPCError } from "@trpc/server";
6
+ import {
7
+ findSectionById,
8
+ findClassWithTeacher,
9
+ createSection,
10
+ findSectionsByClassId,
11
+ findAssignmentsByClassId,
12
+ updateSectionOrder,
13
+ updateAssignmentOrder,
14
+ updateSection,
15
+ deleteSectionById,
16
+ findSectionByClassAndId,
17
+ } from "../models/section.js";
18
+ import { prisma } from "../lib/prisma.js";
19
+
20
+ /** Check if a section exists. */
21
+ export async function sectionExists(id: string) {
22
+ const section = await findSectionById(id);
23
+ return !!section;
24
+ }
25
+
26
+ /** Create a section and reorder the unified section/assignment list. */
27
+ export async function createSectionRecord(
28
+ userId: string,
29
+ input: {
30
+ classId: string;
31
+ id?: string;
32
+ name: string;
33
+ color?: string;
34
+ }
35
+ ) {
36
+ const classData = await findClassWithTeacher(input.classId, userId);
37
+ if (!classData) {
38
+ throw new TRPCError({
39
+ code: "NOT_FOUND",
40
+ message: "Class not found or you are not a teacher",
41
+ });
42
+ }
43
+
44
+ const section = await createSection({
45
+ id: input.id,
46
+ name: input.name,
47
+ order: 0,
48
+ classId: input.classId,
49
+ color: input.color,
50
+ });
51
+
52
+ const [sections, assignments] = await Promise.all([
53
+ findSectionsByClassId(input.classId),
54
+ findAssignmentsByClassId(input.classId),
55
+ ]);
56
+
57
+ const unified = [
58
+ ...sections.map((s) => ({ id: s.id, order: s.order, type: "section" as const })),
59
+ ...assignments.map((a) => ({ id: a.id, order: a.order, type: "assignment" as const })),
60
+ ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
61
+
62
+ const withoutNew = unified.filter(
63
+ (item) => !(item.id === section.id && item.type === "section")
64
+ );
65
+ const reindexed = [
66
+ { id: section.id, type: "section" as const },
67
+ ...withoutNew.map((item) => ({ id: item.id, type: item.type })),
68
+ ];
69
+
70
+ await Promise.all(
71
+ reindexed.map((item, index) =>
72
+ item.type === "section"
73
+ ? updateSectionOrder(item.id, index + 1)
74
+ : updateAssignmentOrder(item.id, index + 1)
75
+ )
76
+ );
77
+
78
+ return section;
79
+ }
80
+
81
+ export async function reorderSection(
82
+ userId: string,
83
+ input: {
84
+ classId: string;
85
+ movedId: string;
86
+ position: "start" | "end" | "before" | "after";
87
+ targetId?: string;
88
+ }
89
+ ) {
90
+ const classData = await findClassWithTeacher(input.classId, userId);
91
+ if (!classData) {
92
+ throw new TRPCError({
93
+ code: "UNAUTHORIZED",
94
+ message: "Not authorized",
95
+ });
96
+ }
97
+
98
+ const moved = await findSectionByClassAndId(input.movedId, input.classId);
99
+ if (!moved) {
100
+ throw new TRPCError({
101
+ code: "NOT_FOUND",
102
+ message: "Section not found",
103
+ });
104
+ }
105
+
106
+ if (
107
+ (input.position === "before" || input.position === "after") &&
108
+ !input.targetId
109
+ ) {
110
+ throw new TRPCError({
111
+ code: "BAD_REQUEST",
112
+ message: "targetId required for before/after",
113
+ });
114
+ }
115
+
116
+ const result = await prisma.$transaction(async (tx) => {
117
+ const [sections, assignments] = await Promise.all([
118
+ tx.section.findMany({
119
+ where: { classId: input.classId },
120
+ select: { id: true, order: true },
121
+ }),
122
+ tx.assignment.findMany({
123
+ where: { classId: input.classId },
124
+ select: { id: true, order: true },
125
+ }),
126
+ ]);
127
+
128
+ const unified = [
129
+ ...sections.map((s) => ({ id: s.id, order: s.order, type: "section" as const })),
130
+ ...assignments.map((a) => ({ id: a.id, order: a.order, type: "assignment" as const })),
131
+ ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
132
+
133
+ const movedIdx = unified.findIndex(
134
+ (item) => item.id === input.movedId && item.type === "section"
135
+ );
136
+ if (movedIdx === -1) {
137
+ throw new TRPCError({
138
+ code: "NOT_FOUND",
139
+ message: "Section not found in unified list",
140
+ });
141
+ }
142
+
143
+ const withoutMoved = unified.filter(
144
+ (item) => !(item.id === input.movedId && item.type === "section")
145
+ );
146
+
147
+ let next: Array<{ id: string; type: "section" | "assignment" }> = [];
148
+
149
+ if (input.position === "start") {
150
+ next = [
151
+ { id: input.movedId, type: "section" },
152
+ ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),
153
+ ];
154
+ } else if (input.position === "end") {
155
+ next = [
156
+ ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),
157
+ { id: input.movedId, type: "section" },
158
+ ];
159
+ } else {
160
+ const targetIdx = withoutMoved.findIndex((item) => item.id === input.targetId);
161
+ if (targetIdx === -1) {
162
+ throw new TRPCError({
163
+ code: "BAD_REQUEST",
164
+ message: "targetId not found in unified list",
165
+ });
166
+ }
167
+ if (input.position === "before") {
168
+ next = [
169
+ ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id, type: item.type })),
170
+ { id: input.movedId, type: "section" },
171
+ ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id, type: item.type })),
172
+ ];
173
+ } else {
174
+ next = [
175
+ ...withoutMoved
176
+ .slice(0, targetIdx + 1)
177
+ .map((item) => ({ id: item.id, type: item.type })),
178
+ { id: input.movedId, type: "section" },
179
+ ...withoutMoved
180
+ .slice(targetIdx + 1)
181
+ .map((item) => ({ id: item.id, type: item.type })),
182
+ ];
183
+ }
184
+ }
185
+
186
+ await Promise.all(
187
+ next.map((item, index) =>
188
+ item.type === "section"
189
+ ? tx.section.update({ where: { id: item.id }, data: { order: index + 1 } })
190
+ : tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } })
191
+ )
192
+ );
193
+
194
+ return tx.section.findUnique({ where: { id: input.movedId } });
195
+ });
196
+
197
+ return result;
198
+ }
199
+
200
+ export async function updateSectionRecord(
201
+ userId: string,
202
+ input: {
203
+ id: string;
204
+ classId: string;
205
+ name: string;
206
+ color?: string;
207
+ }
208
+ ) {
209
+ const classData = await findClassWithTeacher(input.classId, userId);
210
+ if (!classData) {
211
+ throw new TRPCError({
212
+ code: "NOT_FOUND",
213
+ message: "Class not found or you are not a teacher",
214
+ });
215
+ }
216
+
217
+ return updateSection(input.id, {
218
+ name: input.name,
219
+ color: input.color,
220
+ });
221
+ }
222
+
223
+ export async function reOrderSection(
224
+ userId: string,
225
+ input: { id: string; classId: string; order: number }
226
+ ) {
227
+ const classData = await findClassWithTeacher(input.classId, userId);
228
+ if (!classData) {
229
+ throw new TRPCError({
230
+ code: "NOT_FOUND",
231
+ message: "Class not found or you are not a teacher",
232
+ });
233
+ }
234
+
235
+ await prisma.$transaction(async (tx) => {
236
+ await tx.section.update({
237
+ where: { id: input.id },
238
+ data: { order: input.order },
239
+ });
240
+
241
+ const [sections, assignments] = await Promise.all([
242
+ tx.section.findMany({
243
+ where: { classId: input.classId },
244
+ select: { id: true, order: true },
245
+ }),
246
+ tx.assignment.findMany({
247
+ where: { classId: input.classId },
248
+ select: { id: true, order: true },
249
+ }),
250
+ ]);
251
+
252
+ const unified = [
253
+ ...sections.map((s) => ({ id: s.id, order: s.order, type: "section" as const })),
254
+ ...assignments.map((a) => ({ id: a.id, order: a.order, type: "assignment" as const })),
255
+ ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
256
+
257
+ await Promise.all(
258
+ unified.map((item, index) =>
259
+ item.type === "section"
260
+ ? tx.section.update({ where: { id: item.id }, data: { order: index + 1 } })
261
+ : tx.assignment.update({ where: { id: item.id }, data: { order: index + 1 } })
262
+ )
263
+ );
264
+ });
265
+
266
+ return { id: input.id };
267
+ }
268
+
269
+ export async function deleteSectionRecord(
270
+ userId: string,
271
+ input: { id: string; classId: string }
272
+ ) {
273
+ const classData = await findClassWithTeacher(input.classId, userId);
274
+ if (!classData) {
275
+ throw new TRPCError({
276
+ code: "NOT_FOUND",
277
+ message: "Class not found or you are not a teacher",
278
+ });
279
+ }
280
+
281
+ await deleteSectionById(input.id);
282
+ return { id: input.id };
283
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * User service – profile get/update, profile picture upload URLs.
3
+ */
4
+ import { TRPCError } from "@trpc/server";
5
+ import { logger } from "../utils/logger.js";
6
+ import { env } from "../lib/config/env.js";
7
+ import { findUserById, findUserProfileByUserId, upsertUserProfile } from "../models/user.js";
8
+
9
+ function getFileUrl(filePath: string | null): string | null {
10
+ if (!filePath) return null;
11
+ if (filePath.startsWith("http")) {
12
+ return filePath;
13
+ }
14
+ const backendUrl = env.BACKEND_URL || "http://localhost:3001";
15
+ return `${backendUrl}/api/files/${encodeURIComponent(filePath)}`;
16
+ }
17
+
18
+ /** Get user profile with display info and profile picture URLs. */
19
+ export async function getProfile(userId: string) {
20
+ const user = await findUserById(userId);
21
+ if (!user) {
22
+ throw new TRPCError({
23
+ code: "NOT_FOUND",
24
+ message: "User not found",
25
+ });
26
+ }
27
+
28
+ const userProfile = await findUserProfileByUserId(userId);
29
+
30
+ return {
31
+ id: user.id,
32
+ username: user.username,
33
+ profile: userProfile
34
+ ? {
35
+ displayName: (userProfile as any).displayName || null,
36
+ bio: (userProfile as any).bio || null,
37
+ location: (userProfile as any).location || null,
38
+ website: (userProfile as any).website || null,
39
+ profilePicture: getFileUrl((userProfile as any).profilePicture),
40
+ profilePictureThumbnail: getFileUrl(
41
+ (userProfile as any).profilePictureThumbnail
42
+ ),
43
+ }
44
+ : {
45
+ displayName: null,
46
+ bio: null,
47
+ location: null,
48
+ website: null,
49
+ profilePicture: null,
50
+ profilePictureThumbnail: null,
51
+ },
52
+ };
53
+ }
54
+
55
+ /** Update user profile (display name, bio, etc.) and/or profile picture. */
56
+ export async function updateProfile(
57
+ userId: string,
58
+ input: {
59
+ profile?: {
60
+ displayName?: string | null;
61
+ bio?: string | null;
62
+ location?: string | null;
63
+ website?: string | null;
64
+ };
65
+ profilePicture?: {
66
+ filePath: string;
67
+ fileName: string;
68
+ fileType: string;
69
+ fileSize: number;
70
+ };
71
+ dicebearAvatar?: { url: string };
72
+ }
73
+ ) {
74
+ let profilePictureUrl: string | null = null;
75
+ let profilePictureThumbnail: string | null = null;
76
+
77
+ if (input.profilePicture) {
78
+ try {
79
+ profilePictureUrl = input.profilePicture.filePath;
80
+ profilePictureThumbnail = null;
81
+ } catch (error) {
82
+ logger.error("Profile picture processing failed", {
83
+ userId,
84
+ error: error instanceof Error ? error.message : "Unknown error",
85
+ });
86
+ throw new TRPCError({
87
+ code: "INTERNAL_SERVER_ERROR",
88
+ message: "Failed to process profile picture. Please try again.",
89
+ });
90
+ }
91
+ }
92
+
93
+ if (input.dicebearAvatar) {
94
+ profilePictureUrl = input.dicebearAvatar.url;
95
+ profilePictureThumbnail = null;
96
+ }
97
+
98
+ const updateData: any = {};
99
+ if (input.profile) {
100
+ if (input.profile.displayName !== undefined && input.profile.displayName !== null) {
101
+ updateData.displayName = input.profile.displayName;
102
+ }
103
+ if (input.profile.bio !== undefined && input.profile.bio !== null) {
104
+ updateData.bio = input.profile.bio;
105
+ }
106
+ if (input.profile.location !== undefined && input.profile.location !== null) {
107
+ updateData.location = input.profile.location;
108
+ }
109
+ if (input.profile.website !== undefined && input.profile.website !== null) {
110
+ updateData.website = input.profile.website;
111
+ }
112
+ }
113
+ if (profilePictureUrl !== null) updateData.profilePicture = profilePictureUrl;
114
+ if (profilePictureThumbnail !== null)
115
+ updateData.profilePictureThumbnail = profilePictureThumbnail;
116
+
117
+ const updatedProfile = await upsertUserProfile(userId, updateData);
118
+ const user = await findUserById(userId);
119
+
120
+ return {
121
+ id: userId,
122
+ username: user?.username || "",
123
+ profile: {
124
+ displayName: (updatedProfile as any).displayName || null,
125
+ bio: (updatedProfile as any).bio || null,
126
+ location: (updatedProfile as any).location || null,
127
+ website: (updatedProfile as any).website || null,
128
+ profilePicture: getFileUrl((updatedProfile as any).profilePicture),
129
+ profilePictureThumbnail: getFileUrl(
130
+ (updatedProfile as any).profilePictureThumbnail
131
+ ),
132
+ },
133
+ };
134
+ }
135
+
136
+ export async function getUploadUrl(
137
+ userId: string,
138
+ fileName: string,
139
+ fileType: string
140
+ ) {
141
+ try {
142
+ const fileExtension = fileName.split(".").pop();
143
+ const uniqueFilename = `${userId}-${Date.now()}.${fileExtension}`;
144
+ const filePath = `users/${userId}/profile/${uniqueFilename}`;
145
+
146
+ const backendUrl = env.BACKEND_URL || "http://localhost:3001";
147
+ const uploadUrl = `${backendUrl}/api/upload/${encodeURIComponent(filePath)}`;
148
+
149
+ logger.info("Generated upload URL", {
150
+ userId,
151
+ filePath,
152
+ fileName: uniqueFilename,
153
+ fileType,
154
+ uploadUrl,
155
+ });
156
+
157
+ return {
158
+ uploadUrl,
159
+ filePath,
160
+ fileName: uniqueFilename,
161
+ };
162
+ } catch (error) {
163
+ logger.error("Failed to generate upload URL", {
164
+ userId,
165
+ error: error instanceof Error ? error.message : "Unknown error",
166
+ });
167
+ throw new TRPCError({
168
+ code: "INTERNAL_SERVER_ERROR",
169
+ message: "Failed to generate upload URL",
170
+ });
171
+ }
172
+ }