@studious-lms/server 1.2.45 → 1.2.46

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 (231) hide show
  1. package/.env.example +45 -0
  2. package/.env.test.example +37 -0
  3. package/README.md +34 -7
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/clover.xml +12110 -0
  7. package/coverage/coverage-final.json +44 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +221 -0
  10. package/coverage/prettify.css +1 -0
  11. package/coverage/prettify.js +2 -0
  12. package/coverage/server/index.html +116 -0
  13. package/coverage/server/src/exportType.ts.html +109 -0
  14. package/coverage/server/src/index.html +161 -0
  15. package/coverage/server/src/index.ts.html +1702 -0
  16. package/coverage/server/src/instrument.ts.html +130 -0
  17. package/coverage/server/src/lib/config/env.ts.html +448 -0
  18. package/coverage/server/src/lib/config/index.html +116 -0
  19. package/coverage/server/src/lib/fileUpload.ts.html +1138 -0
  20. package/coverage/server/src/lib/googleCloudStorage.ts.html +334 -0
  21. package/coverage/server/src/lib/index.html +206 -0
  22. package/coverage/server/src/lib/jsonConversion.ts.html +2323 -0
  23. package/coverage/server/src/lib/jsonStyles.ts.html +193 -0
  24. package/coverage/server/src/lib/notificationHandler.ts.html +193 -0
  25. package/coverage/server/src/lib/pusher.ts.html +121 -0
  26. package/coverage/server/src/lib/thumbnailGenerator.ts.html +592 -0
  27. package/coverage/server/src/middleware/auth.ts.html +646 -0
  28. package/coverage/server/src/middleware/index.html +146 -0
  29. package/coverage/server/src/middleware/logging.ts.html +244 -0
  30. package/coverage/server/src/middleware/security.ts.html +271 -0
  31. package/coverage/server/src/routers/_app.ts.html +232 -0
  32. package/coverage/server/src/routers/agenda.ts.html +319 -0
  33. package/coverage/server/src/routers/announcement.ts.html +3481 -0
  34. package/coverage/server/src/routers/assignment.ts.html +7633 -0
  35. package/coverage/server/src/routers/attendance.ts.html +1030 -0
  36. package/coverage/server/src/routers/auth.ts.html +1081 -0
  37. package/coverage/server/src/routers/class.ts.html +3535 -0
  38. package/coverage/server/src/routers/comment.ts.html +991 -0
  39. package/coverage/server/src/routers/conversation.ts.html +982 -0
  40. package/coverage/server/src/routers/event.ts.html +1609 -0
  41. package/coverage/server/src/routers/file.ts.html +1144 -0
  42. package/coverage/server/src/routers/folder.ts.html +2797 -0
  43. package/coverage/server/src/routers/index.html +386 -0
  44. package/coverage/server/src/routers/labChat.ts.html +3073 -0
  45. package/coverage/server/src/routers/marketing.ts.html +340 -0
  46. package/coverage/server/src/routers/message.ts.html +1912 -0
  47. package/coverage/server/src/routers/notifications.ts.html +364 -0
  48. package/coverage/server/src/routers/section.ts.html +1120 -0
  49. package/coverage/server/src/routers/user.ts.html +862 -0
  50. package/coverage/server/src/routers/worksheet.ts.html +1729 -0
  51. package/coverage/server/src/trpc.ts.html +397 -0
  52. package/coverage/server/src/types/index.html +116 -0
  53. package/coverage/server/src/types/trpc.ts.html +127 -0
  54. package/coverage/server/src/utils/aiUser.ts.html +280 -0
  55. package/coverage/server/src/utils/email.ts.html +121 -0
  56. package/coverage/server/src/utils/generateInviteCode.ts.html +106 -0
  57. package/coverage/server/src/utils/index.html +206 -0
  58. package/coverage/server/src/utils/inference.ts.html +709 -0
  59. package/coverage/server/src/utils/logger.ts.html +664 -0
  60. package/coverage/server/src/utils/prismaErrorHandler.ts.html +907 -0
  61. package/coverage/server/src/utils/prismaWrapper.ts.html +355 -0
  62. package/coverage/server/vitest.config.ts.html +196 -0
  63. package/coverage/sort-arrow-sprite.png +0 -0
  64. package/coverage/sorter.js +210 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +83 -52
  67. package/dist/index.js.map +1 -1
  68. package/dist/instrument.js +15 -8
  69. package/dist/instrument.js.map +1 -1
  70. package/dist/lib/config/env.d.ts +169 -0
  71. package/dist/lib/config/env.d.ts.map +1 -0
  72. package/dist/lib/config/env.js +115 -0
  73. package/dist/lib/config/env.js.map +1 -0
  74. package/dist/lib/fileUpload.d.ts.map +1 -1
  75. package/dist/lib/fileUpload.js +5 -4
  76. package/dist/lib/fileUpload.js.map +1 -1
  77. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  78. package/dist/lib/googleCloudStorage.js +7 -8
  79. package/dist/lib/googleCloudStorage.js.map +1 -1
  80. package/dist/lib/jsonConversion.d.ts.map +1 -1
  81. package/dist/lib/jsonConversion.js +14 -16
  82. package/dist/lib/jsonConversion.js.map +1 -1
  83. package/dist/lib/notificationHandler.d.ts +2 -2
  84. package/dist/lib/prisma.d.ts +2 -2
  85. package/dist/lib/prisma.d.ts.map +1 -1
  86. package/dist/lib/prisma.js +22 -3
  87. package/dist/lib/prisma.js.map +1 -1
  88. package/dist/lib/pusher.d.ts.map +1 -1
  89. package/dist/lib/pusher.js +8 -7
  90. package/dist/lib/pusher.js.map +1 -1
  91. package/dist/middleware/auth.d.ts.map +1 -1
  92. package/dist/middleware/auth.js +6 -5
  93. package/dist/middleware/auth.js.map +1 -1
  94. package/dist/middleware/security.d.ts +5 -0
  95. package/dist/middleware/security.d.ts.map +1 -0
  96. package/dist/middleware/security.js +77 -0
  97. package/dist/middleware/security.js.map +1 -0
  98. package/dist/routers/_app.d.ts +294 -98
  99. package/dist/routers/_app.d.ts.map +1 -1
  100. package/dist/routers/_app.js +4 -2
  101. package/dist/routers/_app.js.map +1 -1
  102. package/dist/routers/agenda.d.ts.map +1 -1
  103. package/dist/routers/agenda.js +12 -9
  104. package/dist/routers/agenda.js.map +1 -1
  105. package/dist/routers/announcement.d.ts +8 -0
  106. package/dist/routers/announcement.d.ts.map +1 -1
  107. package/dist/routers/announcement.js +6 -4
  108. package/dist/routers/announcement.js.map +1 -1
  109. package/dist/routers/assignment.d.ts +7 -4
  110. package/dist/routers/assignment.d.ts.map +1 -1
  111. package/dist/routers/assignment.js +35 -18
  112. package/dist/routers/assignment.js.map +1 -1
  113. package/dist/routers/attendance.d.ts +1 -0
  114. package/dist/routers/attendance.d.ts.map +1 -1
  115. package/dist/routers/attendance.js +4 -4
  116. package/dist/routers/attendance.js.map +1 -1
  117. package/dist/routers/auth.d.ts +20 -0
  118. package/dist/routers/auth.d.ts.map +1 -1
  119. package/dist/routers/auth.js +132 -15
  120. package/dist/routers/auth.js.map +1 -1
  121. package/dist/routers/class.d.ts +10 -0
  122. package/dist/routers/class.d.ts.map +1 -1
  123. package/dist/routers/class.js +49 -5
  124. package/dist/routers/class.js.map +1 -1
  125. package/dist/routers/comment.d.ts +2 -0
  126. package/dist/routers/comment.d.ts.map +1 -1
  127. package/dist/routers/conversation.d.ts +1 -0
  128. package/dist/routers/conversation.d.ts.map +1 -1
  129. package/dist/routers/conversation.js +46 -31
  130. package/dist/routers/conversation.js.map +1 -1
  131. package/dist/routers/file.d.ts.map +1 -1
  132. package/dist/routers/file.js +30 -7
  133. package/dist/routers/file.js.map +1 -1
  134. package/dist/routers/labChat.d.ts +1 -0
  135. package/dist/routers/labChat.d.ts.map +1 -1
  136. package/dist/routers/labChat.js +2 -3
  137. package/dist/routers/labChat.js.map +1 -1
  138. package/dist/routers/marketing.d.ts +1 -1
  139. package/dist/routers/newtonChat.d.ts +55 -0
  140. package/dist/routers/newtonChat.d.ts.map +1 -0
  141. package/dist/routers/newtonChat.js +438 -0
  142. package/dist/routers/newtonChat.js.map +1 -0
  143. package/dist/routers/notifications.d.ts +4 -4
  144. package/dist/routers/section.d.ts +9 -4
  145. package/dist/routers/section.d.ts.map +1 -1
  146. package/dist/routers/section.js +8 -8
  147. package/dist/routers/section.js.map +1 -1
  148. package/dist/routers/user.d.ts.map +1 -1
  149. package/dist/routers/user.js +5 -4
  150. package/dist/routers/user.js.map +1 -1
  151. package/dist/routers/worksheet.d.ts +30 -36
  152. package/dist/routers/worksheet.d.ts.map +1 -1
  153. package/dist/routers/worksheet.js +11 -33
  154. package/dist/routers/worksheet.js.map +1 -1
  155. package/dist/seedDatabase.d.ts +1 -1
  156. package/dist/seedDatabase.js +275 -284
  157. package/dist/seedDatabase.js.map +1 -1
  158. package/dist/server/pipelines/aiLabChat.d.ts +10 -0
  159. package/dist/server/pipelines/aiLabChat.d.ts.map +1 -0
  160. package/dist/server/pipelines/aiLabChat.js +83 -0
  161. package/dist/server/pipelines/aiLabChat.js.map +1 -0
  162. package/dist/server/pipelines/gradeWorksheet.d.ts +2 -0
  163. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -0
  164. package/dist/server/pipelines/gradeWorksheet.js +138 -0
  165. package/dist/server/pipelines/gradeWorksheet.js.map +1 -0
  166. package/dist/trpc.d.ts.map +1 -1
  167. package/dist/trpc.js +2 -2
  168. package/dist/trpc.js.map +1 -1
  169. package/dist/utils/email.d.ts +9 -1
  170. package/dist/utils/email.d.ts.map +1 -1
  171. package/dist/utils/email.js +20 -5
  172. package/dist/utils/email.js.map +1 -1
  173. package/dist/utils/inference.d.ts +3 -0
  174. package/dist/utils/inference.d.ts.map +1 -1
  175. package/dist/utils/inference.js +41 -7
  176. package/dist/utils/inference.js.map +1 -1
  177. package/dist/utils/logger.d.ts.map +1 -1
  178. package/dist/utils/logger.js +3 -3
  179. package/dist/utils/logger.js.map +1 -1
  180. package/docker-compose.yml +14 -0
  181. package/package.json +13 -4
  182. package/prisma/schema.prisma +32 -5
  183. package/scripts/test-pre-push.ts +14 -0
  184. package/src/index.ts +98 -54
  185. package/src/instrument.ts +13 -6
  186. package/src/lib/config/env.ts +126 -0
  187. package/src/lib/fileUpload.ts +3 -2
  188. package/src/lib/googleCloudStorage.ts +6 -6
  189. package/src/lib/jsonConversion.ts +12 -14
  190. package/src/lib/prisma.ts +23 -2
  191. package/src/lib/pusher.ts +6 -5
  192. package/src/middleware/auth.ts +4 -3
  193. package/src/middleware/security.ts +80 -0
  194. package/src/routers/_app.ts +2 -0
  195. package/src/routers/agenda.ts +10 -7
  196. package/src/routers/announcement.ts +4 -2
  197. package/src/routers/assignment.ts +58 -40
  198. package/src/routers/attendance.ts +2 -2
  199. package/src/routers/auth.ts +143 -14
  200. package/src/routers/class.ts +52 -3
  201. package/src/routers/conversation.ts +49 -29
  202. package/src/routers/file.ts +29 -5
  203. package/src/routers/labChat.ts +0 -1
  204. package/src/routers/newtonChat.ts +520 -0
  205. package/src/routers/section.ts +6 -6
  206. package/src/routers/user.ts +3 -2
  207. package/src/routers/worksheet.ts +9 -37
  208. package/src/seedDatabase.ts +290 -283
  209. package/src/server/pipelines/aiLabChat.ts +92 -0
  210. package/src/server/pipelines/gradeWorksheet.ts +152 -0
  211. package/src/trpc.ts +2 -0
  212. package/src/utils/email.ts +30 -3
  213. package/src/utils/inference.ts +50 -5
  214. package/src/utils/logger.ts +2 -1
  215. package/tests/announcement.test.ts +164 -0
  216. package/tests/assignment.test.ts +296 -0
  217. package/tests/attendance.test.ts +168 -0
  218. package/tests/auth.test.ts +33 -10
  219. package/tests/class.test.ts +34 -9
  220. package/tests/event.test.ts +228 -0
  221. package/tests/section.test.ts +216 -0
  222. package/tests/setup.ts +70 -16
  223. package/tests/user.test.ts +158 -0
  224. package/vitest.config.ts +26 -0
  225. package/API_SPECIFICATION.md +0 -1597
  226. package/BASE64_REMOVAL_SUMMARY.md +0 -164
  227. package/CHAT_API_SPEC.md +0 -579
  228. package/LAB_CHAT_API_SPEC.md +0 -518
  229. package/dist/routers/school.d.ts +0 -208
  230. package/dist/routers/school.d.ts.map +0 -1
  231. package/dist/routers/school.js +0 -483
