@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
@@ -1,259 +1,82 @@
1
1
  import { z } from "zod";
2
2
  import { createTRPCRouter, protectedProcedure } from "../trpc.js";
3
- import { TRPCError } from "@trpc/server";
4
- import { prisma } from "../lib/prisma.js";
5
- import { createDirectUploadFiles, type DirectUploadFile } from "../lib/fileUpload.js";
6
- import { getSignedUrl } from "../lib/googleCloudStorage.js";
7
- import { logger } from "../utils/logger.js";
8
- import { bucket } from "../lib/googleCloudStorage.js";
3
+ import {
4
+ getProfile,
5
+ updateProfile,
6
+ getUploadUrl,
7
+ } from "../services/user.js";
9
8
 
10
- // Helper function to convert file path to backend proxy URL
11
- function getFileUrl(filePath: string | null): string | null {
12
- if (!filePath) return null;
13
-
14
- // If it's already a full URL (DiceBear or external), return as is
15
- if (filePath.startsWith('http')) {
16
- return filePath;
17
- }
18
-
19
- // Convert GCS path to full backend proxy URL
20
- const backendUrl = process.env.BACKEND_URL || 'http://localhost:3001';
21
- return `${backendUrl}/api/files/${encodeURIComponent(filePath)}`;
22
- }
23
-
24
- // For direct file uploads (file already uploaded to GCS)
25
9
  const fileUploadSchema = z.object({
26
10
  filePath: z.string().min(1, "File path is required"),
27
11
  fileName: z.string().min(1, "File name is required"),
28
- fileType: z.string().regex(/^image\/(jpeg|jpg|png|gif|webp)$/i, "Only image files (JPEG, PNG, GIF, WebP) are allowed"),
12
+ fileType: z
13
+ .string()
14
+ .regex(
15
+ /^image\/(jpeg|jpg|png|gif|webp)$/i,
16
+ "Only image files (JPEG, PNG, GIF, WebP) are allowed"
17
+ ),
29
18
  fileSize: z.number().max(5 * 1024 * 1024, "File size must be less than 5MB"),
30
19
  });
31
20
 
32
- // For DiceBear avatar URL
33
21
  const dicebearSchema = z.object({
34
22
  url: z.string().url("Invalid DiceBear avatar URL"),
35
23
  });
36
24
 
37
25
  const profileSchema = z.object({
38
- displayName: z.string().nullable().optional().transform(val => val === null ? undefined : val),
39
- bio: z.string().nullable().optional().transform(val => val === null ? undefined : val),
40
- location: z.string().nullable().optional().transform(val => val === null ? undefined : val),
41
- website: z.union([
42
- z.string().url(),
43
- z.literal(""),
44
- z.null().transform(() => undefined)
45
- ]).optional(),
26
+ displayName: z
27
+ .string()
28
+ .nullable()
29
+ .optional()
30
+ .transform((val) => (val === null ? undefined : val)),
31
+ bio: z
32
+ .string()
33
+ .nullable()
34
+ .optional()
35
+ .transform((val) => (val === null ? undefined : val)),
36
+ location: z
37
+ .string()
38
+ .nullable()
39
+ .optional()
40
+ .transform((val) => (val === null ? undefined : val)),
41
+ website: z
42
+ .union([
43
+ z.string().url(),
44
+ z.literal(""),
45
+ z.null().transform(() => undefined),
46
+ ])
47
+ .optional(),
46
48
  });
47
49
 
48
50
  const updateProfileSchema = z.object({
49
51
  profile: profileSchema.optional(),
50
- // Support both custom file upload and DiceBear avatar
51
52
  profilePicture: fileUploadSchema.optional(),
52
53
  dicebearAvatar: dicebearSchema.optional(),
53
54
  });
54
55
 
55
56
  const getUploadUrlSchema = z.object({
56
57
  fileName: z.string().min(1, "File name is required"),
57
- fileType: z.string().regex(/^image\/(jpeg|jpg|png|gif|webp)$/i, "Only image files are allowed"),
58
+ fileType: z
59
+ .string()
60
+ .regex(
61
+ /^image\/(jpeg|jpg|png|gif|webp)$/i,
62
+ "Only image files are allowed"
63
+ ),
58
64
  });
