@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
@@ -3,7 +3,8 @@
3
3
  * Handles session creation, Redis caching, and email notifications.
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]="18f3fceb-a95b-5d3c-b80c-c8f5a01da04e")}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]="7c41f210-458c-593a-8fe2-a8ecbc675245")}catch(e){}}();
7
+ import { SessionType } from "@prisma/client";
7
8
  import { TRPCError } from "@trpc/server";
8
9
  import { v4 as uuidv4 } from "uuid";
9
10
  import { compare, hash } from "bcryptjs";
@@ -11,8 +12,7 @@ import { sendMail } from "../utils/email.js";
11
12
  import { prismaWrapper } from "../utils/prismaWrapper.js";
12
13
  import { env } from "../lib/config/env.js";
13
14
  import { logger } from "../utils/logger.js";
14
- import { findUserByUsername, findUserByUsernameOrEmail, findUserByEmail, findUserById, createUser, createSession, deleteSessionsByUserId, deleteShortLivedSessionsByUserId, deleteUserById, findSessionById, findSessionByIdWithUser, updateUserVerified, updateUserPassword, deleteSessionById, } from "../models/auth.js";
15
- import { getRedis } from "../lib/redis.js";
15
+ import { findUserByUsername, findUserByUsernameOrEmail, findUserByEmail, findUserById, createUser, createSession, deleteSessionsByUserId, deleteShortLivedSessionsByUserId, deleteUserById, findSessionByIdAndType, findSessionByIdWithUserAndType, updateUserVerified, updateUserPassword, deleteSessionById, } from "../models/auth.js";
16
16
  /** Register a new user, create verification session, and send verification email. */
17
17
  export async function register(input) {
18
18
  const { username, email, password } = input;
@@ -43,6 +43,7 @@ export async function register(input) {
43
43
  }), "creating new user during registration");
44
44
  const verificationToken = await prismaWrapper.create(() => createSession({
45
45
  id: uuidv4(),
46
+ type: SessionType.EMAIL_VERIFICATION,
46
47
  userId: user.id,
47
48
  expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
48
49
  }), "creating verification token");
@@ -62,7 +63,7 @@ export async function register(input) {
62
63
  }
63
64
  return { user: { id: user.id, username: user.username } };
64
65
  }