@@ -18,6 +18,7 @@ export declare const attendanceRouter: import("@trpc/server").TRPCBuiltRouter<{
18
18
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
19
19
  get: import("@trpc/server").TRPCQueryProcedure<{
20
20
  input: {
21
+ [x: string]: unknown;
21
22
  classId: string;
22
23
  eventId?: string | undefined;
23
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"attendance.d.ts","sourceRoot":"/","sources":["routers/attendance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+S3B,CAAC"}
1
+ {"version":3,"file":"attendance.d.ts","sourceRoot":"/","sources":["routers/attendance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+S3B,CAAC"}
@@ -1,7 +1,7 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ce2d9e44-026e-5dd6-b4dd-ee6d4ff41a9e")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c09b7dfb-98b8-5c82-80fb-5f491b530836")}catch(e){}}();
3
3
  import { z } from "zod";
4
- import { createTRPCRouter, protectedProcedure } from "../trpc.js";
4
+ import { createTRPCRouter, protectedClassMemberProcedure, protectedProcedure } from "../trpc.js";
5
5
  import { TRPCError } from "@trpc/server";
6
6
  import { prisma } from "../lib/prisma.js";
7
7
  const attendanceSchema = z.object({
@@ -11,7 +11,7 @@ const attendanceSchema = z.object({
11
11
  absent: z.array(z.object({ id: z.string(), username: z.string() })),
12
12
  });
13
13
  export const attendanceRouter = createTRPCRouter({
14
- get: protectedProcedure
14
+ get: protectedClassMemberProcedure
15
15
  .input(z.object({
16
16
  classId: z.string(),
17
17
  eventId: z.string().optional(),
@@ -299,4 +299,4 @@ export const attendanceRouter = createTRPCRouter({
299
299
  }),
300
300
  });
301
301
  //# sourceMappingURL=attendance.js.map
302
- //# debugId=ce2d9e44-026e-5dd6-b4dd-ee6d4ff41a9e
302
+ //# debugId=c09b7dfb-98b8-5c82-80fb-5f491b530836
@@ -1 +1 @@
1
- {"version":3,"file":"attendance.js","sources":["routers/attendance.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport { createTRPCRouter, protectedProcedure } from \"../trpc.js\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\n\nconst attendanceSchema = z.object({\n eventId: z.string().optional(),\n present: z.array(z.object({ id: z.string(), username: z.string() })),\n late: z.array(z.object({ id: z.string(), username: z.string() })),\n absent: z.array(z.object({ id: z.string(), username: z.string() })),\n});\n\nexport const attendanceRouter = createTRPCRouter({\n get: protectedProcedure\n .input(z.object({\n classId: z.string(),\n eventId: z.string().optional(),\n }))\n .query(async ({ ctx, input }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You must be logged in to view attendance\",\n });\n }\n\n // Check if user is a teacher or student of the class\n const classData = await prisma.class.findUnique({\n where: {\n id: input.classId,\n OR: [\n {\n teachers: {\n some: {\n id: ctx.user.id,\n },\n },\n },\n {\n students: {\n some: {\n id: ctx.user.id,\n },\n },\n },\n ],\n },\n select: {\n students: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n bio: true,\n location: true,\n website: true,\n },\n },\n },\n },\n },\n });\n\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this class's attendance\",\n });\n }\n\n // check each event has an attendance, if not create one\n const events = await prisma.event.findMany({\n where: {\n classId: input.classId,\n },\n });\n \n for (const event of events) {\n const attendance = await prisma.attendance.findFirst({\n where: {\n eventId: event.id,\n },\n });\n \n if (!attendance) {\n await prisma.attendance.create({\n data: {\n event: {\n connect: {\n id: event.id,\n },\n },\n class: {\n connect: {\n id: input.classId,\n },\n },\n present: {\n connect: classData.students.map(student => ({ id: student.id })),\n },\n },\n });\n }\n }\n\n\n const attendance = await prisma.attendance.findMany({\n where: {\n classId: input.classId,\n ...(input.eventId ? { eventId: input.eventId } : {}),\n },\n include: {\n event: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n color: true,\n },\n },\n present: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n late: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n absent: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n },\n orderBy: {\n date: \"desc\",\n },\n });\n\n return attendance;\n }),\n\n update: protectedProcedure\n .input(z.object({\n classId: z.string(),\n eventId: z.string().optional(),\n attendance: attendanceSchema,\n }))\n .mutation(async ({ ctx, input }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You must be logged in to update attendance\",\n });\n }\n\n // Check if user is a teacher of the class\n const classData = await prisma.class.findUnique({\n where: {\n id: input.classId,\n teachers: {\n some: {\n id: ctx.user.id,\n },\n },\n },\n });\n\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this class's attendance\",\n });\n }\n\n // Check if attendance record exists\n const existingAttendance = await prisma.attendance.findFirst({\n where: {\n classId: input.classId,\n eventId: input.eventId,\n },\n });\n\n if (!existingAttendance) {\n // Create new attendance record\n const attendance = await prisma.attendance.create({\n data: {\n classId: input.classId,\n eventId: input.eventId,\n date: new Date(),\n present: {\n connect: input.attendance.present.map(student => ({ id: student.id })),\n },\n late: {\n connect: input.attendance.late.map(student => ({ id: student.id })),\n },\n absent: {\n connect: input.attendance.absent.map(student => ({ id: student.id })),\n },\n },\n include: {\n event: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n },\n },\n present: {\n select: {\n id: true,\n username: true,\n },\n },\n late: {\n select: {\n id: true,\n username: true,\n },\n },\n absent: {\n select: {\n id: true,\n username: true,\n },\n },\n },\n });\n\n return attendance;\n }\n\n // Update existing attendance record\n const attendance = await prisma.attendance.update({\n where: {\n id: existingAttendance.id,\n },\n data: {\n present: {\n set: input.attendance.present.map(student => ({ id: student.id })),\n },\n late: {\n set: input.attendance.late.map(student => ({ id: student.id })),\n },\n absent: {\n set: input.attendance.absent.map(student => ({ id: student.id })),\n },\n },\n include: {\n event: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n },\n },\n present: {\n select: {\n id: true,\n username: true,\n },\n },\n late: {\n select: {\n id: true,\n username: true,\n },\n },\n absent: {\n select: {\n id: true,\n username: true,\n },\n },\n },\n });\n\n return attendance;\n }),\n}); "],"names":[],"mappings":"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","debug_id":"ce2d9e44-026e-5dd6-b4dd-ee6d4ff41a9e"}
1
+ {"version":3,"file":"attendance.js","sources":["routers/attendance.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport { createTRPCRouter, protectedClassMemberProcedure, protectedProcedure } from \"../trpc.js\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\n\nconst attendanceSchema = z.object({\n eventId: z.string().optional(),\n present: z.array(z.object({ id: z.string(), username: z.string() })),\n late: z.array(z.object({ id: z.string(), username: z.string() })),\n absent: z.array(z.object({ id: z.string(), username: z.string() })),\n});\n\nexport const attendanceRouter = createTRPCRouter({\n get: protectedClassMemberProcedure\n .input(z.object({\n classId: z.string(),\n eventId: z.string().optional(),\n }))\n .query(async ({ ctx, input }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You must be logged in to view attendance\",\n });\n }\n\n // Check if user is a teacher or student of the class\n const classData = await prisma.class.findUnique({\n where: {\n id: input.classId,\n OR: [\n {\n teachers: {\n some: {\n id: ctx.user.id,\n },\n },\n },\n {\n students: {\n some: {\n id: ctx.user.id,\n },\n },\n },\n ],\n },\n select: {\n students: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n bio: true,\n location: true,\n website: true,\n },\n },\n },\n },\n },\n });\n\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this class's attendance\",\n });\n }\n\n // check each event has an attendance, if not create one\n const events = await prisma.event.findMany({\n where: {\n classId: input.classId,\n },\n });\n \n for (const event of events) {\n const attendance = await prisma.attendance.findFirst({\n where: {\n eventId: event.id,\n },\n });\n \n if (!attendance) {\n await prisma.attendance.create({\n data: {\n event: {\n connect: {\n id: event.id,\n },\n },\n class: {\n connect: {\n id: input.classId,\n },\n },\n present: {\n connect: classData.students.map(student => ({ id: student.id })),\n },\n },\n });\n }\n }\n\n\n const attendance = await prisma.attendance.findMany({\n where: {\n classId: input.classId,\n ...(input.eventId ? { eventId: input.eventId } : {}),\n },\n include: {\n event: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n color: true,\n },\n },\n present: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n late: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n absent: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n },\n orderBy: {\n date: \"desc\",\n },\n });\n\n return attendance;\n }),\n\n update: protectedProcedure\n .input(z.object({\n classId: z.string(),\n eventId: z.string().optional(),\n attendance: attendanceSchema,\n }))\n .mutation(async ({ ctx, input }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You must be logged in to update attendance\",\n });\n }\n\n // Check if user is a teacher of the class\n const classData = await prisma.class.findUnique({\n where: {\n id: input.classId,\n teachers: {\n some: {\n id: ctx.user.id,\n },\n },\n },\n });\n\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this class's attendance\",\n });\n }\n\n // Check if attendance record exists\n const existingAttendance = await prisma.attendance.findFirst({\n where: {\n classId: input.classId,\n eventId: input.eventId,\n },\n });\n\n if (!existingAttendance) {\n // Create new attendance record\n const attendance = await prisma.attendance.create({\n data: {\n classId: input.classId,\n eventId: input.eventId,\n date: new Date(),\n present: {\n connect: input.attendance.present.map(student => ({ id: student.id })),\n },\n late: {\n connect: input.attendance.late.map(student => ({ id: student.id })),\n },\n absent: {\n connect: input.attendance.absent.map(student => ({ id: student.id })),\n },\n },\n include: {\n event: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n },\n },\n present: {\n select: {\n id: true,\n username: true,\n },\n },\n late: {\n select: {\n id: true,\n username: true,\n },\n },\n absent: {\n select: {\n id: true,\n username: true,\n },\n },\n },\n });\n\n return attendance;\n }\n\n // Update existing attendance record\n const attendance = await prisma.attendance.update({\n where: {\n id: existingAttendance.id,\n },\n data: {\n present: {\n set: input.attendance.present.map(student => ({ id: student.id })),\n },\n late: {\n set: input.attendance.late.map(student => ({ id: student.id })),\n },\n absent: {\n set: input.attendance.absent.map(student => ({ id: student.id })),\n },\n },\n include: {\n event: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n },\n },\n present: {\n select: {\n id: true,\n username: true,\n },\n },\n late: {\n select: {\n id: true,\n username: true,\n },\n },\n absent: {\n select: {\n id: true,\n username: true,\n },\n },\n },\n });\n\n return attendance;\n }),\n}); "],"names":[],"mappings":"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","debug_id":"c09b7dfb-98b8-5c82-80fb-5f491b530836"}
@@ -98,5 +98,25 @@ export declare const authRouter: import("@trpc/server").TRPCBuiltRouter<{
98
98
  };
99
99
  meta: object;
100
100
  }>;
101
+ requestPasswordReset: import("@trpc/server").TRPCMutationProcedure<{
102
+ input: {
103
+ email: string;
104
+ };
105
+ output: {
106
+ success: boolean;
107
+ };
108
+ meta: object;
109
+ }>;
110
+ resetPassword: import("@trpc/server").TRPCMutationProcedure<{
111
+ input: {
112
+ password: string;
113
+ confirmPassword: string;
114
+ token: string;
115
+ };
116
+ output: {
117
+ success: boolean;
118
+ };
119
+ meta: object;
120
+ }>;
101
121
  }>>;
102
122
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["routers/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAwBxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkTrB,CAAC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["routers/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA0BxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAibrB,CAAC"}
@@ -1,12 +1,15 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7c7b7319-88a0-5018-a81e-1b898acd6080")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7ea9b87a-bc47-5078-9339-3ed6c2b60d00")}catch(e){}}();
3
3
  import { z } from "zod";
4
4
  import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc.js";
5
5
  import { TRPCError } from "@trpc/server";
6
6
  import { prisma } from "../lib/prisma.js";
7
7
  import { v4 as uuidv4 } from 'uuid';
8
8
  import { compare, hash } from "bcryptjs";
9
+ import { sendMail } from "../utils/email.js";
9
10
  import { prismaWrapper } from "../utils/prismaWrapper.js";
11
+ import { env } from "../lib/config/env.js";
12
+ import { logger } from "../utils/logger.js";
10
13
  const loginSchema = z.object({
11
14
  username: z.string(),
12
15
  password: z.string(),
@@ -84,13 +87,18 @@ export const authRouter = createTRPCRouter({
84
87
  expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
85
88
  },
86
89
  }), 'creating verification token');
87
- // await transport.sendMail({
88
- // from: 'noreply@studious.sh',
89
- // to: user.email,
90
- // subject: 'Verify your email',
91
- // text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
92
- // });
93
- console.log(`${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`);
90
+ try {
91
+ await sendMail({
92
+ from: 'noreply@studious.sh',
93
+ to: user.email,
94
+ subject: 'Verify your email',
95
+ text: `Click the link to verify your email: ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
96
+ });
97
+ }
98
+ catch (err) {
99
+ logger.error('Failed to send verification email', { email: user.email, err });
100
+ }
101
+ // logger.info(`Password verification email sent to ${user.email} at ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`);
94
102
  return {
95
103
  user: {
96
104
  id: user.id,
@@ -236,12 +244,18 @@ export const authRouter = createTRPCRouter({
236
244
  expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
237
245
  },
238
246
  });
239
- // await transport.sendMail({
240
- // from: 'noreply@studious.sh',
241
- // to: user.email,
242
- // subject: 'Verify your email',
243
- // text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
244
- // });
247
+ try {
248
+ await sendMail({
249
+ from: 'noreply@studious.sh',
250
+ to: user.email,
251
+ subject: 'Verify your email',
252
+ text: `Click the link to verify your email: ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
253
+ });
254
+ }
255
+ catch (err) {
256
+ logger.error('Failed to send verification email', { email: user.email, err });
257
+ }
258
+ // logger.info(`Password verification email sent to ${user.email} at ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`);
245
259
  return { success: true };
246
260
  }),