59
65
 
60
66
  export const userRouter = createTRPCRouter({
61
- getProfile: protectedProcedure
62
- .query(async ({ ctx }) => {
63
- if (!ctx.user) {
64
- throw new TRPCError({
65
- code: "UNAUTHORIZED",
66
- message: "User must be authenticated",
67
- });
68
- }
69
-
70
- const user = await prisma.user.findUnique({
71
- where: { id: ctx.user.id },
72
- select: {
73
- id: true,
74
- username: true,
75
- },
76
- });
77
-
78
- if (!user) {
79
- throw new TRPCError({
80
- code: "NOT_FOUND",
81
- message: "User not found",
82
- });
83
- }
84
-
85
- // Get user profile separately
86
- const userProfile = await prisma.userProfile.findUnique({
87
- where: { userId: ctx.user.id },
88
- });
89
-
90
- return {
91
- id: user.id,
92
- username: user.username,
93
- profile: userProfile ? {
94
- displayName: (userProfile as any).displayName || null,
95
- bio: (userProfile as any).bio || null,
96
- location: (userProfile as any).location || null,
97
- website: (userProfile as any).website || null,
98
- profilePicture: getFileUrl((userProfile as any).profilePicture),
99
- profilePictureThumbnail: getFileUrl((userProfile as any).profilePictureThumbnail),
100
- } : {
101
- displayName: null,
102
- bio: null,
103
- location: null,
104
- website: null,
105
- profilePicture: null,
106
- profilePictureThumbnail: null,
107
- },
108
- };
109
- }),
67
+ getProfile: protectedProcedure.query(({ ctx }) =>
68
+ getProfile(ctx.user!.id)
69
+ ),
110
70
 
111
71
  updateProfile: protectedProcedure
112
72
  .input(updateProfileSchema)
113
- .mutation(async ({ ctx, input }) => {
114
- if (!ctx.user) {
115
- throw new TRPCError({
116
- code: "UNAUTHORIZED",
117
- message: "User must be authenticated",
118
- });
119
- }
120
-
121
- // Get current profile to clean up old profile picture
122
- const currentProfile = await prisma.userProfile.findUnique({
123
- where: { userId: ctx.user.id },
124
- });
125
-
126
- let profilePictureUrl: string | null = null;
127
- let profilePictureThumbnail: string | null = null;
128
-
129
- // Handle custom profile picture (already uploaded to GCS)
130
- if (input.profilePicture) {
131
- try {
132
- // File is already uploaded to GCS, just use the path
133
- profilePictureUrl = input.profilePicture.filePath;
134
-
135
- // Generate thumbnail for the uploaded file
136
- // TODO: Implement thumbnail generation for direct uploads
137
- profilePictureThumbnail = null;
138
-
139
- // Clean up old profile picture if it exists
140
- if ((currentProfile as any)?.profilePicture) {
141
- // TODO: Implement file deletion logic here
142
- // await deleteFile((currentProfile as any).profilePicture);
143
- }
144
- } catch (error) {
145
- logger.error('Profile picture processing failed', {
146
- userId: ctx.user.id,
147
- error: error instanceof Error ? error.message : 'Unknown error'
148
- });
149
- throw new TRPCError({
150
- code: "INTERNAL_SERVER_ERROR",
151
- message: "Failed to process profile picture. Please try again.",
152
- });
153
- }
154
- }
155
-
156
- // Handle DiceBear avatar URL
157
- if (input.dicebearAvatar) {
158
- profilePictureUrl = input.dicebearAvatar.url;
159
- // No thumbnail for DiceBear avatars since they're SVG URLs
160
- profilePictureThumbnail = null;
161
- }
162
-
163
- // Prepare update data
164
- const updateData: any = {};
165
- if (input.profile) {
166
- if (input.profile.displayName !== undefined && input.profile.displayName !== null) {
167
- updateData.displayName = input.profile.displayName;
168
- }
169
- if (input.profile.bio !== undefined && input.profile.bio !== null) {
170
- updateData.bio = input.profile.bio;
171
- }
172
- if (input.profile.location !== undefined && input.profile.location !== null) {
173
- updateData.location = input.profile.location;
174
- }
175
- if (input.profile.website !== undefined && input.profile.website !== null) {
176
- updateData.website = input.profile.website;
177
- }
178
- }
179
- if (profilePictureUrl !== null) updateData.profilePicture = profilePictureUrl;
180
- if (profilePictureThumbnail !== null) updateData.profilePictureThumbnail = profilePictureThumbnail;
181
-
182
- // Upsert user profile with structured data
183
- const updatedProfile = await prisma.userProfile.upsert({
184
- where: { userId: ctx.user.id },
185
- create: {
186
- userId: ctx.user.id,
187
- ...updateData,
188
- },
189
- update: {
190
- ...updateData,
191
- updatedAt: new Date(),
192
- },
193
- });
194
-
195
- // Get username for response
196
- const user = await prisma.user.findUnique({
197
- where: { id: ctx.user.id },
198
- select: { username: true },
199
- });
200
-
201
- return {
202
- id: ctx.user.id,
203
- username: user?.username || '',
204
- profile: {
205
- displayName: (updatedProfile as any).displayName || null,
206
- bio: (updatedProfile as any).bio || null,
207
- location: (updatedProfile as any).location || null,
208
- website: (updatedProfile as any).website || null,
209
- profilePicture: getFileUrl((updatedProfile as any).profilePicture),
210
- profilePictureThumbnail: getFileUrl((updatedProfile as any).profilePictureThumbnail),
211
- },
212
- };
213
- }),
73
+ .mutation(({ ctx, input }) =>
74
+ updateProfile(ctx.user!.id, input)
75
+ ),
214
76
 
215
77
  getUploadUrl: protectedProcedure
216
78
  .input(getUploadUrlSchema)
217
- .mutation(async ({ ctx, input }) => {
218
- if (!ctx.user) {
219
- throw new TRPCError({
220
- code: "UNAUTHORIZED",
221
- message: "User must be authenticated",
222
- });
223
- }
224
-
225
- try {
226
- // Generate unique filename
227
- const fileExtension = input.fileName.split('.').pop();
228
- const uniqueFilename = `${ctx.user.id}-${Date.now()}.${fileExtension}`;
229
- const filePath = `users/${ctx.user.id}/profile/${uniqueFilename}`;
230
-
231
- // Generate backend proxy upload URL instead of direct GCS signed URL
232
- const backendUrl = process.env.BACKEND_URL || 'http://localhost:3001';
233
- const uploadUrl = `${backendUrl}/api/upload/${encodeURIComponent(filePath)}`;
234
-
235
- logger.info('Generated upload URL', {
236
- userId: ctx.user.id,
237
- filePath,
238
- fileName: uniqueFilename,
239
- fileType: input.fileType,
240
- uploadUrl
241
- });
242
-
243
- return {
244
- uploadUrl,
245
- filePath,
246
- fileName: uniqueFilename,
247
- };
248
- } catch (error) {
249
- logger.error('Failed to generate upload URL', {
250
- userId: ctx.user.id,
251
- error: error instanceof Error ? error.message : 'Unknown error'
252
- });
253
- throw new TRPCError({
254
- code: "INTERNAL_SERVER_ERROR",
255
- message: "Failed to generate upload URL",
256
- });
257
- }
258
- }),
259
- });
79
+ .mutation(({ ctx, input }) =>
80
+ getUploadUrl(ctx.user!.id, input.fileName, input.fileType)
81
+ ),
82
+ });
@@ -0,0 +1,252 @@
1
+ import { TRPCError } from "@trpc/server";
2
+ import {
3
+ createTRPCRouter,
4
+ protectedClassMemberProcedure,
5
+ protectedProcedure,
6
+ } from "../trpc.js";
7
+ import { z } from "zod";
8
+ import {
9
+ getWorksheet,
10
+ worksheetExists,
11
+ listWorksheets,
12
+ updateWorksheetRecord,
13
+ deleteWorksheetRecord,
14
+ createWorksheetRecord,
15
+ addQuestionToWorksheet,
16
+ reorderWorksheetQuestions,
17
+ updateWorksheetQuestionRecord,
18
+ deleteWorksheetQuestionRecord,
19
+ getWorksheetSubmission,
20
+ answerWorksheetQuestion,
21
+ cancelGrading,
22
+ regradeQuestion,
23
+ gradeAnswer,
24
+ addCommentToResponse,
25
+ } from "../services/worksheet.js";
26
+
27
+ export const worksheetRouter = createTRPCRouter({
28
+ getWorksheet: protectedProcedure
29
+ .input(z.object({ worksheetId: z.string() }))
30
+ .query(({ input }) => getWorksheet(input.worksheetId)),
31
+
32
+ exists: protectedClassMemberProcedure
33
+ .input(z.object({ id: z.string() }))
34
+ .query(async ({ ctx, input }) => {
35
+ if (!ctx.user) {
36
+ throw new TRPCError({
37
+ code: "UNAUTHORIZED",
38
+ message: "User must be authenticated",
39
+ });
40
+ }
41
+ return worksheetExists(input.id);
42
+ }),
43
+
44
+ listWorksheets: protectedProcedure
45
+ .input(z.object({ classId: z.string() }))
46
+ .query(({ input }) => listWorksheets(input.classId)),
47
+
48
+ updateWorksheet: protectedProcedure
49
+ .input(z.object({ worksheetId: z.string(), name: z.string().optional() }))
50
+ .mutation(({ input }) =>
51
+ updateWorksheetRecord(input.worksheetId, { name: input.name })
52
+ ),
53
+
54
+ deleteWorksheet: protectedProcedure
55
+ .input(z.object({ worksheetId: z.string() }))
56
+ .mutation(({ input }) => deleteWorksheetRecord(input.worksheetId)),
57
+
58
+ create: protectedProcedure
59
+ .input(z.object({ classId: z.string(), name: z.string() }))
60
+ .mutation(({ input }) =>
61
+ createWorksheetRecord(input.classId, input.name)
62
+ ),
63
+
64
+ addQuestion: protectedProcedure
65
+ .input(
66
+ z.object({
67
+ worksheetId: z.string(),
68
+ question: z.string(),
69
+ answer: z.string(),
70
+ points: z.number().optional(),
71
+ options: z.any().optional(),
72
+ markScheme: z.any().optional(),
73
+ type: z.enum([
74
+ "MULTIPLE_CHOICE",
75
+ "TRUE_FALSE",
76
+ "SHORT_ANSWER",
77
+ "LONG_ANSWER",
78
+ "MATH_EXPRESSION",
79
+ "ESSAY",
80
+ ]),
81
+ })
82
+ )
83
+ .mutation(({ input }) =>
84
+ addQuestionToWorksheet(input.worksheetId, {
85
+ question: input.question,
86
+ answer: input.answer,
87
+ points: input.points,
88
+ options: input.options,
89
+ markScheme: input.markScheme,
90
+ type: input.type,
91
+ })
92
+ ),
93
+
94
+ reorderQuestions: protectedProcedure
95
+ .input(
96
+ z.object({
97
+ worksheetId: z.string(),
98
+ movedId: z.string(),
99
+ position: z.enum(["before", "after"]),
100
+ targetId: z.string(),
101
+ })
102
+ )
103
+ .mutation(({ input }) =>
104
+ reorderWorksheetQuestions(
105
+ input.worksheetId,
106
+ input.movedId,
107
+ input.position,
108
+ input.targetId
109
+ )
110
+ ),
111
+
112
+ updateQuestion: protectedProcedure
113
+ .input(
114
+ z.object({
115
+ worksheetId: z.string(),
116
+ questionId: z.string(),
117
+ question: z.string().optional(),
118
+ answer: z.string().optional(),
119
+ points: z.number().optional(),
120
+ options: z.any().optional(),
121
+ markScheme: z.any().optional(),
122
+ type: z
123
+ .enum([
124
+ "MULTIPLE_CHOICE",
125
+ "TRUE_FALSE",
126
+ "SHORT_ANSWER",
127
+ "LONG_ANSWER",
128
+ "MATH_EXPRESSION",
129
+ "ESSAY",
130
+ ])
131
+ .optional(),
132
+ })
133
+ )
134
+ .mutation(({ input }) =>
135
+ updateWorksheetQuestionRecord(
136
+ input.worksheetId,
137
+ input.questionId,
138
+ {
139
+ question: input.question,
140
+ answer: input.answer,
141
+ points: input.points,
142
+ options: input.options,
143
+ markScheme: input.markScheme,
144
+ type: input.type,
145
+ }
146
+ )
147
+ ),
148
+
149
+ deleteQuestion: protectedProcedure
150
+ .input(
151
+ z.object({
152
+ worksheetId: z.string(),
153
+ questionId: z.string(),
154
+ })
155
+ )
156
+ .mutation(({ input }) =>
157
+ deleteWorksheetQuestionRecord(input.worksheetId, input.questionId)
158
+ ),
159
+
160
+ getWorksheetSubmission: protectedProcedure
161
+ .input(
162
+ z.object({
163
+ worksheetId: z.string(),
164
+ submissionId: z.string(),
165
+ })
166
+ )
167
+ .query(({ input }) =>
168
+ getWorksheetSubmission(input.worksheetId, input.submissionId)
169
+ ),
170
+
171
+ answerQuestion: protectedProcedure
172
+ .input(
173
+ z.object({
174
+ worksheetResponseId: z.string(),
175
+ questionId: z.string(),
176
+ response: z.string(),
177
+ })
178
+ )
179
+ .mutation(({ ctx, input }) =>
180
+ answerWorksheetQuestion(
181
+ input.worksheetResponseId,
182
+ input.questionId,
183
+ input.response,
184
+ ctx.user!.id
185
+ )
186
+ ),
187
+
188
+ cancelGrading: protectedProcedure
189
+ .input(
190
+ z.object({
191
+ worksheetResponseId: z.string(),
192
+ progressId: z.string(),
193
+ })
194
+ )
195
+ .mutation(({ input }) =>
196
+ cancelGrading(input.worksheetResponseId, input.progressId)
197
+ ),
198
+
199
+ regradeQuestion: protectedProcedure
200
+ .input(
201
+ z.object({
202
+ worksheetResponseId: z.string(),
203
+ progressId: z.string(),
204
+ })
205
+ )
206
+ .mutation(({ input }) =>
207
+ regradeQuestion(input.worksheetResponseId, input.progressId)
208
+ ),
209
+
210
+ gradeAnswer: protectedProcedure
211
+ .input(
212
+ z.object({
213
+ questionId: z.string(),
214
+ responseId: z.string().optional(),
215
+ studentWorksheetResponseId: z.string(),
216
+ response: z.string().optional(),
217
+ isCorrect: z.boolean(),
218
+ feedback: z.string().optional(),
219
+ markschemeState: z.any().optional(),
220
+ points: z.number().optional(),
221
+ })
222
+ )
223
+ .mutation(({ input }) =>
224
+ gradeAnswer(
225
+ input.questionId,
226
+ input.studentWorksheetResponseId,
227
+ {
228
+ responseId: input.responseId,
229
+ response: input.response,
230
+ isCorrect: input.isCorrect,
231
+ feedback: input.feedback,
232
+ markschemeState: input.markschemeState,
233
+ points: input.points,
234
+ }
235
+ )
236
+ ),
237
+
238
+ addComment: protectedProcedure
239
+ .input(
240
+ z.object({
241
+ responseId: z.string(),
242
+ comment: z.string(),
243
+ })
244
+ )
245
+ .mutation(({ ctx, input }) =>
246
+ addCommentToResponse(
247
+ input.responseId,
248
+ input.comment,
249
+ ctx.user!.id
250
+ )
251
+ ),
252
+ });