65
- /** Authenticate user by username/password, create session, and cache in Redis. */
66
+ /** Authenticate user by username/password, create session. */
66
67
  export async function login(input) {
67
68
  const { username, password } = input;
68
69
  const user = await findUserByUsername(username);
@@ -86,14 +87,10 @@ export async function login(input) {
86
87
  }
87
88
  const session = await createSession({
88
89
  id: uuidv4(),
90
+ type: SessionType.AUTH,
89
91
  userId: user.id,
90
92
  expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
91
93
  });
92
- const cachePayload = JSON.stringify({ id: user.id, username: user.username });
93
- const ttlSeconds = 30 * 24 * 60 * 60; // 30 days, match session expiry
94
- const r = getRedis();
95
- if (r)
96
- await r.setex(`session:${session.id}`, ttlSeconds, cachePayload);
97
94
  return {
98
95
  token: session.id,
99
96
  user: { id: user.id, username: user.username },
@@ -127,6 +124,7 @@ export async function resendVerificationEmail(email) {
127
124
  await deleteSessionsByUserId(user.id);
128
125
  const verificationToken = await createSession({
129
126
  id: uuidv4(),
127
+ type: SessionType.EMAIL_VERIFICATION,
130
128
  userId: user.id,
131
129
  expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
132
130
  });
@@ -148,7 +146,7 @@ export async function resendVerificationEmail(email) {
148
146
  }
149
147
  /** Verify email via token. Marks user as verified and deletes the session. */
150
148
  export async function verify(token) {
151
- const session = await findSessionById(token);
149
+ const session = await findSessionByIdAndType(token, SessionType.EMAIL_VERIFICATION);
152
150
  if (!session) {
153
151
  throw new TRPCError({
154
152
  code: "NOT_FOUND",
@@ -156,11 +154,19 @@ export async function verify(token) {
156
154
  });
157
155
  }
158
156
  if (session.expiresAt && session.expiresAt < new Date()) {
157
+ await deleteSessionById(token);
159
158
  throw new TRPCError({
160
159
  code: "UNAUTHORIZED",
161
160
  message: "Session expired",
162
161
  });
163
162
  }
163
+ if (!session.userId) {
164
+ await deleteSessionById(token);
165
+ throw new TRPCError({
166
+ code: "BAD_REQUEST",
167
+ message: "Invalid session state",
168
+ });
169
+ }
164
170
  await updateUserVerified(session.userId, true);
165
171
  await deleteSessionById(token);
166
172
  return { success: true };
@@ -175,6 +181,7 @@ export async function requestPasswordReset(email) {
175
181
  await deleteShortLivedSessionsByUserId(user.id, twoHoursFromNow);
176
182
  const resetToken = await createSession({
177
183
  id: uuidv4(),
184
+ type: SessionType.PASSWORD_RESET,
178
185
  userId: user.id,
179
186
  expiresAt: new Date(Date.now() + 1000 * 60 * 60),
180
187
  });
@@ -196,7 +203,7 @@ export async function requestPasswordReset(email) {
196
203
  }
197
204
  /** Reset password using token from email. Validates token and updates password. */
198
205
  export async function resetPassword(token, password) {
199
- const session = await findSessionByIdWithUser(token);
206
+ const session = await findSessionByIdWithUserAndType(token, SessionType.PASSWORD_RESET);
200
207
  if (!session || !session.userId) {
201
208
  throw new TRPCError({
202
209
  code: "NOT_FOUND",
@@ -215,4 +222,4 @@ export async function resetPassword(token, password) {
215
222
  return { success: true };
216
223
  }
217
224
  //# sourceMappingURL=auth.js.map
218
- //# debugId=18f3fceb-a95b-5d3c-b80c-c8f5a01da04e
225
+ //# debugId=7c41f210-458c-593a-8fe2-a8ecbc675245
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["services/auth.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Auth service – user registration, login, logout, verification, and password reset.\n * Handles session creation, Redis caching, and email notifications.\n */\nimport { TRPCError } from \"@trpc/server\";\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\";\nimport {\n findUserByUsername,\n findUserByUsernameOrEmail,\n findUserByEmail,\n findUserById,\n createUser,\n createSession,\n deleteSessionsByUserId,\n deleteShortLivedSessionsByUserId,\n deleteUserById,\n findSessionById,\n findSessionByIdWithUser,\n updateUserVerified,\n updateUserPassword,\n deleteSessionById,\n} from \"../models/auth.js\";\nimport { getRedis } from \"../lib/redis.js\";\n\n/** Register a new user, create verification session, and send verification email. */\nexport async function register(input: {\n username: string;\n email: string;\n password: string;\n}) {\n const { username, email, password } = input;\n\n const existingUser = await prismaWrapper.findFirst(\n () => findUserByUsernameOrEmail(username, email),\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 () => deleteSessionsByUserId(existingUser.id),\n \"deleting existing sessions for unverified user\"\n );\n await prismaWrapper.delete(\n () => deleteUserById(existingUser.id),\n \"deleting unverified user\"\n );\n }\n\n const user = await prismaWrapper.create(\n async () =>\n createUser({\n username,\n email,\n password: await hash(password, 10),\n verified: true,\n }),\n \"creating new user during registration\"\n );\n\n const verificationToken = await prismaWrapper.create(\n () =>\n createSession({\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n }),\n \"creating verification token\"\n );\n\n try {\n await sendMail({\n from: \"hello@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\", {\n email: user.email,\n err,\n });\n }\n\n return { user: { id: user.id, username: user.username } };\n}\n\n/** Authenticate user by username/password, create session, and cache in Redis. */\nexport async function login(input: { username: string; password: string }) {\n const { username, password } = input;\n\n const user = await findUserByUsername(username);\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: { email: user.email },\n };\n }\n\n const session = await createSession({\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n });\n\n const cachePayload = JSON.stringify({ id: user.id, username: user.username });\n const ttlSeconds = 30 * 24 * 60 * 60; // 30 days, match session expiry\n const r = getRedis();\n if (r) await r.setex(`session:${session.id}`, ttlSeconds, cachePayload);\n\n return {\n token: session.id,\n user: { id: user.id, username: user.username },\n };\n}\n\n/** Delete all sessions for a user (logs them out everywhere). */\nexport async function logout(userId: string) {\n await deleteSessionsByUserId(userId);\n return { success: true };\n}\n\n/** Verify user exists and return profile. Used for session validation. */\nexport async function check(userId: string) {\n const user = await findUserById(userId);\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n return { user };\n}\n\n/** Resend verification email. Replaces existing sessions and sends new link. */\nexport async function resendVerificationEmail(email: string) {\n const user = await findUserByEmail(email);\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n await deleteSessionsByUserId(user.id);\n\n const verificationToken = await createSession({\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n });\n\n try {\n await sendMail({\n from: \"hello@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\", {\n email: user.email,\n err,\n });\n }\n\n return { success: true };\n}\n\n/** Verify email via token. Marks user as verified and deletes the session. */\nexport async function verify(token: string) {\n const session = await findSessionById(token);\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 updateUserVerified(session.userId!, true);\n await deleteSessionById(token);\n return { success: true };\n}\n\n/** Send password reset email. Silently succeeds if email not found (security). */\nexport async function requestPasswordReset(email: string) {\n const user = await findUserByEmail(email);\n if (!user) {\n return { success: true };\n }\n\n const twoHoursFromNow = new Date(Date.now() + 1000 * 60 * 60 * 2);\n await deleteShortLivedSessionsByUserId(user.id, twoHoursFromNow);\n\n const resetToken = await createSession({\n id: uuidv4(),\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60),\n });\n\n try {\n await sendMail({\n from: \"hello@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\", {\n email: user.email,\n err,\n });\n }\n\n return { success: true };\n}\n\n/** Reset password using token from email. Validates token and updates password. */\nexport async function resetPassword(token: string, password: string) {\n const session = await findSessionByIdWithUser(token);\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 await deleteSessionById(token);\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Reset token has expired\",\n });\n }\n\n await updateUserPassword(session.userId, await hash(password, 10));\n await deleteSessionById(token);\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"18f3fceb-a95b-5d3c-b80c-c8f5a01da04e"}
1
+ {"version":3,"file":"auth.js","sources":["services/auth.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Auth service – user registration, login, logout, verification, and password reset.\n * Handles session creation, Redis caching, and email notifications.\n */\nimport { SessionType } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\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\";\nimport {\n findUserByUsername,\n findUserByUsernameOrEmail,\n findUserByEmail,\n findUserById,\n createUser,\n createSession,\n deleteSessionsByUserId,\n deleteShortLivedSessionsByUserId,\n deleteUserById,\n findSessionByIdAndType,\n findSessionByIdWithUserAndType,\n updateUserVerified,\n updateUserPassword,\n deleteSessionById,\n} from \"../models/auth.js\";\n\n/** Register a new user, create verification session, and send verification email. */\nexport async function register(input: {\n username: string;\n email: string;\n password: string;\n}) {\n const { username, email, password } = input;\n\n const existingUser = await prismaWrapper.findFirst(\n () => findUserByUsernameOrEmail(username, email),\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 () => deleteSessionsByUserId(existingUser.id),\n \"deleting existing sessions for unverified user\"\n );\n await prismaWrapper.delete(\n () => deleteUserById(existingUser.id),\n \"deleting unverified user\"\n );\n }\n\n const user = await prismaWrapper.create(\n async () =>\n createUser({\n username,\n email,\n password: await hash(password, 10),\n verified: true,\n }),\n \"creating new user during registration\"\n );\n\n const verificationToken = await prismaWrapper.create(\n () =>\n createSession({\n id: uuidv4(),\n type: SessionType.EMAIL_VERIFICATION,\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n }),\n \"creating verification token\"\n );\n\n try {\n await sendMail({\n from: \"hello@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\", {\n email: user.email,\n err,\n });\n }\n\n return { user: { id: user.id, username: user.username } };\n}\n\n/** Authenticate user by username/password, create session. */\nexport async function login(input: { username: string; password: string }) {\n const { username, password } = input;\n\n const user = await findUserByUsername(username);\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: { email: user.email },\n };\n }\n\n const session = await createSession({\n id: uuidv4(),\n type: SessionType.AUTH,\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n });\n\n return {\n token: session.id,\n user: { id: user.id, username: user.username },\n };\n}\n\n/** Delete all sessions for a user (logs them out everywhere). */\nexport async function logout(userId: string) {\n await deleteSessionsByUserId(userId);\n return { success: true };\n}\n\n/** Verify user exists and return profile. Used for session validation. */\nexport async function check(userId: string) {\n const user = await findUserById(userId);\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n return { user };\n}\n\n/** Resend verification email. Replaces existing sessions and sends new link. */\nexport async function resendVerificationEmail(email: string) {\n const user = await findUserByEmail(email);\n if (!user) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"User not found\",\n });\n }\n\n await deleteSessionsByUserId(user.id);\n\n const verificationToken = await createSession({\n id: uuidv4(),\n type: SessionType.EMAIL_VERIFICATION,\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),\n });\n\n try {\n await sendMail({\n from: \"hello@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\", {\n email: user.email,\n err,\n });\n }\n\n return { success: true };\n}\n\n/** Verify email via token. Marks user as verified and deletes the session. */\nexport async function verify(token: string) {\n const session = await findSessionByIdAndType(\n token,\n SessionType.EMAIL_VERIFICATION\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 await deleteSessionById(token);\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Session expired\",\n });\n }\n\n if (!session.userId) {\n await deleteSessionById(token);\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Invalid session state\",\n });\n }\n\n await updateUserVerified(session.userId, true);\n await deleteSessionById(token);\n return { success: true };\n}\n\n/** Send password reset email. Silently succeeds if email not found (security). */\nexport async function requestPasswordReset(email: string) {\n const user = await findUserByEmail(email);\n if (!user) {\n return { success: true };\n }\n\n const twoHoursFromNow = new Date(Date.now() + 1000 * 60 * 60 * 2);\n await deleteShortLivedSessionsByUserId(user.id, twoHoursFromNow);\n\n const resetToken = await createSession({\n id: uuidv4(),\n type: SessionType.PASSWORD_RESET,\n userId: user.id,\n expiresAt: new Date(Date.now() + 1000 * 60 * 60),\n });\n\n try {\n await sendMail({\n from: \"hello@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\", {\n email: user.email,\n err,\n });\n }\n\n return { success: true };\n}\n\n/** Reset password using token from email. Validates token and updates password. */\nexport async function resetPassword(token: string, password: string) {\n const session = await findSessionByIdWithUserAndType(\n token,\n SessionType.PASSWORD_RESET\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 await deleteSessionById(token);\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"Reset token has expired\",\n });\n }\n\n await updateUserPassword(session.userId, await hash(password, 10));\n await deleteSessionById(token);\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"7c41f210-458c-593a-8fe2-a8ecbc675245"}
@@ -615,8 +615,8 @@ export declare function exportClass(classId: string, userId: string): Promise<{
615
615
  id: string;
616
616
  classId: string;
617
617
  questions: {
618
- options: import("@prisma/client/runtime/library.js").JsonValue;
619
618
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
619
+ options: import("@prisma/client/runtime/library.js").JsonValue;
620
620
  order: number | null;
621
621
  points: number;
622
622
  worksheetId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["services/class.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["services/class.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,8 @@
3
3
  * syllabus, lab drafts, and 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]="f6a97cb7-783f-56a4-ae40-1d145ee5c1ee")}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]="b0980742-2fbe-5c12-897f-ba5bf6ed98c4")}catch(e){}}();
7
+ import { SessionType } from "@prisma/client";
7
8
  import { TRPCError } from "@trpc/server";
8
9
  import { prisma } from "../lib/prisma.js";
9
10
  import { generateInviteCode } from "../utils/generateInviteCode.js";
@@ -214,6 +215,7 @@ export async function getInviteCode(classId) {
214
215
  data: {
215
216
  id: generateInviteCode(),
216
217
  classId,
218
+ type: SessionType.CLASS_INVITE,
217
219
  expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
218
220
  },
219
221
  });
@@ -223,12 +225,13 @@ export async function getInviteCode(classId) {
223
225
  }
224
226
  export async function createInviteCode(classId) {
225
227
  await prisma.session.deleteMany({
226
- where: { classId },
228
+ where: { classId, type: SessionType.CLASS_INVITE },
227
229
  });
228
230
  const session = await prisma.session.create({
229
231
  data: {
230
232
  id: generateInviteCode(),
231
233
  classId,
234
+ type: SessionType.CLASS_INVITE,
232
235
  expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
233
236
  },
234
237
  });
@@ -483,4 +486,4 @@ export async function importClass(classId, userId, year, classData) {
483
486
  }
484
487
  }
485
488
  //# sourceMappingURL=class.js.map
486
- //# debugId=f6a97cb7-783f-56a4-ae40-1d145ee5c1ee
489
+ //# debugId=b0980742-2fbe-5c12-897f-ba5bf6ed98c4
@@ -1 +1 @@
1
- {"version":3,"file":"class.js","sources":["services/class.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Class service – CRUD for classes, members, grades, mark schemes, grading boundaries,\n * syllabus, lab drafts, and invite codes.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { generateInviteCode } from \"../utils/generateInviteCode.js\";\nimport {\n findTeacherClasses,\n findStudentClasses,\n isTeacherInClass,\n findClassWithAssignments,\n findSectionsByClassId,\n findUserById,\n findSessionByClassCode,\n findSessionByClassId,\n findSubmissionsForGrades,\n findEventsByClassId,\n findMarkSchemesByClassId,\n findGradingBoundariesByClassId,\n findClassSyllabus,\n findLabDraftsByClass,\n findAssignmentsWithFiles,\n findFullExportableClass,\n createClassByImport,\n} from \"../models/class.js\";\nimport { Class, Folder } from \"@prisma/client\";\n\n/** Get all classes where user is teacher or student, with assignments and members. */\nexport async function getAllClasses(userId: string) {\n const [teacherClasses, studentClasses] = await Promise.all([\n findTeacherClasses(userId),\n findStudentClasses(userId),\n ]);\n\n return {\n teacherInClass: teacherClasses.map((cls) => ({\n id: cls.id,\n name: cls.name,\n section: cls.section,\n subject: cls.subject,\n dueToday: cls.assignments,\n assignments: cls.assignments,\n members: [...cls.students, ...cls.teachers],\n color: cls.color,\n })),\n studentInClass: studentClasses.map((cls) => ({\n id: cls.id,\n name: cls.name,\n section: cls.section,\n subject: cls.subject,\n dueToday: cls.assignments,\n assignments: cls.assignments,\n members: [...cls.students, ...cls.teachers],\n color: cls.color,\n })),\n };\n}\n\n/** Get single class with assignments, sections, and submission status. */\nexport async function getClass(userId: string, classId: string) {\n const isTeacher = await isTeacherInClass(classId, userId ?? \"\");\n\n const classData = await findClassWithAssignments(classId, {\n isTeacher: !!isTeacher,\n userId: userId ?? undefined,\n });\n\n if (!classData) {\n throw new Error(\"Class not found\");\n }\n\n const formattedClassData = {\n ...classData,\n assignments: classData.assignments.map((assignment) => ({\n ...assignment,\n late: assignment.dueDate < new Date(),\n submitted: assignment.submissions.find(\n (s) => s.studentId === userId\n )?.submitted,\n returned: assignment.submissions.find(\n (s) => s.studentId === userId\n )?.returned,\n })),\n };\n\n const sections = await findSectionsByClassId(classId);\n\n return {\n class: {\n ...formattedClassData,\n sections,\n },\n };\n}\n\nexport async function updateClass(\n classId: string,\n data: { name?: string; section?: string; subject?: string }\n) {\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data,\n select: {\n id: true,\n name: true,\n section: true,\n subject: true,\n },\n });\n return { updatedClass };\n}\n\nexport async function createClass(\n userId: string,\n input: {\n students?: string[];\n teachers?: string[];\n name: string;\n section: string;\n subject: string;\n color?: string;\n }\n) {\n const { students, teachers, name, section, subject, color } = input;\n\n if (teachers && teachers.length > 0 && students && students.length > 0) {\n return prisma.class.create({\n data: {\n name,\n section,\n subject,\n color,\n teachers: { connect: teachers.map((id) => ({ id })) },\n students: { connect: students.map((id) => ({ id })) },\n },\n include: { teachers: true, students: true },\n });\n }\n\n return prisma.class.create({\n data: {\n name,\n section,\n subject,\n color,\n teachers: { connect: { id: userId } },\n },\n });\n}\n\nexport async function deleteClass(classId: string) {\n const classToDelete = await prisma.class.findFirst({\n where: { id: classId },\n });\n\n if (!classToDelete) {\n throw new Error(\"Class not found or you don't have permission to delete it\");\n }\n\n await prisma.class.delete({\n where: { id: classId },\n });\n\n return { deletedClass: { id: classId } };\n}\n\nexport async function addStudent(classId: string, studentId: string) {\n const student = await findUserById(studentId);\n if (!student) {\n throw new Error(\"Student not found\");\n }\n\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data: {\n students: { connect: { id: studentId } },\n },\n select: {\n id: true,\n name: true,\n section: true,\n subject: true,\n },\n });\n\n return { updatedClass, newStudent: student };\n}\n\nexport async function changeRole(\n classId: string,\n userId: string,\n type: \"teacher\" | \"student\"\n) {\n const user = await findUserById(userId);\n if (!user) {\n throw new Error(\"User not found\");\n }\n\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data: {\n [type === \"teacher\" ? \"teachers\" : \"students\"]: {\n connect: { id: userId },\n },\n [type === \"teacher\" ? \"students\" : \"teachers\"]: {\n disconnect: { id: userId },\n },\n },\n });\n\n return {\n updatedClass,\n user: { ...user, type },\n };\n}\n\nexport async function removeMember(classId: string, userId: string) {\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data: {\n teachers: { disconnect: { id: userId } },\n students: { disconnect: { id: userId } },\n },\n });\n return { updatedClass, removedUserId: userId };\n}\n\nexport async function leaveClass(userId: string, classId: string) {\n const classData = await prisma.class.findFirst({\n where: {\n id: classId,\n students: { some: { id: userId } },\n },\n });\n\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a student in this class\",\n });\n }\n\n await prisma.class.update({\n where: { id: classId },\n data: {\n students: { disconnect: { id: userId } },\n },\n });\n\n return { success: true, leftClassId: classId };\n}\n\nexport async function joinClass(userId: string, classCode: string) {\n const session = await findSessionByClassCode(classCode);\n\n if (!session || !session.classId) {\n throw new Error(\"Class not found\");\n }\n\n if (session.expiresAt && session.expiresAt < new Date()) {\n throw new Error(\"Session expired\");\n }\n\n const updatedClass = await prisma.class.update({\n where: { id: session.classId },\n data: {\n students: { connect: { id: userId } },\n },\n select: {\n id: true,\n name: true,\n section: true,\n subject: true,\n },\n });\n\n return { joinedClass: updatedClass };\n}\n\nexport async function getInviteCode(classId: string) {\n const session = await findSessionByClassId(classId);\n\n if ((session?.expiresAt && session.expiresAt < new Date()) || !session) {\n const newSession = await prisma.session.create({\n data: {\n id: generateInviteCode(),\n classId,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),\n },\n });\n return { code: newSession.id };\n }\n\n return { code: session?.id };\n}\n\nexport async function createInviteCode(classId: string) {\n await prisma.session.deleteMany({\n where: { classId },\n });\n\n const session = await prisma.session.create({\n data: {\n id: generateInviteCode(),\n classId,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),\n },\n });\n\n return { code: session.id };\n}\n\nexport async function getGrades(\n userId: string,\n classId: string,\n targetUserId: string\n) {\n const isTeacher = await isTeacherInClass(classId, userId);\n if (userId !== targetUserId && !isTeacher) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You can only view your own grades\",\n });\n }\n\n const grades = await findSubmissionsForGrades(targetUserId, classId);\n return { grades };\n}\n\nexport async function updateGrade(\n assignmentId: string,\n submissionId: string,\n gradeReceived: number | null\n) {\n const updatedSubmission = await prisma.submission.update({\n where: { id: submissionId, assignmentId },\n data: { gradeReceived },\n include: {\n assignment: {\n select: {\n id: true,\n title: true,\n maxGrade: true,\n weight: true,\n },\n },\n },\n });\n return updatedSubmission;\n}\n\nexport async function getEvents(classId: string) {\n return findEventsByClassId(classId);\n}\n\nexport async function listMarkSchemes(classId: string) {\n return findMarkSchemesByClassId(classId);\n}\n\nexport async function createMarkScheme(classId: string, structure: string) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.markScheme.create({\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function updateMarkScheme(\n markSchemeId: string,\n classId: string,\n structure: string\n) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.markScheme.update({\n where: { id: markSchemeId },\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function deleteMarkScheme(markSchemeId: string) {\n return prisma.markScheme.delete({\n where: { id: markSchemeId },\n });\n}\n\nexport async function listGradingBoundaries(classId: string) {\n return findGradingBoundariesByClassId(classId);\n}\n\nexport async function createGradingBoundary(classId: string, structure: string) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.gradingBoundary.create({\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function updateGradingBoundary(\n gradingBoundaryId: string,\n classId: string,\n structure: string\n) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.gradingBoundary.update({\n where: { id: gradingBoundaryId },\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function deleteGradingBoundary(gradingBoundaryId: string) {\n return prisma.gradingBoundary.delete({\n where: { id: gradingBoundaryId },\n });\n}\n\nexport async function getSyllabus(classId: string) {\n const classData = await findClassSyllabus(classId);\n const markSchemes = await findMarkSchemesByClassId(classId);\n const gradingBoundaries = await findGradingBoundariesByClassId(classId);\n return {\n syllabus: classData?.syllabus,\n gradingBoundaries,\n markSchemes,\n };\n}\n\nexport async function updateSyllabus(classId: string, contents: string) {\n if (!contents) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Missing key contents\",\n });\n }\n return prisma.class.update({\n where: { id: classId },\n data: { syllabus: contents },\n });\n}\n\nexport async function listLabDrafts(classId: string, teacherId: string) {\n return findLabDraftsByClass(classId, teacherId);\n}\n\nexport async function createLabDraft(\n classId: string,\n teacherId: string,\n input: {\n title: string;\n type: string;\n instructions: string;\n dueDate?: Date;\n maxGrade?: number;\n weight?: number;\n graded?: boolean;\n sectionId?: string;\n markSchemeId?: string;\n gradingBoundaryId?: string;\n }\n) {\n return prisma.assignment.create({\n data: {\n classId,\n teacherId,\n inProgress: true,\n graded: input.graded ?? false,\n maxGrade: input.maxGrade ?? 0,\n weight: input.weight ?? 1,\n dueDate:\n input.dueDate ||\n new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\n title: input.title,\n instructions: input.instructions,\n type: input.type as any,\n ...(input.sectionId && { sectionId: input.sectionId }),\n ...(input.markSchemeId && { markSchemeId: input.markSchemeId }),\n ...(input.gradingBoundaryId && {\n gradingBoundaryId: input.gradingBoundaryId,\n }),\n },\n });\n}\n\nexport async function updateLabDraft(\n classId: string,\n teacherId: string,\n draftId: string,\n input: Record<string, unknown>\n) {\n const updateData: Record<string, unknown> = {\n modifiedAt: new Date(),\n };\n if (input.title !== undefined) updateData.title = input.title;\n if (input.instructions !== undefined)\n updateData.instructions = input.instructions;\n if (input.dueDate !== undefined) updateData.dueDate = input.dueDate;\n if (input.maxGrade !== undefined) updateData.maxGrade = input.maxGrade;\n if (input.weight !== undefined) updateData.weight = input.weight;\n if (input.graded !== undefined) updateData.graded = input.graded;\n if (input.sectionId !== undefined) updateData.sectionId = input.sectionId;\n if (input.markSchemeId !== undefined)\n updateData.markSchemeId = input.markSchemeId;\n if (input.gradingBoundaryId !== undefined)\n updateData.gradingBoundaryId = input.gradingBoundaryId;\n\n return prisma.assignment.update({\n where: {\n id: draftId,\n classId,\n teacherId,\n inProgress: true,\n },\n data: updateData as any,\n });\n}\n\nexport async function deleteLabDraft(\n classId: string,\n teacherId: string,\n draftId: string\n) {\n return prisma.assignment.delete({\n where: {\n id: draftId,\n classId,\n teacherId,\n inProgress: true,\n },\n });\n}\n\nexport async function publishLabDraft(\n classId: string,\n teacherId: string,\n draftId: string,\n input: {\n dueDate?: Date;\n maxGrade?: number;\n weight?: number;\n graded?: boolean;\n }\n) {\n const labDraft = await prisma.assignment.findUnique({\n where: {\n id: draftId,\n classId,\n teacherId,\n inProgress: true,\n },\n });\n\n if (!labDraft) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Lab draft not found\",\n });\n }\n\n return prisma.assignment.update({\n where: { id: draftId },\n data: {\n inProgress: false,\n dueDate: input.dueDate || labDraft.dueDate,\n maxGrade: input.maxGrade || labDraft.maxGrade || 100,\n weight: input.weight || labDraft.weight || 1,\n graded: input.graded !== undefined ? input.graded : true,\n modifiedAt: new Date(),\n },\n });\n}\n\nexport async function getFiles(classId: string) {\n const assignments = await findAssignmentsWithFiles(classId);\n return assignments.map((assignment) => ({\n id: assignment.id,\n title: assignment.title,\n teacher: assignment.teacher,\n teacherAttachments: assignment.attachments,\n students: assignment.submissions.map((submission) => ({\n id: submission.student.id,\n username: submission.student.username,\n attachments: submission.attachments,\n annotations: submission.annotations,\n })),\n }));\n}\n\nexport async function exportClass(classId: string, userId: string) {\n const classExport = await findFullExportableClass(classId);\n if (!classExport) {\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to export class\",\n });\n }\n\n return classExport;\n}\n\nexport async function importClass(classId: string, userId: string, year: number, classData: Class & { classFiles: Folder | null }) {\n try {\n const newClassId = await createClassByImport(classId, userId, year, classData);\n if (!newClassId) {\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to import class\",\n });\n }\n return newClassId;\n } catch (err) {\n if (err instanceof TRPCError) throw err;\n const message = err instanceof Error ? err.message : \"Unknown error\";\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: `Failed to import class: ${message}`,\n cause: err,\n });\n }\n}"],"names":[],"mappings":"AAAA;;;GAGG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,8BAA8B,EAC9B,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc;IAChD,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzD,kBAAkB,CAAC,MAAM,CAAC;QAC1B,kBAAkB,CAAC,MAAM,CAAC;KAC3B,CAAC,CAAC;IAEH,OAAO;QACL,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,WAAW;YACzB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;YAC3C,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB,CAAC,CAAC;QACH,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,WAAW;YACzB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;YAC3C,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAc,EAAE,OAAe;IAC5D,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAG,MAAM,wBAAwB,CAAC,OAAO,EAAE;QACxD,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,MAAM,EAAE,MAAM,IAAI,SAAS;KAC5B,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,kBAAkB,GAAG;QACzB,GAAG,SAAS;QACZ,WAAW,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACtD,GAAG,UAAU;YACb,IAAI,EAAE,UAAU,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE;YACrC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CACpC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAC9B,EAAE,SAAS;YACZ,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAC9B,EAAE,QAAQ;SACZ,CAAC,CAAC;KACJ,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAEtD,OAAO;QACL,KAAK,EAAE;YACL,GAAG,kBAAkB;YACrB,QAAQ;SACT;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,IAA2D;IAE3D,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI;QACJ,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd;KACF,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,KAOC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAEpE,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvE,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE;gBACJ,IAAI;gBACJ,OAAO;gBACP,OAAO;gBACP,KAAK;gBACL,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;gBACrD,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;aACtD;YACD,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACzB,IAAI,EAAE;YACJ,IAAI;YACJ,OAAO;YACP,OAAO;YACP,KAAK;YACL,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACtC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QACjD,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;KACvB,CAAC,CAAC;IAEH,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;KACvB,CAAC,CAAC;IAEH,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,SAAiB;IACjE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;SACzC;QACD,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,MAAc,EACd,IAA2B;IAE3B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE;gBAC9C,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aACxB;YACD,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE;gBAC9C,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aAC3B;SACF;KACF,CAAC,CAAC;IAEH,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE;KACxB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,MAAc;IAChE,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;YACxC,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACzC;KACF,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,OAAe;IAC9D,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAC7C,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,wDAAwD;SAClE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACzC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,SAAiB;IAC/D,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAExD,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,OAAO,EAAE;QAC9B,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACtC;QACD,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe;IACjD,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEpD,IAAI,CAAC,OAAO,EAAE,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACvE,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAC7C,IAAI,EAAE;gBACJ,EAAE,EAAE,kBAAkB,EAAE;gBACxB,OAAO;gBACP,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;aACtD;SACF,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe;IACpD,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;QAC9B,KAAK,EAAE,EAAE,OAAO,EAAE;KACnB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1C,IAAI,EAAE;YACJ,EAAE,EAAE,kBAAkB,EAAE;YACxB,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;SACtD;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,OAAe,EACf,YAAoB;IAEpB,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,MAAM,KAAK,YAAY,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1C,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,mCAAmC;SAC7C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACrE,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,YAAoB,EACpB,YAAoB,EACpB,aAA4B;IAE5B,MAAM,iBAAiB,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QACvD,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE;QACzC,IAAI,EAAE,EAAE,aAAa,EAAE;QACvB,OAAO,EAAE;YACP,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,KAAK,EAAE,IAAI;oBACX,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,IAAI;iBACb;aACF;SACF;KACF,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,OAAO,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe,EAAE,SAAiB;IACvE,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,YAAoB,EACpB,OAAe,EACf,SAAiB;IAEjB,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE;QAC3B,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,YAAoB;IACzD,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE;KAC5B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe;IACzD,OAAO,8BAA8B,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe,EAAE,SAAiB;IAC5E,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;QACnC,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,iBAAyB,EACzB,OAAe,EACf,SAAiB;IAEjB,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,EAAE,EAAE,EAAE,iBAAiB,EAAE;QAChC,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,iBAAyB;IACnE,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,EAAE,EAAE,EAAE,iBAAiB,EAAE;KACjC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,iBAAiB,GAAG,MAAM,8BAA8B,CAAC,OAAO,CAAC,CAAC;IACxE,OAAO;QACL,QAAQ,EAAE,SAAS,EAAE,QAAQ;QAC7B,iBAAiB;QACjB,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,QAAgB;IACpE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,sBAAsB;SAChC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACzB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB;IACpE,OAAO,oBAAoB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,SAAiB,EACjB,KAWC;IAED,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,IAAI,EAAE;YACJ,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK;YAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC;YAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;YACzB,OAAO,EACL,KAAK,CAAC,OAAO;gBACb,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;YAChD,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,IAAI,EAAE,KAAK,CAAC,IAAW;YACvB,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;YACtD,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;YAC/D,GAAG,CAAC,KAAK,CAAC,iBAAiB,IAAI;gBAC7B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;aAC3C,CAAC;SACH;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,SAAiB,EACjB,OAAe,EACf,KAA8B;IAE9B,MAAM,UAAU,GAA4B;QAC1C,UAAU,EAAE,IAAI,IAAI,EAAE;KACvB,CAAC;IACF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,UAAU,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAClC,UAAU,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IAC/C,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,UAAU,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACpE,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;QAAE,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACjE,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,UAAU,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAC1E,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAClC,UAAU,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IAC/C,IAAI,KAAK,CAAC,iBAAiB,KAAK,SAAS;QACvC,UAAU,CAAC,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAEzD,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;QACD,IAAI,EAAE,UAAiB;KACxB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,SAAiB,EACjB,OAAe;IAEf,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAe,EACf,SAAiB,EACjB,OAAe,EACf,KAKC;IAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;QAClD,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,UAAU,EAAE,KAAK;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;YAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,IAAI,GAAG;YACpD,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC;YAC5C,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;YACxD,UAAU,EAAE,IAAI,IAAI,EAAE;SACvB;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAe;IAC5C,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAC5D,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACtC,EAAE,EAAE,UAAU,CAAC,EAAE;QACjB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,kBAAkB,EAAE,UAAU,CAAC,WAAW;QAC1C,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE;YACzB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ;YACrC,WAAW,EAAE,UAAU,CAAC,WAAW;YACnC,WAAW,EAAE,UAAU,CAAC,WAAW;SACpC,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,MAAc;IAC/D,MAAM,WAAW,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,wBAAwB;SAClC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,MAAc,EAAE,IAAY,EAAE,SAAgD;IAC/H,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/E,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,wBAAwB;aAClC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,SAAS;YAAE,MAAM,GAAG,CAAC;QACxC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,2BAA2B,OAAO,EAAE;YAC7C,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC;AACH,CAAC","debug_id":"f6a97cb7-783f-56a4-ae40-1d145ee5c1ee"}
1
+ {"version":3,"file":"class.js","sources":["services/class.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Class service – CRUD for classes, members, grades, mark schemes, grading boundaries,\n * syllabus, lab drafts, and invite codes.\n */\nimport { SessionType } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { generateInviteCode } from \"../utils/generateInviteCode.js\";\nimport {\n findTeacherClasses,\n findStudentClasses,\n isTeacherInClass,\n findClassWithAssignments,\n findSectionsByClassId,\n findUserById,\n findSessionByClassCode,\n findSessionByClassId,\n findSubmissionsForGrades,\n findEventsByClassId,\n findMarkSchemesByClassId,\n findGradingBoundariesByClassId,\n findClassSyllabus,\n findLabDraftsByClass,\n findAssignmentsWithFiles,\n findFullExportableClass,\n createClassByImport,\n} from \"../models/class.js\";\nimport { Class, Folder } from \"@prisma/client\";\n\n/** Get all classes where user is teacher or student, with assignments and members. */\nexport async function getAllClasses(userId: string) {\n const [teacherClasses, studentClasses] = await Promise.all([\n findTeacherClasses(userId),\n findStudentClasses(userId),\n ]);\n\n return {\n teacherInClass: teacherClasses.map((cls) => ({\n id: cls.id,\n name: cls.name,\n section: cls.section,\n subject: cls.subject,\n dueToday: cls.assignments,\n assignments: cls.assignments,\n members: [...cls.students, ...cls.teachers],\n color: cls.color,\n })),\n studentInClass: studentClasses.map((cls) => ({\n id: cls.id,\n name: cls.name,\n section: cls.section,\n subject: cls.subject,\n dueToday: cls.assignments,\n assignments: cls.assignments,\n members: [...cls.students, ...cls.teachers],\n color: cls.color,\n })),\n };\n}\n\n/** Get single class with assignments, sections, and submission status. */\nexport async function getClass(userId: string, classId: string) {\n const isTeacher = await isTeacherInClass(classId, userId ?? \"\");\n\n const classData = await findClassWithAssignments(classId, {\n isTeacher: !!isTeacher,\n userId: userId ?? undefined,\n });\n\n if (!classData) {\n throw new Error(\"Class not found\");\n }\n\n const formattedClassData = {\n ...classData,\n assignments: classData.assignments.map((assignment) => ({\n ...assignment,\n late: assignment.dueDate < new Date(),\n submitted: assignment.submissions.find(\n (s) => s.studentId === userId\n )?.submitted,\n returned: assignment.submissions.find(\n (s) => s.studentId === userId\n )?.returned,\n })),\n };\n\n const sections = await findSectionsByClassId(classId);\n\n return {\n class: {\n ...formattedClassData,\n sections,\n },\n };\n}\n\nexport async function updateClass(\n classId: string,\n data: { name?: string; section?: string; subject?: string }\n) {\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data,\n select: {\n id: true,\n name: true,\n section: true,\n subject: true,\n },\n });\n return { updatedClass };\n}\n\nexport async function createClass(\n userId: string,\n input: {\n students?: string[];\n teachers?: string[];\n name: string;\n section: string;\n subject: string;\n color?: string;\n }\n) {\n const { students, teachers, name, section, subject, color } = input;\n\n if (teachers && teachers.length > 0 && students && students.length > 0) {\n return prisma.class.create({\n data: {\n name,\n section,\n subject,\n color,\n teachers: { connect: teachers.map((id) => ({ id })) },\n students: { connect: students.map((id) => ({ id })) },\n },\n include: { teachers: true, students: true },\n });\n }\n\n return prisma.class.create({\n data: {\n name,\n section,\n subject,\n color,\n teachers: { connect: { id: userId } },\n },\n });\n}\n\nexport async function deleteClass(classId: string) {\n const classToDelete = await prisma.class.findFirst({\n where: { id: classId },\n });\n\n if (!classToDelete) {\n throw new Error(\"Class not found or you don't have permission to delete it\");\n }\n\n await prisma.class.delete({\n where: { id: classId },\n });\n\n return { deletedClass: { id: classId } };\n}\n\nexport async function addStudent(classId: string, studentId: string) {\n const student = await findUserById(studentId);\n if (!student) {\n throw new Error(\"Student not found\");\n }\n\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data: {\n students: { connect: { id: studentId } },\n },\n select: {\n id: true,\n name: true,\n section: true,\n subject: true,\n },\n });\n\n return { updatedClass, newStudent: student };\n}\n\nexport async function changeRole(\n classId: string,\n userId: string,\n type: \"teacher\" | \"student\"\n) {\n const user = await findUserById(userId);\n if (!user) {\n throw new Error(\"User not found\");\n }\n\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data: {\n [type === \"teacher\" ? \"teachers\" : \"students\"]: {\n connect: { id: userId },\n },\n [type === \"teacher\" ? \"students\" : \"teachers\"]: {\n disconnect: { id: userId },\n },\n },\n });\n\n return {\n updatedClass,\n user: { ...user, type },\n };\n}\n\nexport async function removeMember(classId: string, userId: string) {\n const updatedClass = await prisma.class.update({\n where: { id: classId },\n data: {\n teachers: { disconnect: { id: userId } },\n students: { disconnect: { id: userId } },\n },\n });\n return { updatedClass, removedUserId: userId };\n}\n\nexport async function leaveClass(userId: string, classId: string) {\n const classData = await prisma.class.findFirst({\n where: {\n id: classId,\n students: { some: { id: userId } },\n },\n });\n\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a student in this class\",\n });\n }\n\n await prisma.class.update({\n where: { id: classId },\n data: {\n students: { disconnect: { id: userId } },\n },\n });\n\n return { success: true, leftClassId: classId };\n}\n\nexport async function joinClass(userId: string, classCode: string) {\n const session = await findSessionByClassCode(classCode);\n\n if (!session || !session.classId) {\n throw new Error(\"Class not found\");\n }\n\n if (session.expiresAt && session.expiresAt < new Date()) {\n throw new Error(\"Session expired\");\n }\n\n const updatedClass = await prisma.class.update({\n where: { id: session.classId },\n data: {\n students: { connect: { id: userId } },\n },\n select: {\n id: true,\n name: true,\n section: true,\n subject: true,\n },\n });\n\n return { joinedClass: updatedClass };\n}\n\nexport async function getInviteCode(classId: string) {\n const session = await findSessionByClassId(classId);\n\n if ((session?.expiresAt && session.expiresAt < new Date()) || !session) {\n const newSession = await prisma.session.create({\n data: {\n id: generateInviteCode(),\n classId,\n type: SessionType.CLASS_INVITE,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),\n },\n });\n return { code: newSession.id };\n }\n\n return { code: session?.id };\n}\n\nexport async function createInviteCode(classId: string) {\n await prisma.session.deleteMany({\n where: { classId, type: SessionType.CLASS_INVITE },\n });\n\n const session = await prisma.session.create({\n data: {\n id: generateInviteCode(),\n classId,\n type: SessionType.CLASS_INVITE,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),\n },\n });\n\n return { code: session.id };\n}\n\nexport async function getGrades(\n userId: string,\n classId: string,\n targetUserId: string\n) {\n const isTeacher = await isTeacherInClass(classId, userId);\n if (userId !== targetUserId && !isTeacher) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You can only view your own grades\",\n });\n }\n\n const grades = await findSubmissionsForGrades(targetUserId, classId);\n return { grades };\n}\n\nexport async function updateGrade(\n assignmentId: string,\n submissionId: string,\n gradeReceived: number | null\n) {\n const updatedSubmission = await prisma.submission.update({\n where: { id: submissionId, assignmentId },\n data: { gradeReceived },\n include: {\n assignment: {\n select: {\n id: true,\n title: true,\n maxGrade: true,\n weight: true,\n },\n },\n },\n });\n return updatedSubmission;\n}\n\nexport async function getEvents(classId: string) {\n return findEventsByClassId(classId);\n}\n\nexport async function listMarkSchemes(classId: string) {\n return findMarkSchemesByClassId(classId);\n}\n\nexport async function createMarkScheme(classId: string, structure: string) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.markScheme.create({\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function updateMarkScheme(\n markSchemeId: string,\n classId: string,\n structure: string\n) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.markScheme.update({\n where: { id: markSchemeId },\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function deleteMarkScheme(markSchemeId: string) {\n return prisma.markScheme.delete({\n where: { id: markSchemeId },\n });\n}\n\nexport async function listGradingBoundaries(classId: string) {\n return findGradingBoundariesByClassId(classId);\n}\n\nexport async function createGradingBoundary(classId: string, structure: string) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.gradingBoundary.create({\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function updateGradingBoundary(\n gradingBoundaryId: string,\n classId: string,\n structure: string\n) {\n const validatedStructure = structure.replace(/\\\\n/g, \"\\n\");\n return prisma.gradingBoundary.update({\n where: { id: gradingBoundaryId },\n data: {\n class: { connect: { id: classId } },\n structured: validatedStructure,\n },\n });\n}\n\nexport async function deleteGradingBoundary(gradingBoundaryId: string) {\n return prisma.gradingBoundary.delete({\n where: { id: gradingBoundaryId },\n });\n}\n\nexport async function getSyllabus(classId: string) {\n const classData = await findClassSyllabus(classId);\n const markSchemes = await findMarkSchemesByClassId(classId);\n const gradingBoundaries = await findGradingBoundariesByClassId(classId);\n return {\n syllabus: classData?.syllabus,\n gradingBoundaries,\n markSchemes,\n };\n}\n\nexport async function updateSyllabus(classId: string, contents: string) {\n if (!contents) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Missing key contents\",\n });\n }\n return prisma.class.update({\n where: { id: classId },\n data: { syllabus: contents },\n });\n}\n\nexport async function listLabDrafts(classId: string, teacherId: string) {\n return findLabDraftsByClass(classId, teacherId);\n}\n\nexport async function createLabDraft(\n classId: string,\n teacherId: string,\n input: {\n title: string;\n type: string;\n instructions: string;\n dueDate?: Date;\n maxGrade?: number;\n weight?: number;\n graded?: boolean;\n sectionId?: string;\n markSchemeId?: string;\n gradingBoundaryId?: string;\n }\n) {\n return prisma.assignment.create({\n data: {\n classId,\n teacherId,\n inProgress: true,\n graded: input.graded ?? false,\n maxGrade: input.maxGrade ?? 0,\n weight: input.weight ?? 1,\n dueDate:\n input.dueDate ||\n new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\n title: input.title,\n instructions: input.instructions,\n type: input.type as any,\n ...(input.sectionId && { sectionId: input.sectionId }),\n ...(input.markSchemeId && { markSchemeId: input.markSchemeId }),\n ...(input.gradingBoundaryId && {\n gradingBoundaryId: input.gradingBoundaryId,\n }),\n },\n });\n}\n\nexport async function updateLabDraft(\n classId: string,\n teacherId: string,\n draftId: string,\n input: Record<string, unknown>\n) {\n const updateData: Record<string, unknown> = {\n modifiedAt: new Date(),\n };\n if (input.title !== undefined) updateData.title = input.title;\n if (input.instructions !== undefined)\n updateData.instructions = input.instructions;\n if (input.dueDate !== undefined) updateData.dueDate = input.dueDate;\n if (input.maxGrade !== undefined) updateData.maxGrade = input.maxGrade;\n if (input.weight !== undefined) updateData.weight = input.weight;\n if (input.graded !== undefined) updateData.graded = input.graded;\n if (input.sectionId !== undefined) updateData.sectionId = input.sectionId;\n if (input.markSchemeId !== undefined)\n updateData.markSchemeId = input.markSchemeId;\n if (input.gradingBoundaryId !== undefined)\n updateData.gradingBoundaryId = input.gradingBoundaryId;\n\n return prisma.assignment.update({\n where: {\n id: draftId,\n classId,\n teacherId,\n inProgress: true,\n },\n data: updateData as any,\n });\n}\n\nexport async function deleteLabDraft(\n classId: string,\n teacherId: string,\n draftId: string\n) {\n return prisma.assignment.delete({\n where: {\n id: draftId,\n classId,\n teacherId,\n inProgress: true,\n },\n });\n}\n\nexport async function publishLabDraft(\n classId: string,\n teacherId: string,\n draftId: string,\n input: {\n dueDate?: Date;\n maxGrade?: number;\n weight?: number;\n graded?: boolean;\n }\n) {\n const labDraft = await prisma.assignment.findUnique({\n where: {\n id: draftId,\n classId,\n teacherId,\n inProgress: true,\n },\n });\n\n if (!labDraft) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Lab draft not found\",\n });\n }\n\n return prisma.assignment.update({\n where: { id: draftId },\n data: {\n inProgress: false,\n dueDate: input.dueDate || labDraft.dueDate,\n maxGrade: input.maxGrade || labDraft.maxGrade || 100,\n weight: input.weight || labDraft.weight || 1,\n graded: input.graded !== undefined ? input.graded : true,\n modifiedAt: new Date(),\n },\n });\n}\n\nexport async function getFiles(classId: string) {\n const assignments = await findAssignmentsWithFiles(classId);\n return assignments.map((assignment) => ({\n id: assignment.id,\n title: assignment.title,\n teacher: assignment.teacher,\n teacherAttachments: assignment.attachments,\n students: assignment.submissions.map((submission) => ({\n id: submission.student.id,\n username: submission.student.username,\n attachments: submission.attachments,\n annotations: submission.annotations,\n })),\n }));\n}\n\nexport async function exportClass(classId: string, userId: string) {\n const classExport = await findFullExportableClass(classId);\n if (!classExport) {\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to export class\",\n });\n }\n\n return classExport;\n}\n\nexport async function importClass(classId: string, userId: string, year: number, classData: Class & { classFiles: Folder | null }) {\n try {\n const newClassId = await createClassByImport(classId, userId, year, classData);\n if (!newClassId) {\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to import class\",\n });\n }\n return newClassId;\n } catch (err) {\n if (err instanceof TRPCError) throw err;\n const message = err instanceof Error ? err.message : \"Unknown error\";\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: `Failed to import class: ${message}`,\n cause: err,\n });\n }\n}"],"names":[],"mappings":"AAAA;;;GAGG;;;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,8BAA8B,EAC9B,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc;IAChD,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzD,kBAAkB,CAAC,MAAM,CAAC;QAC1B,kBAAkB,CAAC,MAAM,CAAC;KAC3B,CAAC,CAAC;IAEH,OAAO;QACL,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,WAAW;YACzB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;YAC3C,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB,CAAC,CAAC;QACH,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,WAAW;YACzB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;YAC3C,KAAK,EAAE,GAAG,CAAC,KAAK;SACjB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAc,EAAE,OAAe;IAC5D,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAG,MAAM,wBAAwB,CAAC,OAAO,EAAE;QACxD,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,MAAM,EAAE,MAAM,IAAI,SAAS;KAC5B,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,kBAAkB,GAAG;QACzB,GAAG,SAAS;QACZ,WAAW,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACtD,GAAG,UAAU;YACb,IAAI,EAAE,UAAU,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE;YACrC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CACpC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAC9B,EAAE,SAAS;YACZ,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAC9B,EAAE,QAAQ;SACZ,CAAC,CAAC;KACJ,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAEtD,OAAO;QACL,KAAK,EAAE;YACL,GAAG,kBAAkB;YACrB,QAAQ;SACT;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,IAA2D;IAE3D,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI;QACJ,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd;KACF,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,KAOC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAEpE,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvE,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE;gBACJ,IAAI;gBACJ,OAAO;gBACP,OAAO;gBACP,KAAK;gBACL,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;gBACrD,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;aACtD;YACD,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACzB,IAAI,EAAE;YACJ,IAAI;YACJ,OAAO;YACP,OAAO;YACP,KAAK;YACL,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACtC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QACjD,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;KACvB,CAAC,CAAC;IAEH,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;KACvB,CAAC,CAAC;IAEH,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,SAAiB;IACjE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;SACzC;QACD,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,MAAc,EACd,IAA2B;IAE3B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE;gBAC9C,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aACxB;YACD,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE;gBAC9C,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aAC3B;SACF;KACF,CAAC,CAAC;IAEH,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE;KACxB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,MAAc;IAChE,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;YACxC,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACzC;KACF,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,OAAe;IAC9D,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAC7C,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,wDAAwD;SAClE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACzC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,SAAiB;IAC/D,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAExD,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,OAAO,EAAE;QAC9B,IAAI,EAAE;YACJ,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;SACtC;QACD,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,IAAI;SACd;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe;IACjD,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEpD,IAAI,CAAC,OAAO,EAAE,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACvE,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAC7C,IAAI,EAAE;gBACJ,EAAE,EAAE,kBAAkB,EAAE;gBACxB,OAAO;gBACP,IAAI,EAAE,WAAW,CAAC,YAAY;gBAC9B,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;aACtD;SACF,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe;IACpD,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;QAC9B,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,YAAY,EAAE;KACnD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1C,IAAI,EAAE;YACJ,EAAE,EAAE,kBAAkB,EAAE;YACxB,OAAO;YACP,IAAI,EAAE,WAAW,CAAC,YAAY;YAC9B,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;SACtD;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,OAAe,EACf,YAAoB;IAEpB,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,MAAM,KAAK,YAAY,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1C,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,mCAAmC;SAC7C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACrE,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,YAAoB,EACpB,YAAoB,EACpB,aAA4B;IAE5B,MAAM,iBAAiB,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QACvD,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE;QACzC,IAAI,EAAE,EAAE,aAAa,EAAE;QACvB,OAAO,EAAE;YACP,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,EAAE,EAAE,IAAI;oBACR,KAAK,EAAE,IAAI;oBACX,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,IAAI;iBACb;aACF;SACF;KACF,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,OAAO,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe,EAAE,SAAiB;IACvE,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,YAAoB,EACpB,OAAe,EACf,SAAiB;IAEjB,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE;QAC3B,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,YAAoB;IACzD,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE;KAC5B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe;IACzD,OAAO,8BAA8B,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe,EAAE,SAAiB;IAC5E,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;QACnC,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,iBAAyB,EACzB,OAAe,EACf,SAAiB;IAEjB,MAAM,kBAAkB,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,EAAE,EAAE,EAAE,iBAAiB,EAAE;QAChC,IAAI,EAAE;YACJ,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE;YACnC,UAAU,EAAE,kBAAkB;SAC/B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,iBAAyB;IACnE,OAAO,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,EAAE,EAAE,EAAE,iBAAiB,EAAE;KACjC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,iBAAiB,GAAG,MAAM,8BAA8B,CAAC,OAAO,CAAC,CAAC;IACxE,OAAO;QACL,QAAQ,EAAE,SAAS,EAAE,QAAQ;QAC7B,iBAAiB;QACjB,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,QAAgB;IACpE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,sBAAsB;SAChC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QACzB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB;IACpE,OAAO,oBAAoB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,SAAiB,EACjB,KAWC;IAED,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,IAAI,EAAE;YACJ,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK;YAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC;YAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;YACzB,OAAO,EACL,KAAK,CAAC,OAAO;gBACb,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;YAChD,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,IAAI,EAAE,KAAK,CAAC,IAAW;YACvB,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;YACtD,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;YAC/D,GAAG,CAAC,KAAK,CAAC,iBAAiB,IAAI;gBAC7B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;aAC3C,CAAC;SACH;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,SAAiB,EACjB,OAAe,EACf,KAA8B;IAE9B,MAAM,UAAU,GAA4B;QAC1C,UAAU,EAAE,IAAI,IAAI,EAAE;KACvB,CAAC;IACF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,UAAU,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAClC,UAAU,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IAC/C,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,UAAU,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACpE,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;QAAE,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACjE,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,UAAU,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAC1E,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAClC,UAAU,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IAC/C,IAAI,KAAK,CAAC,iBAAiB,KAAK,SAAS;QACvC,UAAU,CAAC,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAEzD,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;QACD,IAAI,EAAE,UAAiB;KACxB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,SAAiB,EACjB,OAAe;IAEf,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAe,EACf,SAAiB,EACjB,OAAe,EACf,KAKC;IAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;QAClD,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,OAAO;YACP,SAAS;YACT,UAAU,EAAE,IAAI;SACjB;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,IAAI,EAAE;YACJ,UAAU,EAAE,KAAK;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;YAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,IAAI,GAAG;YACpD,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC;YAC5C,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;YACxD,UAAU,EAAE,IAAI,IAAI,EAAE;SACvB;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAe;IAC5C,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAC5D,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACtC,EAAE,EAAE,UAAU,CAAC,EAAE;QACjB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,kBAAkB,EAAE,UAAU,CAAC,WAAW;QAC1C,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE;YACzB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ;YACrC,WAAW,EAAE,UAAU,CAAC,WAAW;YACnC,WAAW,EAAE,UAAU,CAAC,WAAW;SACpC,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,MAAc;IAC/D,MAAM,WAAW,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,wBAAwB;SAClC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,MAAc,EAAE,IAAY,EAAE,SAAgD;IAC/H,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/E,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,wBAAwB;aAClC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,SAAS;YAAE,MAAM,GAAG,CAAC;QACxC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,2BAA2B,OAAO,EAAE;YAC7C,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC;AACH,CAAC","debug_id":"b0980742-2fbe-5c12-897f-ba5bf6ed98c4"}
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"/","sources":["services/file.ts"],"names":[],"mappings":"AAiBA,0EAA0E;AAC1E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;GAwEpE;AAED,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmBvB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkBhB;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;GA6BrE"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"/","sources":["services/file.ts"],"names":[],"mappings":"AAkBA,0EAA0E;AAC1E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;GA6EpE;AAED,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmBvB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkBhB;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;GA6BrE"}
@@ -3,11 +3,12 @@
3
3
  * for assignment, submission, announcement, folder, and user-owned files.
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]="df3e48dc-35bf-5218-b82c-a8934040efcf")}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]="c9a35639-e642-5ed5-b260-9bacbf8cb7e9")}catch(e){}}();
7
7
  import { TRPCError } from "@trpc/server";