247
261
  verify: publicProcedure
@@ -277,6 +291,109 @@ export const authRouter = createTRPCRouter({
277
291
  });
278
292
  return { success: true };
279
293
  }),
294
+ requestPasswordReset: publicProcedure
295
+ .input(z.object({
296
+ email: z.string().email(),
297
+ }))
298
+ .mutation(async ({ input }) => {
299
+ const { email } = input;
300
+ const user = await prisma.user.findFirst({
301
+ where: { email },
302
+ select: {
303
+ id: true,
304
+ email: true,
305
+ username: true,
306
+ },
307
+ });
308
+ // Don't reveal if user exists or not for security
309
+ if (!user) {
310
+ return { success: true };
311
+ }
312
+ // Delete any existing password reset tokens for this user
313
+ // Only delete tokens that expire within 2 hours (likely password reset tokens)
314
+ const twoHoursFromNow = new Date(Date.now() + 1000 * 60 * 60 * 2);
315
+ await prisma.session.deleteMany({
316
+ where: {
317
+ userId: user.id,
318
+ classId: null,
319
+ expiresAt: {
320
+ lte: twoHoursFromNow, // Only delete short-lived tokens (password reset tokens)
321
+ },
322
+ },
323
+ });
324
+ // Create a new password reset token (expires in 1 hour)
325
+ const resetToken = await prisma.session.create({
326
+ data: {
327
+ id: uuidv4(),
328
+ userId: user.id,
329
+ expiresAt: new Date(Date.now() + 1000 * 60 * 60), // 1 hour
330
+ },
331
+ });
332
+ // Send password reset email
333
+ try {
334
+ await sendMail({
335
+ from: 'noreply@studious.sh',
336
+ to: user.email,
337
+ subject: 'Reset your password',
338
+ text: `Click the link to reset your password: ${env.NEXT_PUBLIC_APP_URL}/reset-password/${resetToken.id}`,
339
+ });
340
+ }
341
+ catch (err) {
342
+ logger.error('Failed to send password reset email', { email: user.email, err });
343
+ }
344
+ // logger.info(`Password reset email sent to ${user.email} at ${env.NEXT_PUBLIC_APP_URL}/reset-password/${resetToken.id}`);
345
+ return { success: true };
346
+ }),
347
+ resetPassword: publicProcedure
348
+ .input(z.object({
349
+ token: z.string(),
350
+ password: z.string().min(6, "Password must be at least 6 characters"),
351
+ confirmPassword: z.string(),
352
+ }).refine((data) => data.password === data.confirmPassword, {
353
+ message: "Passwords don't match",
354
+ path: ["confirmPassword"],
355
+ }))
356
+ .mutation(async ({ input }) => {
357
+ const { token, password } = input;
358
+ const session = await prisma.session.findUnique({
359
+ where: { id: token },
360
+ include: {
361
+ user: {
362
+ select: {
363
+ id: true,
364
+ },
365
+ },
366
+ },
367
+ });
368
+ if (!session || !session.userId) {
369
+ throw new TRPCError({
370
+ code: "NOT_FOUND",
371
+ message: "Invalid or expired reset token",
372
+ });
373
+ }
374
+ if (session.expiresAt && session.expiresAt < new Date()) {
375
+ // Clean up expired token
376
+ await prisma.session.delete({
377
+ where: { id: token },
378
+ });
379
+ throw new TRPCError({
380
+ code: "UNAUTHORIZED",
381
+ message: "Reset token has expired",
382
+ });
383
+ }
384
+ // Update the user's password
385
+ await prisma.user.update({
386
+ where: { id: session.userId },
387
+ data: {
388
+ password: await hash(password, 10),
389
+ },
390
+ });
391
+ // Clean up the reset token
392
+ await prisma.session.delete({
393
+ where: { id: token },
394
+ });
395
+ return { success: true };
396
+ }),
280
397
  });
