@studious-lms/server 1.4.2 → 1.4.3

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 (145) hide show
  1. package/.coderabbit.yaml +4 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +67 -28
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/fileUpload.d.ts +10 -2
  6. package/dist/lib/fileUpload.d.ts.map +1 -1
  7. package/dist/lib/fileUpload.js +38 -6
  8. package/dist/lib/fileUpload.js.map +1 -1
  9. package/dist/middleware/auth.d.ts.map +1 -1
  10. package/dist/middleware/auth.js +18 -5
  11. package/dist/middleware/auth.js.map +1 -1
  12. package/dist/models/auth.d.ts +19 -14
  13. package/dist/models/auth.d.ts.map +1 -1
  14. package/dist/models/auth.js +53 -30
  15. package/dist/models/auth.js.map +1 -1
  16. package/dist/models/class.d.ts +3 -1
  17. package/dist/models/class.d.ts.map +1 -1
  18. package/dist/models/class.js +16 -4
  19. package/dist/models/class.js.map +1 -1
  20. package/dist/models/notification.d.ts +11 -10
  21. package/dist/models/notification.d.ts.map +1 -1
  22. package/dist/models/notification.js +13 -9
  23. package/dist/models/notification.js.map +1 -1
  24. package/dist/models/worksheet.d.ts +378 -7
  25. package/dist/models/worksheet.d.ts.map +1 -1
  26. package/dist/models/worksheet.js +168 -2
  27. package/dist/models/worksheet.js.map +1 -1
  28. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  29. package/dist/pipelines/aiLabChat.js +11 -3
  30. package/dist/pipelines/aiLabChat.js.map +1 -1
  31. package/dist/pipelines/aiLabChatContract.d.ts +3 -3
  32. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
  33. package/dist/pipelines/aiNewtonChat.js +25 -14
  34. package/dist/pipelines/aiNewtonChat.js.map +1 -1
  35. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
  36. package/dist/pipelines/gradeWorksheet.js +10 -3
  37. package/dist/pipelines/gradeWorksheet.js.map +1 -1
  38. package/dist/routers/_app.d.ts +124 -18
  39. package/dist/routers/_app.d.ts.map +1 -1
  40. package/dist/routers/class.d.ts +2 -1
  41. package/dist/routers/class.d.ts.map +1 -1
  42. package/dist/routers/class.js +3 -3
  43. package/dist/routers/class.js.map +1 -1
  44. package/dist/routers/labChat.d.ts +6 -0
  45. package/dist/routers/labChat.d.ts.map +1 -1
  46. package/dist/routers/labChat.js +5 -3
  47. package/dist/routers/labChat.js.map +1 -1
  48. package/dist/routers/message.d.ts +31 -0
  49. package/dist/routers/message.d.ts.map +1 -1
  50. package/dist/routers/message.js +27 -4
  51. package/dist/routers/message.js.map +1 -1
  52. package/dist/routers/newtonChat.d.ts +6 -0
  53. package/dist/routers/newtonChat.d.ts.map +1 -1
  54. package/dist/routers/newtonChat.js +5 -4
  55. package/dist/routers/newtonChat.js.map +1 -1
  56. package/dist/routers/notifications.d.ts +9 -2
  57. package/dist/routers/notifications.d.ts.map +1 -1
  58. package/dist/routers/notifications.js +4 -4
  59. package/dist/routers/notifications.js.map +1 -1
  60. package/dist/routers/worksheet.d.ts +8 -6
  61. package/dist/routers/worksheet.d.ts.map +1 -1
  62. package/dist/routers/worksheet.js +18 -16
  63. package/dist/routers/worksheet.js.map +1 -1
  64. package/dist/services/auth.d.ts +1 -1
  65. package/dist/services/auth.d.ts.map +1 -1
  66. package/dist/services/auth.js +19 -12
  67. package/dist/services/auth.js.map +1 -1
  68. package/dist/services/class.d.ts +1 -1
  69. package/dist/services/class.d.ts.map +1 -1
  70. package/dist/services/class.js +6 -3
  71. package/dist/services/class.js.map +1 -1
  72. package/dist/services/file.d.ts.map +1 -1
  73. package/dist/services/file.js +7 -2
  74. package/dist/services/file.js.map +1 -1
  75. package/dist/services/labChat.d.ts +6 -0
  76. package/dist/services/labChat.d.ts.map +1 -1
  77. package/dist/services/labChat.js +30 -18
  78. package/dist/services/labChat.js.map +1 -1
  79. package/dist/services/message.d.ts +30 -0
  80. package/dist/services/message.d.ts.map +1 -1
  81. package/dist/services/message.js +97 -21
  82. package/dist/services/message.js.map +1 -1
  83. package/dist/services/newtonChat.d.ts +6 -1
  84. package/dist/services/newtonChat.d.ts.map +1 -1
  85. package/dist/services/newtonChat.js +36 -18
  86. package/dist/services/newtonChat.js.map +1 -1
  87. package/dist/services/notification.d.ts +12 -5
  88. package/dist/services/notification.d.ts.map +1 -1
  89. package/dist/services/notification.js +24 -9
  90. package/dist/services/notification.js.map +1 -1
  91. package/dist/services/studentProgress.d.ts.map +1 -1
  92. package/dist/services/studentProgress.js +6 -3
  93. package/dist/services/studentProgress.js.map +1 -1
  94. package/dist/services/worksheet.d.ts +15 -15
  95. package/dist/services/worksheet.d.ts.map +1 -1
  96. package/dist/services/worksheet.js +138 -54
  97. package/dist/services/worksheet.js.map +1 -1
  98. package/dist/trpc.d.ts.map +1 -1
  99. package/dist/trpc.js +11 -17
  100. package/dist/trpc.js.map +1 -1
  101. package/dist/utils/inference.d.ts +9 -1
  102. package/dist/utils/inference.d.ts.map +1 -1
  103. package/dist/utils/inference.js +138 -50
  104. package/dist/utils/inference.js.map +1 -1
  105. package/package.json +1 -1
  106. package/prisma/migrations/20260415103500_add_session_type/migration.sql +33 -0
  107. package/prisma/migrations/20260415133000_add_ai_usage_logs/migration.sql +19 -0
  108. package/prisma/schema.prisma +27 -1
  109. package/scripts/test-pre-push.ts +6 -2
  110. package/src/index.ts +80 -28
  111. package/src/lib/fileUpload.ts +55 -5
  112. package/src/middleware/auth.ts +19 -4
  113. package/src/models/auth.ts +59 -29
  114. package/src/models/class.ts +15 -4
  115. package/src/models/notification.ts +13 -7
  116. package/src/models/worksheet.ts +202 -0
  117. package/src/pipelines/aiLabChat.ts +9 -1
  118. package/src/pipelines/aiNewtonChat.ts +24 -14
  119. package/src/pipelines/gradeWorksheet.ts +8 -0
  120. package/src/routers/class.ts +2 -2
  121. package/src/routers/labChat.ts +3 -1
  122. package/src/routers/message.ts +38 -1
  123. package/src/routers/newtonChat.ts +4 -2
  124. package/src/routers/notifications.ts +2 -2
  125. package/src/routers/worksheet.ts +31 -19
  126. package/src/services/auth.ts +26 -12
  127. package/src/services/class.ts +4 -1
  128. package/src/services/file.ts +6 -0
  129. package/src/services/labChat.ts +42 -18
  130. package/src/services/message.ts +163 -18
  131. package/src/services/newtonChat.ts +49 -18
  132. package/src/services/notification.ts +27 -8
  133. package/src/services/studentProgress.ts +4 -1
  134. package/src/services/worksheet.ts +191 -51
  135. package/src/trpc.ts +12 -18
  136. package/src/utils/inference.ts +209 -55
  137. package/tests/globalSetup.ts +13 -4
  138. package/tests/routers/auth.test.ts +120 -17
  139. package/tests/routers/class.test.ts +7 -1
  140. package/tests/routers/message.test.ts +236 -9
  141. package/tests/routers/newtonChat.test.ts +81 -0
  142. package/tests/routers/notification.test.ts +12 -0
  143. package/tests/routers/worksheet.test.ts +194 -0
  144. package/tests/setup.ts +22 -5
  145. package/tests/utils/inference.test.ts +218 -0