8
8
  import { getSignedUrl, deleteFile } from "../lib/googleCloudStorage.js";
9
9
  import { logger } from "../utils/logger.js";
10
10
  import { findFileById, findFileWithFolder, findFileWithFolderAndThumbnail, findFolderById, updateFileFolder, updateFileName, deleteFileById, } from "../models/file.js";
11
+ import { findConversationMembership } from "../models/message.js";
11
12
  /** Generate a signed URL for file download. Validates user has access. */
12
13
  export async function getFileSignedUrl(userId, fileId) {
13
14
  const file = await findFileById(fileId);
@@ -45,6 +46,10 @@ export async function getFileSignedUrl(userId, fileId) {
45
46
  if (file.userId === userId) {
46
47
  hasAccess = true;
47
48
  }
49
+ if (file.conversationId) {
50
+ const membership = await findConversationMembership(file.conversationId, userId);
51
+ hasAccess = hasAccess || Boolean(membership);
52
+ }
48
53
  if (file.folder?.class) {
49
54
  const isTeacher = file.folder.class.teachers.some((t) => t.id === userId);
50
55
  const isStudent = file.folder.class.students.some((s) => s.id === userId);
@@ -130,4 +135,4 @@ export async function deleteFileRecord(fileId, classId) {
130
135
  return { success: true };
131
136
  }
132
137
  //# sourceMappingURL=file.js.map
133
- //# debugId=df3e48dc-35bf-5218-b82c-a8934040efcf
138
+ //# debugId=c9a35639-e642-5ed5-b260-9bacbf8cb7e9
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","sources":["services/file.ts"],"sourceRoot":"/","sourcesContent":["/**\n * File service – signed URLs, move, rename, delete. Handles permission checks\n * for assignment, submission, announcement, folder, and user-owned files.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { getSignedUrl, deleteFile } from \"../lib/googleCloudStorage.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n findFileById,\n findFileWithFolder,\n findFileWithFolderAndThumbnail,\n findFolderById,\n updateFileFolder,\n updateFileName,\n deleteFileById,\n} from \"../models/file.js\";\n\n/** Generate a signed URL for file download. Validates user has access. */\nexport async function getFileSignedUrl(userId: string, fileId: string) {\n const file = await findFileById(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File does not exist\",\n });\n }\n\n let hasAccess = false;\n\n if (file.assignment?.class) {\n const isTeacher = file.assignment.class.teachers.some(\n (t) => t.id === userId\n );\n const isStudent = file.assignment.class.students.some(\n (s) => s.id === userId\n );\n logger.info(\n `Assignment file access check - userId: ${userId}, isTeacher: ${isTeacher}, isStudent: ${isStudent}`\n );\n hasAccess = isTeacher || isStudent;\n }\n\n if ((file as any).announcement?.class) {\n const ann = (file as any).announcement;\n const isTeacher = ann.class.teachers.some((t: any) => t.id === userId);\n const isStudent = ann.class.students.some((s: any) => s.id === userId);\n hasAccess = hasAccess || isTeacher || isStudent;\n }\n\n if (file.submission?.assignment?.classId) {\n hasAccess =\n hasAccess ||\n file.submission.studentId === userId ||\n file.submission.assignment.class.teachers.some((t) => t.id === userId);\n }\n\n if (file.annotations?.assignment?.classId) {\n hasAccess =\n hasAccess ||\n file.annotations.studentId === userId ||\n file.annotations.assignment.class.teachers.some((t) => t.id === userId);\n }\n\n if (file.userId === userId) {\n hasAccess = true;\n }\n\n if (file.folder?.class) {\n const isTeacher = file.folder.class.teachers.some((t) => t.id === userId);\n const isStudent = file.folder.class.students.some((s) => s.id === userId);\n hasAccess = hasAccess || isTeacher || isStudent;\n }\n\n if (!hasAccess) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You do not have access to this file\",\n });\n }\n\n try {\n const signedUrl = await getSignedUrl(file.path);\n return { url: signedUrl };\n } catch (error) {\n logger.error(\"Error generating signed URL:\", error as Record<string, unknown>);\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to generate download URL\",\n });\n }\n}\n\nexport async function moveFile(\n fileId: string,\n targetFolderId: string\n) {\n const file = await findFileWithFolder(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found\",\n });\n }\n\n const targetFolder = await findFolderById(targetFolderId);\n if (!targetFolder) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Target folder not found\",\n });\n }\n\n return updateFileFolder(fileId, targetFolderId);\n}\n\nexport async function renameFile(\n fileId: string,\n newName: string\n) {\n const file = await findFileWithFolder(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found\",\n });\n }\n\n if (!newName.trim()) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"File name cannot be empty\",\n });\n }\n\n return updateFileName(fileId, newName.trim());\n}\n\nexport async function deleteFileRecord(fileId: string, classId: string) {\n const file = await findFileWithFolderAndThumbnail(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found\",\n });\n }\n\n if (file.folder?.classId !== classId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"File does not belong to this class\",\n });\n }\n\n try {\n if (file.uploadStatus === \"COMPLETED\") {\n await deleteFile(file.path);\n if (file.thumbnail) {\n await deleteFile(file.thumbnail.path);\n }\n }\n } catch (error) {\n logger.warn(`Failed to delete file ${file.path}:`, error as Record<string, unknown>);\n }\n\n await deleteFileById(fileId);\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"df3e48dc-35bf-5218-b82c-a8934040efcf"}
1
+ {"version":3,"file":"file.js","sources":["services/file.ts"],"sourceRoot":"/","sourcesContent":["/**\n * File service – signed URLs, move, rename, delete. Handles permission checks\n * for assignment, submission, announcement, folder, and user-owned files.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { getSignedUrl, deleteFile } from \"../lib/googleCloudStorage.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n findFileById,\n findFileWithFolder,\n findFileWithFolderAndThumbnail,\n findFolderById,\n updateFileFolder,\n updateFileName,\n deleteFileById,\n} from \"../models/file.js\";\nimport { findConversationMembership } from \"../models/message.js\";\n\n/** Generate a signed URL for file download. Validates user has access. */\nexport async function getFileSignedUrl(userId: string, fileId: string) {\n const file = await findFileById(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File does not exist\",\n });\n }\n\n let hasAccess = false;\n\n if (file.assignment?.class) {\n const isTeacher = file.assignment.class.teachers.some(\n (t) => t.id === userId\n );\n const isStudent = file.assignment.class.students.some(\n (s) => s.id === userId\n );\n logger.info(\n `Assignment file access check - userId: ${userId}, isTeacher: ${isTeacher}, isStudent: ${isStudent}`\n );\n hasAccess = isTeacher || isStudent;\n }\n\n if ((file as any).announcement?.class) {\n const ann = (file as any).announcement;\n const isTeacher = ann.class.teachers.some((t: any) => t.id === userId);\n const isStudent = ann.class.students.some((s: any) => s.id === userId);\n hasAccess = hasAccess || isTeacher || isStudent;\n }\n\n if (file.submission?.assignment?.classId) {\n hasAccess =\n hasAccess ||\n file.submission.studentId === userId ||\n file.submission.assignment.class.teachers.some((t) => t.id === userId);\n }\n\n if (file.annotations?.assignment?.classId) {\n hasAccess =\n hasAccess ||\n file.annotations.studentId === userId ||\n file.annotations.assignment.class.teachers.some((t) => t.id === userId);\n }\n\n if (file.userId === userId) {\n hasAccess = true;\n }\n\n if (file.conversationId) {\n const membership = await findConversationMembership(file.conversationId, userId);\n hasAccess = hasAccess || Boolean(membership);\n }\n\n if (file.folder?.class) {\n const isTeacher = file.folder.class.teachers.some((t) => t.id === userId);\n const isStudent = file.folder.class.students.some((s) => s.id === userId);\n hasAccess = hasAccess || isTeacher || isStudent;\n }\n\n if (!hasAccess) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You do not have access to this file\",\n });\n }\n\n try {\n const signedUrl = await getSignedUrl(file.path);\n return { url: signedUrl };\n } catch (error) {\n logger.error(\"Error generating signed URL:\", error as Record<string, unknown>);\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to generate download URL\",\n });\n }\n}\n\nexport async function moveFile(\n fileId: string,\n targetFolderId: string\n) {\n const file = await findFileWithFolder(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found\",\n });\n }\n\n const targetFolder = await findFolderById(targetFolderId);\n if (!targetFolder) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Target folder not found\",\n });\n }\n\n return updateFileFolder(fileId, targetFolderId);\n}\n\nexport async function renameFile(\n fileId: string,\n newName: string\n) {\n const file = await findFileWithFolder(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found\",\n });\n }\n\n if (!newName.trim()) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"File name cannot be empty\",\n });\n }\n\n return updateFileName(fileId, newName.trim());\n}\n\nexport async function deleteFileRecord(fileId: string, classId: string) {\n const file = await findFileWithFolderAndThumbnail(fileId);\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found\",\n });\n }\n\n if (file.folder?.classId !== classId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"File does not belong to this class\",\n });\n }\n\n try {\n if (file.uploadStatus === \"COMPLETED\") {\n await deleteFile(file.path);\n if (file.thumbnail) {\n await deleteFile(file.thumbnail.path);\n }\n }\n } catch (error) {\n logger.warn(`Failed to delete file ${file.path}:`, error as Record<string, unknown>);\n }\n\n await deleteFileById(fileId);\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"c9a35639-e642-5ed5-b260-9bacbf8cb7e9"}
@@ -143,6 +143,7 @@ export declare function postToLabChat(userId: string, input: {
143
143
  labChatId: string;
144
144
  content: string;
145
145
  mentionedUserIds?: string[];
146
+ fileIds?: string[];
146
147
  }): Promise<{
147
148
  id: string;
148
149
  content: string;
@@ -157,6 +158,11 @@ export declare function postToLabChat(userId: string, input: {
157
158
  profilePicture: string | null;
158
159
  } | null;
159
160
  };
161
+ attachments: {
162
+ type: string;
163
+ name: string;
164
+ id: string;
165
+ }[];
160
166
  mentionedUserIds: string[];
161
167
  }>;
162
168
  export declare function deleteLabChat(userId: string, labChatId: string): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["services/labChat.ts"],"names":[],"mappings":"AAuBA,sFAAsF;AACtF,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsH3D;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCjE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqBjE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;;;;;;;;;;;;;;;GAoHF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiDpE;AAED,8FAA8F;AAC9F,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiFxE"}
1
+ {"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["services/labChat.ts"],"names":[],"mappings":"AA2BA,sFAAsF;AACtF,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsH3D;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCjE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqBjE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;GAuIF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiDpE;AAED,8FAA8F;AAC9F,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiFxE"}