281
398
  //# sourceMappingURL=auth.js.map
282
- //# debugId=7c7b7319-88a0-5018-a81e-1b898acd6080
399
+ //# debugId=7ea9b87a-bc47-5078-9339-3ed6c2b60d00
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["routers/auth.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport { createTRPCRouter, protectedProcedure, publicProcedure } from \"../trpc.js\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { v4 as uuidv4 } from 'uuid';\nimport { compare, hash } from \"bcryptjs\";\nimport { transport } from \"../utils/email.js\";\nimport { prismaWrapper } from \"../utils/prismaWrapper.js\";\n\nconst loginSchema = z.object({\n username: z.string(),\n password: z.string(),\n});\n\nconst registerSchema = z.object({\n username: z.string().min(3, \"Username must be at least 3 characters\"),\n email: z.string().email(\"Invalid email address\"),\n password: z.string().min(6, \"Password must be at least 6 characters\"),\n confirmPassword: z.string(),\n}).refine((data) => data.password === data.confirmPassword, {\n message: \"Passwords don't match\",\n path: [\"confirmPassword\"],\n});\n\nexport const authRouter = createTRPCRouter({\n register: publicProcedure\n .input(registerSchema)\n .mutation(async ({ input }) => {\n const { username, email, password } = input;\n\n // Check if username already exists\n const existingUser = await prismaWrapper.findFirst(\n () => prisma.user.findFirst({\n where: { \n OR: [\n { username },\n { email }\n ]\n },\n select: {\n id: true,\n username: true,\n email: true,\n verified: true,\n }\n }),\n 'checking for existing user during registration'\n );\n\n if (existingUser && existingUser.verified) {\n if (existingUser.username === username) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"Username already exists\",\n });\n }\n if (existingUser.email === email) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"Email already exists\",\n });\n }\n } else if (existingUser && !existingUser.verified) {\n await prismaWrapper.deleteMany(\n () => prisma.session.deleteMany({\n where: { userId: existingUser.id },\n }),\n 'deleting existing sessions for unverified user'\n );\n\n await prismaWrapper.delete(\n () => prisma.user.delete({\n where: { id: existingUser.id },\n }),\n 'deleting unverified user'\n );\n }\n\n // Create new user\n const user = await prismaWrapper.create(\n async () => await prisma.user.create({\n data: {\n username,\n email,\n password: await hash(password, 10),\n profile: {},\n verified: true, // temporary\n },\n select: {\n id: true,\n username: true,\n email: true,\n }\n }),\n 'creating new user during registration'\n );\n\n const verificationToken = await prismaWrapper.create(\n () => prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n },\n }),\n 'creating verification token'\n );\n\n // await transport.sendMail({\n // from: 'noreply@studious.sh',\n // to: user.email,\n // subject: 'Verify your email',\n // text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,\n // });\n\n console.log(`${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`)\n\n return {\n user: {\n id: user.id,\n username: user.username,\n },\n };\n }),\n\n login: publicProcedure\n .input(loginSchema)\n .mutation(async ({ input }) => {\n const { username, password } = input;\n\n const user = await prisma.user.findFirst({\n where: { username },\n select: {\n id: true,\n username: true,\n password: true,\n email: true,\n verified: true,\n }\n });\n\n if (!user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Invalid username or password\",\n });\n }\n\n if (!(await compare(password, user.password))) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Invalid username or password\",\n });\n }\n\n if (!user.verified) {\n return {\n verified: false,\n user: {\n email: user.email,\n },\n }\n }\n\n // Create a new session\n const session = await prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n },\n });\n\n return {\n token: session.id,\n user: {\n id: user.id,\n username: user.username,\n },\n };\n }),\n\n logout: protectedProcedure\n .mutation(async ({ ctx }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n // Delete the current session\n await prisma.session.deleteMany({\n where: { userId: ctx.user.id },\n });\n\n return { success: true };\n }),\n\n\n check: protectedProcedure\n .query(async ({ ctx }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const user = await prisma.user.findUnique({\n where: { id: ctx.user.id },\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n bio: true,\n location: true,\n website: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n }\n });\n\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n return {user};\n }),\n resendVerificationEmail: publicProcedure\n .input(z.object({\n email: z.string().email(),\n }))\n .mutation(async ({ input }) => {\n const { email } = input;\n\n const user = await prisma.user.findFirst({\n where: { \n email,\n },\n select: {\n id: true,\n email: true,\n role: true,\n profile: {\n select: {\n displayName: true,\n bio: true,\n location: true,\n website: true,\n profilePicture: true,\n },\n },\n },\n });\n\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n await prisma.session.deleteMany({\n where: { userId: user?.id },\n });\n\n const verificationToken = await prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n },\n });\n \n // await transport.sendMail({\n // from: 'noreply@studious.sh',\n // to: user.email,\n // subject: 'Verify your email',\n // text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,\n // });\n\n return { success: true };\n }),\n verify: publicProcedure\n .input(z.object({\n token: z.string(),\n }))\n .mutation(async ({ input }) => {\n const { token } = input;\n\n const session = await prisma.session.findUnique({\n where: { id: token },\n });\n\n if (!session) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Session not found\",\n });\n }\n\n if (session.expiresAt && session.expiresAt < new Date()) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Session expired\",\n });\n }\n\n await prisma.user.update({\n where: { id: session.userId! },\n data: {\n verified: true,\n },\n });\n\n // Clean up the verification token\n await prisma.session.delete({\n where: { id: token },\n });\n\n return { success: true };\n }),\n}); "],"names":[],"mappings":"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","debug_id":"7c7b7319-88a0-5018-a81e-1b898acd6080"}
1
+ {"version":3,"file":"auth.js","sources":["routers/auth.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport { createTRPCRouter, protectedProcedure, publicProcedure } from \"../trpc.js\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { v4 as uuidv4 } from 'uuid';\nimport { compare, hash } from \"bcryptjs\";\nimport { sendMail } from \"../utils/email.js\";\nimport { prismaWrapper } from \"../utils/prismaWrapper.js\";\nimport { env } from \"../lib/config/env.js\";\nimport { logger } from \"../utils/logger.js\";\n\nconst loginSchema = z.object({\n username: z.string(),\n password: z.string(),\n});\n\nconst registerSchema = z.object({\n username: z.string().min(3, \"Username must be at least 3 characters\"),\n email: z.string().email(\"Invalid email address\"),\n password: z.string().min(6, \"Password must be at least 6 characters\"),\n confirmPassword: z.string(),\n}).refine((data) => data.password === data.confirmPassword, {\n message: \"Passwords don't match\",\n path: [\"confirmPassword\"],\n});\n\nexport const authRouter = createTRPCRouter({\n register: publicProcedure\n .input(registerSchema)\n .mutation(async ({ input }) => {\n const { username, email, password } = input;\n\n // Check if username already exists\n const existingUser = await prismaWrapper.findFirst(\n () => prisma.user.findFirst({\n where: { \n OR: [\n { username },\n { email }\n ]\n },\n select: {\n id: true,\n username: true,\n email: true,\n verified: true,\n }\n }),\n 'checking for existing user during registration'\n );\n\n if (existingUser && existingUser.verified) {\n if (existingUser.username === username) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"Username already exists\",\n });\n }\n if (existingUser.email === email) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"Email already exists\",\n });\n }\n } else if (existingUser && !existingUser.verified) {\n await prismaWrapper.deleteMany(\n () => prisma.session.deleteMany({\n where: { userId: existingUser.id },\n }),\n 'deleting existing sessions for unverified user'\n );\n\n await prismaWrapper.delete(\n () => prisma.user.delete({\n where: { id: existingUser.id },\n }),\n 'deleting unverified user'\n );\n }\n\n // Create new user\n const user = await prismaWrapper.create(\n async () => await prisma.user.create({\n data: {\n username,\n email,\n password: await hash(password, 10),\n profile: {},\n verified: true, // temporary\n },\n select: {\n id: true,\n username: true,\n email: true,\n }\n }),\n 'creating new user during registration'\n );\n\n const verificationToken = await prismaWrapper.create(\n () => prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n },\n }),\n 'creating verification token'\n );\n\n try {\n await sendMail({\n from: 'noreply@studious.sh',\n to: user.email,\n subject: 'Verify your email',\n text: `Click the link to verify your email: ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,\n });\n } catch (err) {\n logger.error('Failed to send verification email', { email: user.email, err });\n }\n\n // logger.info(`Password verification email sent to ${user.email} at ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`);\n\n return {\n user: {\n id: user.id,\n username: user.username,\n },\n };\n }),\n\n login: publicProcedure\n .input(loginSchema)\n .mutation(async ({ input }) => {\n const { username, password } = input;\n\n const user = await prisma.user.findFirst({\n where: { username },\n select: {\n id: true,\n username: true,\n password: true,\n email: true,\n verified: true,\n }\n });\n\n if (!user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Invalid username or password\",\n });\n }\n\n if (!(await compare(password, user.password))) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Invalid username or password\",\n });\n }\n\n if (!user.verified) {\n return {\n verified: false,\n user: {\n email: user.email,\n },\n }\n }\n\n // Create a new session\n const session = await prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n },\n });\n\n return {\n token: session.id,\n user: {\n id: user.id,\n username: user.username,\n },\n };\n }),\n\n logout: protectedProcedure\n .mutation(async ({ ctx }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n // Delete the current session\n await prisma.session.deleteMany({\n where: { userId: ctx.user.id },\n });\n\n return { success: true };\n }),\n\n\n check: protectedProcedure\n .query(async ({ ctx }) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const user = await prisma.user.findUnique({\n where: { id: ctx.user.id },\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n bio: true,\n location: true,\n website: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n }\n });\n\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n return {user};\n }),\n resendVerificationEmail: publicProcedure\n .input(z.object({\n email: z.string().email(),\n }))\n .mutation(async ({ input }) => {\n const { email } = input;\n\n const user = await prisma.user.findFirst({\n where: { \n email,\n },\n select: {\n id: true,\n email: true,\n role: true,\n profile: {\n select: {\n displayName: true,\n bio: true,\n location: true,\n website: true,\n profilePicture: true,\n },\n },\n },\n });\n\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n await prisma.session.deleteMany({\n where: { userId: user?.id },\n });\n\n const verificationToken = await prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n },\n });\n \n try {\n await sendMail({\n from: 'noreply@studious.sh',\n to: user.email,\n subject: 'Verify your email',\n text: `Click the link to verify your email: ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,\n });\n } catch (err) {\n logger.error('Failed to send verification email', { email: user.email, err });\n }\n\n // logger.info(`Password verification email sent to ${user.email} at ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`);\n\n return { success: true };\n }),\n verify: publicProcedure\n .input(z.object({\n token: z.string(),\n }))\n .mutation(async ({ input }) => {\n const { token } = input;\n\n const session = await prisma.session.findUnique({\n where: { id: token },\n });\n\n if (!session) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Session not found\",\n });\n }\n\n if (session.expiresAt && session.expiresAt < new Date()) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Session expired\",\n });\n }\n\n await prisma.user.update({\n where: { id: session.userId! },\n data: {\n verified: true,\n },\n });\n\n // Clean up the verification token\n await prisma.session.delete({\n where: { id: token },\n });\n\n return { success: true };\n }),\n\n requestPasswordReset: publicProcedure\n .input(z.object({\n email: z.string().email(),\n }))\n .mutation(async ({ input }) => {\n const { email } = input;\n\n const user = await prisma.user.findFirst({\n where: { email },\n select: {\n id: true,\n email: true,\n username: true,\n },\n });\n\n // Don't reveal if user exists or not for security\n if (!user) {\n return { success: true };\n }\n\n // Delete any existing password reset tokens for this user\n // Only delete tokens that expire within 2 hours (likely password reset tokens)\n const twoHoursFromNow = new Date(Date.now() + 1000 * 60 * 60 * 2);\n await prisma.session.deleteMany({\n where: { \n userId: user.id,\n classId: null,\n expiresAt: {\n lte: twoHoursFromNow, // Only delete short-lived tokens (password reset tokens)\n },\n },\n });\n\n // Create a new password reset token (expires in 1 hour)\n const resetToken = await prisma.session.create({\n data: {\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60), // 1 hour\n },\n });\n\n // Send password reset email\n try {\n await sendMail({\n from: 'noreply@studious.sh',\n to: user.email,\n subject: 'Reset your password',\n text: `Click the link to reset your password: ${env.NEXT_PUBLIC_APP_URL}/reset-password/${resetToken.id}`,\n });\n } catch (err) {\n logger.error('Failed to send password reset email', { email: user.email, err });\n }\n\n // logger.info(`Password reset email sent to ${user.email} at ${env.NEXT_PUBLIC_APP_URL}/reset-password/${resetToken.id}`);\n\n return { success: true };\n }),\n\n resetPassword: publicProcedure\n .input(z.object({\n token: z.string(),\n password: z.string().min(6, \"Password must be at least 6 characters\"),\n confirmPassword: z.string(),\n }).refine((data) => data.password === data.confirmPassword, {\n message: \"Passwords don't match\",\n path: [\"confirmPassword\"],\n }))\n .mutation(async ({ input }) => {\n const { token, password } = input;\n\n const session = await prisma.session.findUnique({\n where: { id: token },\n include: {\n user: {\n select: {\n id: true,\n },\n },\n },\n });\n\n if (!session || !session.userId) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Invalid or expired reset token\",\n });\n }\n\n if (session.expiresAt && session.expiresAt < new Date()) {\n // Clean up expired token\n await prisma.session.delete({\n where: { id: token },\n });\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Reset token has expired\",\n });\n }\n\n // Update the user's password\n await prisma.user.update({\n where: { id: session.userId },\n data: {\n password: await hash(password, 10),\n },\n });\n\n // Clean up the reset token\n await prisma.session.delete({\n where: { id: token },\n });\n\n return { success: true };\n }),\n}); "],"names":[],"mappings":"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","debug_id":"7ea9b87a-bc47-5078-9339-3ed6c2b60d00"}
@@ -276,6 +276,16 @@ export declare const classRouter: import("@trpc/server").TRPCBuiltRouter<{
276
276
  };