@@ -1,19 +1,32 @@
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]="f1f0bcb4-fc6b-5b13-92c1-e2fedb706ef2")}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]="ef261406-0c03-5992-af7a-26665f456c05")}catch(e){}}();
3
3
  import { TRPCError } from "@trpc/server";
4
4
  import * as Sentry from "@sentry/node";
5
5
  import { findUserBySessionToken, findTeacherClassesByUserId, findClassWithMember, findClassWithTeacher, } from "../models/auth.js";
6
+ function getRequestToken(headers) {
7
+ const userHeader = headers["x-user"];
8
+ if (typeof userHeader === "string") {
9
+ return userHeader;
10
+ }
11
+ if (Array.isArray(userHeader) && userHeader.length > 0) {
12
+ return userHeader[0];
13
+ }
14
+ const authorizationHeader = headers.authorization;
15
+ if (typeof authorizationHeader === "string" && authorizationHeader.startsWith("Bearer ")) {
16
+ return authorizationHeader.slice(7);
17
+ }
18
+ return null;
19
+ }
6
20
  export const createAuthMiddleware = (t) => {
7
21
  const isAuthed = t.middleware(async ({ next, ctx }) => {
8
- const userHeader = ctx.req.headers["x-user"];
9
- if (!userHeader) {
22
+ const token = getRequestToken(ctx.req.headers);
23
+ if (!token) {
10
24
  throw new TRPCError({
11
25
  code: "UNAUTHORIZED",
12
26
  message: "Not authenticated - no token found",
13
27
  });
14
28
  }
15
29
  try {
16
- const token = typeof userHeader === "string" ? userHeader : userHeader[0];
17
30
  const user = await findUserBySessionToken(token);
18
31
  if (!user) {
19
32
  throw new TRPCError({
@@ -110,4 +123,4 @@ export const createAuthMiddleware = (t) => {
110
123
  };
111
124
  };
112
125
  //# sourceMappingURL=auth.js.map
113
- //# debugId=f1f0bcb4-fc6b-5b13-92c1-e2fedb706ef2
126
+ //# debugId=ef261406-0c03-5992-af7a-26665f456c05
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["middleware/auth.ts"],"sourceRoot":"/","sourcesContent":["import { TRPCError } from \"@trpc/server\";\nimport * as Sentry from \"@sentry/node\";\nimport type { MiddlewareContext } from \"../types/trpc.js\";\nimport {\n findUserBySessionToken,\n findTeacherClassesByUserId,\n findClassWithMember,\n findClassWithTeacher,\n} from \"../models/auth.js\";\n\nexport const createAuthMiddleware = (t: any) => {\n const isAuthed = t.middleware(async ({ next, ctx }: MiddlewareContext) => {\n const userHeader = ctx.req.headers[\"x-user\"];\n\n if (!userHeader) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated - no token found\",\n });\n }\n\n try {\n const token =\n typeof userHeader === \"string\" ? userHeader : userHeader[0];\n const user = await findUserBySessionToken(token);\n\n if (!user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Invalid or expired session\",\n });\n }\n\n return next({\n ctx: {\n ...ctx,\n user,\n },\n });\n } catch (error) {\n if (error instanceof TRPCError) {\n throw error;\n }\n Sentry.captureException(error);\n console.error(error);\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Internal server error\",\n });\n }\n });\n\n const addComputedFlags = t.middleware(\n async ({ next, ctx }: MiddlewareContext) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const teacherClasses = await findTeacherClassesByUserId(ctx.user.id);\n\n return next({\n ctx: {\n ...ctx,\n isTeacher: teacherClasses.length > 0,\n teacherClassIds: teacherClasses.map((c) => c.id),\n },\n });\n }\n );\n\n const isMemberInClass = t.middleware(\n async ({ next, ctx, input }: MiddlewareContext) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const classId = (input as { classId: string })?.classId;\n if (!classId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"classId is required\",\n });\n }\n\n const isMember = await findClassWithMember(classId, ctx.user.id);\n if (!isMember) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member in this class\",\n });\n }\n\n return next();\n }\n );\n\n const isTeacherInClass = t.middleware(\n async ({ next, ctx, input }: MiddlewareContext) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const classId = (input as { classId: string })?.classId;\n if (!classId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"classId is required\",\n });\n }\n\n const isTeacher = await findClassWithTeacher(classId, ctx.user.id);\n if (!isTeacher) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a teacher in this class\",\n });\n }\n\n return next();\n }\n );\n\n return {\n isAuthed,\n addComputedFlags,\n isMemberInClass,\n isTeacherInClass,\n };\n};\n"],"names":[],"mappings":"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","debug_id":"f1f0bcb4-fc6b-5b13-92c1-e2fedb706ef2"}
1
+ {"version":3,"file":"auth.js","sources":["middleware/auth.ts"],"sourceRoot":"/","sourcesContent":["import { TRPCError } from \"@trpc/server\";\nimport * as Sentry from \"@sentry/node\";\nimport type { MiddlewareContext } from \"../types/trpc.js\";\nimport {\n findUserBySessionToken,\n findTeacherClassesByUserId,\n findClassWithMember,\n findClassWithTeacher,\n} from \"../models/auth.js\";\n\nfunction getRequestToken(headers: MiddlewareContext[\"ctx\"][\"req\"][\"headers\"]) {\n const userHeader = headers[\"x-user\"];\n if (typeof userHeader === \"string\") {\n return userHeader;\n }\n if (Array.isArray(userHeader) && userHeader.length > 0) {\n return userHeader[0];\n }\n\n const authorizationHeader = headers.authorization;\n if (typeof authorizationHeader === \"string\" && authorizationHeader.startsWith(\"Bearer \")) {\n return authorizationHeader.slice(7);\n }\n\n return null;\n}\n\nexport const createAuthMiddleware = (t: any) => {\n const isAuthed = t.middleware(async ({ next, ctx }: MiddlewareContext) => {\n const token = getRequestToken(ctx.req.headers);\n\n if (!token) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated - no token found\",\n });\n }\n\n try {\n const user = await findUserBySessionToken(token);\n\n if (!user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Invalid or expired session\",\n });\n }\n\n return next({\n ctx: {\n ...ctx,\n user,\n },\n });\n } catch (error) {\n if (error instanceof TRPCError) {\n throw error;\n }\n Sentry.captureException(error);\n console.error(error);\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Internal server error\",\n });\n }\n });\n\n const addComputedFlags = t.middleware(\n async ({ next, ctx }: MiddlewareContext) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const teacherClasses = await findTeacherClassesByUserId(ctx.user.id);\n\n return next({\n ctx: {\n ...ctx,\n isTeacher: teacherClasses.length > 0,\n teacherClassIds: teacherClasses.map((c) => c.id),\n },\n });\n }\n );\n\n const isMemberInClass = t.middleware(\n async ({ next, ctx, input }: MiddlewareContext) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const classId = (input as { classId: string })?.classId;\n if (!classId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"classId is required\",\n });\n }\n\n const isMember = await findClassWithMember(classId, ctx.user.id);\n if (!isMember) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member in this class\",\n });\n }\n\n return next();\n }\n );\n\n const isTeacherInClass = t.middleware(\n async ({ next, ctx, input }: MiddlewareContext) => {\n if (!ctx.user) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Not authenticated\",\n });\n }\n\n const classId = (input as { classId: string })?.classId;\n if (!classId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"classId is required\",\n });\n }\n\n const isTeacher = await findClassWithTeacher(classId, ctx.user.id);\n if (!isTeacher) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a teacher in this class\",\n });\n }\n\n return next();\n }\n );\n\n return {\n isAuthed,\n addComputedFlags,\n isMemberInClass,\n isTeacherInClass,\n };\n};\n"],"names":[],"mappings":"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","debug_id":"ef261406-0c03-5992-af7a-26665f456c05"}
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Auth model – user lookup, session CRUD, Redis session cache.
3
+ * findUserBySessionToken checks Redis first, then DB.
4
+ */
5
+ import { SessionType } from "@prisma/client";
1
6
  /** @returns User by username (includes password for login). */
2
7
  export declare function findUserByUsername(username: string): import(".prisma/client").Prisma.Prisma__UserClient<{
3
8
  id: string;
@@ -83,9 +88,12 @@ export declare function createUser(data: {
83
88
  /** Create a session. */
84
89
  export declare function createSession(data: {
85
90
  id: string;
86
- userId: string;
91
+ type: SessionType;
87
92
  expiresAt: Date;
93
+ userId?: string;
94
+ classId?: string;
88
95
  }): import(".prisma/client").Prisma.Prisma__SessionClient<{
96
+ type: import(".prisma/client").$Enums.SessionType;
89
97
  id: string;
90
98
  userId: string | null;
91
99
  createdAt: Date | null;
@@ -100,7 +108,7 @@ export declare function createSession(data: {
100
108
  }>;
101
109
  /** Delete all sessions for user. Invalidates Redis cache. */
102
110
  export declare function deleteSessionsByUserId(userId: string): Promise<import(".prisma/client").Prisma.BatchPayload>;
103
- /** Delete short-lived sessions (no classId) expiring before date. */
111
+ /** Delete password reset sessions expiring before date. */
104
112
  export declare function deleteShortLivedSessionsByUserId(userId: string, expiresBefore: Date): import(".prisma/client").Prisma.PrismaPromise<import(".prisma/client").Prisma.BatchPayload>;
105
113
  /** Delete a user. */
106
114
  export declare function deleteUserById(id: string): import(".prisma/client").Prisma.Prisma__UserClient<{
@@ -118,8 +126,9 @@ export declare function deleteUserById(id: string): import(".prisma/client").Pri
118
126
  };
119
127
  };
120
128
  }>;
121
- /** @returns Session by ID. */
122
- export declare function findSessionById(id: string): import(".prisma/client").Prisma.Prisma__SessionClient<{
129
+ /** @returns Session by ID and type. */
130
+ export declare function findSessionByIdAndType(id: string, type: SessionType): import(".prisma/client").Prisma.Prisma__SessionClient<{
131
+ type: import(".prisma/client").$Enums.SessionType;
123
132
  id: string;
124
133
  userId: string | null;
125
134
  createdAt: Date | null;
@@ -132,12 +141,13 @@ export declare function findSessionById(id: string): import(".prisma/client").Pr
132
141
  };
133
142
  };
134
143
  }>;
135
- /** @returns Session with user. */
136
- export declare function findSessionByIdWithUser(id: string): import(".prisma/client").Prisma.Prisma__SessionClient<({
144
+ /** @returns Session with user by ID and type. */
145
+ export declare function findSessionByIdWithUserAndType(id: string, type: SessionType): import(".prisma/client").Prisma.Prisma__SessionClient<({
137
146
  user: {
138
147
  id: string;
139
148
  } | null;
140
149
  } & {
150
+ type: import(".prisma/client").$Enums.SessionType;
141
151
  id: string;
142
152
  userId: string | null;
143
153
  createdAt: Date | null;
@@ -183,20 +193,15 @@ export declare function updateUserPassword(userId: string, password: string): im
183
193
  };
184
194
  }>;
185
195
  /** Delete a session. */
186
- export declare function deleteSessionById(id: string): import(".prisma/client").Prisma.Prisma__SessionClient<{
196
+ export declare function deleteSessionById(id: string): Promise<{
197
+ type: import(".prisma/client").$Enums.SessionType;
187
198
  id: string;
188
199
  userId: string | null;
189
200
  createdAt: Date | null;
190
201
  expiresAt: Date | null;
191
202
  classId: string | null;
192
- }, never, import("@prisma/client/runtime/library.js").DefaultArgs, {
193
- datasources: {
194
- db: {
195
- url: string;
196
- };
197
- };
198
203
  }>;
199
- /** @returns User from session token. Checks Redis first, then DB. Caches in Redis. */
204
+ /** @returns User from valid auth session token. Checks Redis first, then DB. Caches in Redis. */
200
205
  export declare function findUserBySessionToken(token: string): Promise<any>;
201
206
  /** @returns Class IDs where user is a teacher. */
202
207
  export declare function findTeacherClassesByUserId(userId: string): import(".prisma/client").Prisma.PrismaPromise<{
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["models/auth.ts"],"names":[],"mappings":"AAOA,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;GAWlD;AAED,0CAA0C;AAC1C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;GAYxE;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;GAkB5C;AAED,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;GAkBtC;AAED,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;;;;;;;;;;GAeA;AAED,wBAAwB;AACxB,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CACjB;;;;;;;;;;;;GAIA;AAED,6DAA6D;AAC7D,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,yDAU1D;AAED,qEAAqE;AACrE,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,IAAI,+FASpB;AAED,qBAAqB;AACrB,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;GAIxC;AAED,8BAA8B;AAC9B,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;GAIzC;AAED,kCAAkC;AAClC,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;GAOjD;AAED,gCAAgC;AAChC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO;;;;;;;;;;;;;;GAKnE;AAED,4BAA4B;AAC5B,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;GAKlE;AAED,wBAAwB;AACxB,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;GAI3C;AAED,sFAAsF;AACtF,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,gBA0BzD;AAED,kDAAkD;AAClD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM;;KAOxD;AAED,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;GAUlE;AAED,2CAA2C;AAC3C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;GAOtE"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["models/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmB7C,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;GAWlD;AAED,0CAA0C;AAC1C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;GAYxE;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;GAkB5C;AAED,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;GAkBtC;AAED,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;;;;;;;;;;GAeA;AAED,wBAAwB;AACxB,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;;;;;;;;;;;;;GAIA;AAED,6DAA6D;AAC7D,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,yDAU1D;AAED,2DAA2D;AAC3D,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,IAAI,+FASpB;AAED,qBAAqB;AACrB,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;GAIxC;AAED,uCAAuC;AACvC,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW;;;;;;;;;;;;;GAInE;AAED,iDAAiD;AACjD,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW;;;;;;;;;;;;;;;;;GAO3E;AAED,gCAAgC;AAChC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO;;;;;;;;;;;;;;GAKnE;AAED,4BAA4B;AAC5B,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;GAKlE;AAED,wBAAwB;AACxB,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM;;;;;;;GAKjD;AAED,iGAAiG;AACjG,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,gBAqCzD;AAED,kDAAkD;AAClD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM;;KAOxD;AAED,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;GAUlE;AAED,2CAA2C;AAC3C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;GAOtE"}
@@ -3,9 +3,21 @@
3
3
  * findUserBySessionToken checks Redis first, then DB.
4
4
  */
5
5
 
6
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="807b5838-7465-59d7-9c89-4d5177b75a3c")}catch(e){}}();
6
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="caa0301d-42ed-55b1-a0ef-383918fe715a")}catch(e){}}();
7
+ import { SessionType } from "@prisma/client";
7
8
  import { getRedis } from "../lib/redis.js";
8
9
  import { prisma } from "../lib/prisma.js";
10
+ const AUTH_SESSION_CACHE_PREFIX = "auth-session:";
11
+ function getAuthSessionCacheKey(token) {
12
+ return `${AUTH_SESSION_CACHE_PREFIX}${token}`;
13
+ }
14
+ function getSessionTtlSeconds(expiresAt) {
15
+ if (!expiresAt) {
16
+ return null;
17
+ }
18
+ const ttlSeconds = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
19
+ return ttlSeconds > 0 ? ttlSeconds : null;
20
+ }
9
21
  /** @returns User by username (includes password for login). */
10
22
  export function findUserByUsername(username) {
11
23
  return prisma.user.findFirst({
@@ -102,18 +114,18 @@ export async function deleteSessionsByUserId(userId) {
102
114
  where: { userId },
103
115
  });
104
116
  for (const session of sessions) {
105
- await getRedis()?.del(`session:${session.id}`);
117
+ await getRedis()?.del(getAuthSessionCacheKey(session.id));
106
118
  }
107
119
  return prisma.session.deleteMany({
108
120
  where: { userId },
109
121
  });
110
122
  }
111
- /** Delete short-lived sessions (no classId) expiring before date. */
123
+ /** Delete password reset sessions expiring before date. */
112
124
  export function deleteShortLivedSessionsByUserId(userId, expiresBefore) {
113
125
  return prisma.session.deleteMany({
114
126
  where: {
115
127
  userId,
116
- classId: null,
128
+ type: SessionType.PASSWORD_RESET,
117
129
  expiresAt: { lte: expiresBefore },
118
130
  },
119
131
  });
@@ -124,16 +136,16 @@ export function deleteUserById(id) {
124
136
  where: { id },
125
137
  });
126
138
  }
127
- /** @returns Session by ID. */
128
- export function findSessionById(id) {
129
- return prisma.session.findUnique({
130
- where: { id },
139
+ /** @returns Session by ID and type. */
140
+ export function findSessionByIdAndType(id, type) {
141
+ return prisma.session.findFirst({
142
+ where: { id, type },
131
143
  });
132
144
  }
133
- /** @returns Session with user. */
134
- export function findSessionByIdWithUser(id) {
135
- return prisma.session.findUnique({
136
- where: { id },
145
+ /** @returns Session with user by ID and type. */
146
+ export function findSessionByIdWithUserAndType(id, type) {
147
+ return prisma.session.findFirst({
148
+ where: { id, type },
137
149
  include: {
138
150
  user: { select: { id: true } },
139
151
  },
@@ -154,34 +166,45 @@ export function updateUserPassword(userId, password) {
154
166
  });
155
167
  }
156
168
  /** Delete a session. */
157
- export function deleteSessionById(id) {
169
+ export async function deleteSessionById(id) {
170
+ await getRedis()?.del(getAuthSessionCacheKey(id));
158
171
  return prisma.session.delete({
159
172
  where: { id },
160
173
  });
161
174
  }
162
- /** @returns User from session token. Checks Redis first, then DB. Caches in Redis. */
175
+ /** @returns User from valid auth session token. Checks Redis first, then DB. Caches in Redis. */
163
176
  export async function findUserBySessionToken(token) {
164
- const session = await getRedis()?.get(`session:${token}`);
165
- if (session)
166
- return JSON.parse(session);
167
- const user = await prisma.user.findFirst({
177
+ const cacheKey = getAuthSessionCacheKey(token);
178
+ const cachedUser = await getRedis()?.get(cacheKey);
179
+ if (cachedUser) {
180
+ return JSON.parse(cachedUser);
181
+ }
182
+ const session = await prisma.session.findFirst({
168
183
  where: {
169
- sessions: { some: { id: token } },
184
+ id: token,
185
+ type: SessionType.AUTH,
186
+ userId: { not: null },
187
+ expiresAt: { gt: new Date() },
170
188
  },
171
189
  select: {
172
- id: true,
173
- username: true,
190
+ expiresAt: true,
191
+ user: {
192
+ select: {
193
+ id: true,
194
+ username: true,
195
+ },
196
+ },
174
197
  },
175
198
  });
176
- if (user) {
177
- const r = getRedis();
178
- if (r) {
179
- const ttlSeconds = 30 * 24 * 60 * 60; // 30 days, match session expiry
180
- await r.setex(`session:${token}`, ttlSeconds, JSON.stringify(user));
181
- }
182
- return user;
199
+ if (!session?.user) {
200
+ return null;
201
+ }
202
+ const ttlSeconds = getSessionTtlSeconds(session.expiresAt);
203
+ const r = getRedis();
204
+ if (r && ttlSeconds) {
205
+ await r.setex(cacheKey, ttlSeconds, JSON.stringify(session.user));
183
206
  }
184
- return null;
207
+ return session.user;
185
208
  }
186
209
  /** @returns Class IDs where user is a teacher. */
187
210
  export function findTeacherClassesByUserId(userId) {
@@ -214,4 +237,4 @@ export function findClassWithTeacher(classId, teacherId) {
214
237
  });
215
238
  }
216
239
  //# sourceMappingURL=auth.js.map
217
- //# debugId=807b5838-7465-59d7-9c89-4d5177b75a3c
240
+ //# debugId=caa0301d-42ed-55b1-a0ef-383918fe715a
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["models/auth.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Auth model – user lookup, session CRUD, Redis session cache.\n * findUserBySessionToken checks Redis first, then DB.\n */\nimport { getRedis } from \"../lib/redis.js\";\nimport { prisma } from \"../lib/prisma.js\";\n\n/** @returns User by username (includes password for login). */\nexport function findUserByUsername(username: string) {\n return 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\n/** @returns User by username or email. */\nexport function findUserByUsernameOrEmail(username: string, email: string) {\n return prisma.user.findFirst({\n where: {\n OR: [{ username }, { email }],\n },\n select: {\n id: true,\n username: true,\n email: true,\n verified: true,\n },\n });\n}\n\n/** @returns User by email with profile. */\nexport function findUserByEmail(email: string) {\n return prisma.user.findFirst({\n where: { email },\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\n/** @returns User by ID with profile. */\nexport function findUserById(id: string) {\n return prisma.user.findUnique({\n where: { 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\n/** Create a user with empty profile. */\nexport function createUser(data: {\n username: string;\n email: string;\n password: string;\n verified?: boolean;\n}) {\n return prisma.user.create({\n data: {\n username: data.username,\n email: data.email,\n password: data.password,\n verified: data.verified ?? false,\n profile: { create: {} },\n },\n select: {\n id: true,\n username: true,\n email: true,\n },\n });\n}\n\n/** Create a session. */\nexport function createSession(data: {\n id: string;\n userId: string;\n expiresAt: Date;\n}) {\n return prisma.session.create({\n data,\n });\n}\n\n/** Delete all sessions for user. Invalidates Redis cache. */\nexport async function deleteSessionsByUserId(userId: string) {\n const sessions = await prisma.session.findMany({\n where: { userId },\n });\n for (const session of sessions) {\n await getRedis()?.del(`session:${session.id}`);\n }\n return prisma.session.deleteMany({\n where: { userId },\n });\n}\n\n/** Delete short-lived sessions (no classId) expiring before date. */\nexport function deleteShortLivedSessionsByUserId(\n userId: string,\n expiresBefore: Date\n) {\n return prisma.session.deleteMany({\n where: {\n userId,\n classId: null,\n expiresAt: { lte: expiresBefore },\n },\n });\n}\n\n/** Delete a user. */\nexport function deleteUserById(id: string) {\n return prisma.user.delete({\n where: { id },\n });\n}\n\n/** @returns Session by ID. */\nexport function findSessionById(id: string) {\n return prisma.session.findUnique({\n where: { id },\n });\n}\n\n/** @returns Session with user. */\nexport function findSessionByIdWithUser(id: string) {\n return prisma.session.findUnique({\n where: { id },\n include: {\n user: { select: { id: true } },\n },\n });\n}\n\n/** Set user verified status. */\nexport function updateUserVerified(userId: string, verified: boolean) {\n return prisma.user.update({\n where: { id: userId },\n data: { verified },\n });\n}\n\n/** Update user password. */\nexport function updateUserPassword(userId: string, password: string) {\n return prisma.user.update({\n where: { id: userId },\n data: { password },\n });\n}\n\n/** Delete a session. */\nexport function deleteSessionById(id: string) {\n return prisma.session.delete({\n where: { id },\n });\n}\n\n/** @returns User from session token. Checks Redis first, then DB. Caches in Redis. */\nexport async function findUserBySessionToken(token: string) {\n const session = await getRedis()?.get(`session:${token}`);\n\n if (session)\n return JSON.parse(session);\n\n const user = await prisma.user.findFirst({\n where: {\n sessions: { some: { id: token } },\n },\n select: {\n id: true,\n username: true,\n },\n });\n\n if (user) {\n const r = getRedis();\n if (r) {\n const ttlSeconds = 30 * 24 * 60 * 60; // 30 days, match session expiry\n await r.setex(`session:${token}`, ttlSeconds, JSON.stringify(user));\n }\n return user;\n }\n\n return null;\n}\n\n/** @returns Class IDs where user is a teacher. */\nexport function findTeacherClassesByUserId(userId: string) {\n return prisma.class.findMany({\n where: {\n teachers: { some: { id: userId } },\n },\n select: { id: true },\n });\n}\n\n/** @returns Class if user is student or teacher. */\nexport function findClassWithMember(classId: string, userId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n OR: [\n { students: { some: { id: userId } } },\n { teachers: { some: { id: userId } } },\n ],\n },\n });\n}\n\n/** @returns Class if user is a teacher. */\nexport function findClassWithTeacher(classId: string, teacherId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n teachers: { some: { id: teacherId } },\n },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"807b5838-7465-59d7-9c89-4d5177b75a3c"}
1
+ {"version":3,"file":"auth.js","sources":["models/auth.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Auth model – user lookup, session CRUD, Redis session cache.\n * findUserBySessionToken checks Redis first, then DB.\n */\nimport { SessionType } from \"@prisma/client\";\nimport { getRedis } from \"../lib/redis.js\";\nimport { prisma } from \"../lib/prisma.js\";\n\nconst AUTH_SESSION_CACHE_PREFIX = \"auth-session:\";\n\nfunction getAuthSessionCacheKey(token: string) {\n return `${AUTH_SESSION_CACHE_PREFIX}${token}`;\n}\n\nfunction getSessionTtlSeconds(expiresAt?: Date | null) {\n if (!expiresAt) {\n return null;\n }\n\n const ttlSeconds = Math.floor((expiresAt.getTime() - Date.now()) / 1000);\n return ttlSeconds > 0 ? ttlSeconds : null;\n}\n\n/** @returns User by username (includes password for login). */\nexport function findUserByUsername(username: string) {\n return 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\n/** @returns User by username or email. */\nexport function findUserByUsernameOrEmail(username: string, email: string) {\n return prisma.user.findFirst({\n where: {\n OR: [{ username }, { email }],\n },\n select: {\n id: true,\n username: true,\n email: true,\n verified: true,\n },\n });\n}\n\n/** @returns User by email with profile. */\nexport function findUserByEmail(email: string) {\n return prisma.user.findFirst({\n where: { email },\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\n/** @returns User by ID with profile. */\nexport function findUserById(id: string) {\n return prisma.user.findUnique({\n where: { 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\n/** Create a user with empty profile. */\nexport function createUser(data: {\n username: string;\n email: string;\n password: string;\n verified?: boolean;\n}) {\n return prisma.user.create({\n data: {\n username: data.username,\n email: data.email,\n password: data.password,\n verified: data.verified ?? false,\n profile: { create: {} },\n },\n select: {\n id: true,\n username: true,\n email: true,\n },\n });\n}\n\n/** Create a session. */\nexport function createSession(data: {\n id: string;\n type: SessionType;\n expiresAt: Date;\n userId?: string;\n classId?: string;\n}) {\n return prisma.session.create({\n data,\n });\n}\n\n/** Delete all sessions for user. Invalidates Redis cache. */\nexport async function deleteSessionsByUserId(userId: string) {\n const sessions = await prisma.session.findMany({\n where: { userId },\n });\n for (const session of sessions) {\n await getRedis()?.del(getAuthSessionCacheKey(session.id));\n }\n return prisma.session.deleteMany({\n where: { userId },\n });\n}\n\n/** Delete password reset sessions expiring before date. */\nexport function deleteShortLivedSessionsByUserId(\n userId: string,\n expiresBefore: Date\n) {\n return prisma.session.deleteMany({\n where: {\n userId,\n type: SessionType.PASSWORD_RESET,\n expiresAt: { lte: expiresBefore },\n },\n });\n}\n\n/** Delete a user. */\nexport function deleteUserById(id: string) {\n return prisma.user.delete({\n where: { id },\n });\n}\n\n/** @returns Session by ID and type. */\nexport function findSessionByIdAndType(id: string, type: SessionType) {\n return prisma.session.findFirst({\n where: { id, type },\n });\n}\n\n/** @returns Session with user by ID and type. */\nexport function findSessionByIdWithUserAndType(id: string, type: SessionType) {\n return prisma.session.findFirst({\n where: { id, type },\n include: {\n user: { select: { id: true } },\n },\n });\n}\n\n/** Set user verified status. */\nexport function updateUserVerified(userId: string, verified: boolean) {\n return prisma.user.update({\n where: { id: userId },\n data: { verified },\n });\n}\n\n/** Update user password. */\nexport function updateUserPassword(userId: string, password: string) {\n return prisma.user.update({\n where: { id: userId },\n data: { password },\n });\n}\n\n/** Delete a session. */\nexport async function deleteSessionById(id: string) {\n await getRedis()?.del(getAuthSessionCacheKey(id));\n return prisma.session.delete({\n where: { id },\n });\n}\n\n/** @returns User from valid auth session token. Checks Redis first, then DB. Caches in Redis. */\nexport async function findUserBySessionToken(token: string) {\n const cacheKey = getAuthSessionCacheKey(token);\n const cachedUser = await getRedis()?.get(cacheKey);\n\n if (cachedUser) {\n return JSON.parse(cachedUser);\n }\n\n const session = await prisma.session.findFirst({\n where: {\n id: token,\n type: SessionType.AUTH,\n userId: { not: null },\n expiresAt: { gt: new Date() },\n },\n select: {\n expiresAt: true,\n user: {\n select: {\n id: true,\n username: true,\n },\n },\n },\n });\n\n if (!session?.user) {\n return null;\n }\n\n const ttlSeconds = getSessionTtlSeconds(session.expiresAt);\n const r = getRedis();\n if (r && ttlSeconds) {\n await r.setex(cacheKey, ttlSeconds, JSON.stringify(session.user));\n }\n\n return session.user;\n}\n\n/** @returns Class IDs where user is a teacher. */\nexport function findTeacherClassesByUserId(userId: string) {\n return prisma.class.findMany({\n where: {\n teachers: { some: { id: userId } },\n },\n select: { id: true },\n });\n}\n\n/** @returns Class if user is student or teacher. */\nexport function findClassWithMember(classId: string, userId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n OR: [\n { students: { some: { id: userId } } },\n { teachers: { some: { id: userId } } },\n ],\n },\n });\n}\n\n/** @returns Class if user is a teacher. */\nexport function findClassWithTeacher(classId: string, teacherId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n teachers: { some: { id: teacherId } },\n },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"caa0301d-42ed-55b1-a0ef-383918fe715a"}
@@ -214,6 +214,7 @@ export declare function findUserById(userId: string): import(".prisma/client").P
214
214
  }>;
215
215
  /** @returns Session by class invite code. */
216
216
  export declare function findSessionByClassCode(classCode: string): import(".prisma/client").Prisma.Prisma__SessionClient<{
217
+ type: import(".prisma/client").$Enums.SessionType;
217
218
  id: string;
218
219
  userId: string | null;
219
220
  createdAt: Date | null;
@@ -228,6 +229,7 @@ export declare function findSessionByClassCode(classCode: string): import(".pris
228
229
  }>;
229
230
  /** @returns Session for a class. */
230
231
  export declare function findSessionByClassId(classId: string): import(".prisma/client").Prisma.Prisma__SessionClient<{
232
+ type: import(".prisma/client").$Enums.SessionType;
231
233
  id: string;
232
234
  userId: string | null;
233
235
  createdAt: Date | null;
@@ -475,8 +477,8 @@ export declare function findFullExportableClass(classId: string): Promise<{
475
477
  id: string;
476
478
  classId: string;
477
479
  questions: {
478
- options: import("@prisma/client/runtime/library.js").JsonValue;
479
480
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
481
+ options: import("@prisma/client/runtime/library.js").JsonValue;
480
482
  order: number | null;
481
483
  points: number;
482
484
  worksheetId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["models/class.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAc,KAAK,EAAE,MAAM,EAAmD,MAAM,gBAAgB,CAAC;AAgD5G,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAKhD;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAKhD;AAED,2CAA2C;AAC3C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;GAO/D;AAsHD,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCjD;AAED,sCAAsC;AACtC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;GAIjD;AAED,qCAAqC;AACrC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM;;;;;;KAIpD;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;GAK1C;AAED,6CAA6C;AAC7C,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;GAMvD;AAED,oCAAoC;AACpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;GAInD;AAED,gEAAgE;AAChE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAsBvE;AAED,mCAAmC;AACnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;;KAKlD;AAED,yCAAyC;AACzC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM;;;;KAIvD;AAED,+CAA+C;AAC/C,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM;;;;KAI7D;AAED,+BAA+B;AAC/B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM;;;;;;;;GAKhD;AAED,qDAAqD;AACrD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;KAStE;AAED,6DAA6D;AAC7D,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAiDvD;AAoGD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoF5D;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6HjK"}
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["models/class.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAc,KAAK,EAAE,MAAM,EAAgE,MAAM,gBAAgB,CAAC;AAgDzH,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAKhD;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAKhD;AAED,2CAA2C;AAC3C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;GAO/D;AAsHD,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CjD;AAED,sCAAsC;AACtC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;GAIjD;AAED,qCAAqC;AACrC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM;;;;;;KAIpD;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;GAK1C;AAED,6CAA6C;AAC7C,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;GAOvD;AAED,oCAAoC;AACpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;GAInD;AAED,gEAAgE;AAChE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAsBvE;AAED,mCAAmC;AACnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM;;;;KAKlD;AAED,yCAAyC;AACzC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM;;;;KAIvD;AAED,+CAA+C;AAC/C,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM;;;;KAI7D;AAED,+BAA+B;AAC/B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM;;;;;;;;GAKhD;AAED,qDAAqD;AACrD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;KAStE;AAED,6DAA6D;AAC7D,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAiDvD;AAoGD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoF5D;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6HjK"}
@@ -3,7 +3,8 @@
3
3
  * grading boundaries, syllabus, lab drafts, invite codes.
4
4
  */
5
5
 
6
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a5a21161-a4bb-58bf-9c4b-a48cc39cfbdb")}catch(e){}}();
6
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f92cda2a-2ce2-5da3-914b-24deba15bc61")}catch(e){}}();
7
+ import { SessionType } from "@prisma/client";
7
8
  import { prisma } from "../lib/prisma.js";
8
9
  import { v4 as uuidv4 } from 'uuid';
9
10
  import { copyFile } from "../lib/googleCloudStorage.js";
@@ -198,8 +199,18 @@ export function findClassWithAssignments(classId, options) {
198
199
  const submissionWhere = !options.isTeacher && options.userId
199
200
  ? { studentId: options.userId }
200
201
  : undefined;
201
- return prisma.class.findUnique({
202
- where: { id: classId },
202
+ return prisma.class.findFirst({
203
+ where: {
204
+ id: classId,
205
+ ...(options.userId
206
+ ? {
207
+ OR: [
208
+ { students: { some: { id: options.userId } } },
209
+ { teachers: { some: { id: options.userId } } },
210
+ ],
211
+ }
212
+ : {}),
213
+ },
203
214
  include: {
204
215
  teachers: classIncludeForGet.teachers,
205
216
  students: classIncludeForGet.students,
@@ -241,6 +252,7 @@ export function findSessionByClassCode(classCode) {
241
252
  return prisma.session.findFirst({
242
253
  where: {
243
254
  id: { equals: classCode, mode: "insensitive" },
255
+ type: SessionType.CLASS_INVITE,
244
256
  },
245
257
  });
246
258
  }
@@ -642,4 +654,4 @@ export async function createClassByImport(classId, userId, year, classData) {
642
654
  return newClassId;
643
655
  }
644
656
  //# sourceMappingURL=class.js.map
645
- //# debugId=a5a21161-a4bb-58bf-9c4b-a48cc39cfbdb
657
+ //# debugId=f92cda2a-2ce2-5da3-914b-24deba15bc61