277
277
  meta: object;
278
278
  }>;
279
+ leaveClass: import("@trpc/server").TRPCMutationProcedure<{
280
+ input: {
281
+ classId: string;
282
+ };
283
+ output: {
284
+ success: boolean;
285
+ leftClassId: string;
286
+ };
287
+ meta: object;
288
+ }>;
279
289
  join: import("@trpc/server").TRPCMutationProcedure<{
280
290
  input: {
281
291
  classCode: string;
@@ -1 +1 @@
1
- {"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["routers/class.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwnCtB,CAAC"}
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["routers/class.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyqCtB,CAAC"}
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e1b81886-864f-565b-bfd4-84d4f7de1529")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="90bd2ed8-f6aa-5bb5-b0f5-4d2643587610")}catch(e){}}();
3
3
  import { z } from "zod";
4
4
  import { createTRPCRouter, protectedProcedure, protectedTeacherProcedure, protectedClassMemberProcedure } from "../trpc.js";
5
5
  import { prisma } from "../lib/prisma.js";
@@ -439,15 +439,59 @@ export const classRouter = createTRPCRouter({
439
439
  removedUserId: userId,
440
440
  };
441
441
  }),
442
+ leaveClass: protectedProcedure
443
+ .input(z.object({
444
+ classId: z.string(),
445
+ }))
446
+ .mutation(async ({ ctx, input }) => {
447
+ const { classId } = input;
448
+ const userId = ctx.user?.id;
449
+ if (!userId) {
450
+ throw new TRPCError({
451
+ code: 'UNAUTHORIZED',
452
+ message: 'User not authenticated',
453
+ });
454
+ }
455
+ const classData = await prisma.class.findFirst({
456
+ where: {
457
+ id: classId,
458
+ students: {
459
+ some: { id: userId },
460
+ },
461
+ },
462
+ });
463
+ if (!classData) {
464
+ throw new TRPCError({
465
+ code: 'NOT_FOUND',
466
+ message: 'Class not found or you are not a student in this class',
467
+ });
468
+ }
469
+ await prisma.class.update({
470
+ where: { id: classId },
471
+ data: {
472
+ students: {
473
+ disconnect: { id: userId },
474
+ },
475
+ },
476
+ });
477
+ return {
478
+ success: true,
479
+ leftClassId: classId,
480
+ };
481
+ }),
442
482
  join: protectedProcedure
443
483
  .input(z.object({
444
484
  classCode: z.string(),
445
485
  }))
446
486
  .mutation(async ({ ctx, input }) => {
447
487
  const { classCode } = input;
488
+ // Case-insensitive search for invite code
448
489
  const session = await prisma.session.findFirst({
449
490
  where: {
450
- id: classCode,
491
+ id: {
492
+ equals: classCode,
493
+ mode: 'insensitive',
494
+ },
451
495
  },
452
496
  });
453
497
  if (!session || !session.classId) {
@@ -632,7 +676,7 @@ export const classRouter = createTRPCRouter({
632
676
  });
633
677
  return events;
634
678
  }),
635
- listMarkSchemes: protectedTeacherProcedure
679
+ listMarkSchemes: protectedClassMemberProcedure
636
680
  .input(z.object({
637
681
  classId: z.string(),
638
682
  }))
@@ -701,7 +745,7 @@ export const classRouter = createTRPCRouter({
701
745
  });
702
746
  return markScheme;
703
747
  }),
704
- listGradingBoundaries: protectedTeacherProcedure
748
+ listGradingBoundaries: protectedClassMemberProcedure
705
749
  .input(z.object({
706
750
  classId: z.string(),
707
751
  }))
@@ -1065,4 +1109,4 @@ export const classRouter = createTRPCRouter({
1065
1109
  }),
1066
1110
  });
1067
1111
  //# sourceMappingURL=class.js.map
1068
- //# debugId=e1b81886-864f-565b-bfd4-84d4f7de1529
1112
+ //# debugId=90bd2ed8-f6aa-5bb5-b0f5-4d2643587610