@studious-lms/server 1.8.0 → 1.9.0

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 (71) hide show
  1. package/dist/middleware/security.d.ts.map +1 -1
  2. package/dist/middleware/security.js +3 -3
  3. package/dist/middleware/security.js.map +1 -1
  4. package/dist/models/agenda.d.ts +2 -2
  5. package/dist/models/announcement.d.ts +2 -2
  6. package/dist/models/assignment.d.ts +33 -33
  7. package/dist/models/class.d.ts +2 -2
  8. package/dist/models/cloudStorage.d.ts +14 -1
  9. package/dist/models/cloudStorage.d.ts.map +1 -1
  10. package/dist/models/cloudStorage.js +123 -3
  11. package/dist/models/cloudStorage.js.map +1 -1
  12. package/dist/models/event.d.ts +3 -3
  13. package/dist/models/file.d.ts +27 -26
  14. package/dist/models/file.d.ts.map +1 -1
  15. package/dist/models/file.js +4 -39
  16. package/dist/models/file.js.map +1 -1
  17. package/dist/models/labChat.d.ts +6 -6
  18. package/dist/models/section.d.ts +1 -1
  19. package/dist/routers/_app.d.ts +354 -100
  20. package/dist/routers/_app.d.ts.map +1 -1
  21. package/dist/routers/_app.js +4 -2
  22. package/dist/routers/_app.js.map +1 -1
  23. package/dist/routers/agenda.d.ts +2 -2
  24. package/dist/routers/assignment.d.ts +31 -31
  25. package/dist/routers/class.d.ts +5 -5
  26. package/dist/routers/event.d.ts +2 -2
  27. package/dist/routers/file.d.ts +2 -2
  28. package/dist/routers/folder.d.ts +24 -0
  29. package/dist/routers/folder.d.ts.map +1 -1
  30. package/dist/routers/folder.js +27 -5
  31. package/dist/routers/folder.js.map +1 -1
  32. package/dist/routers/labChat.d.ts +2 -2
  33. package/dist/routers/newtonChat.d.ts +1 -1
  34. package/dist/routers/permissions.d.ts +120 -0
  35. package/dist/routers/permissions.d.ts.map +1 -0
  36. package/dist/routers/permissions.js +50 -0
  37. package/dist/routers/permissions.js.map +1 -0
  38. package/dist/routers/tag.d.ts +5 -5
  39. package/dist/services/agenda.d.ts +2 -2
  40. package/dist/services/assignment.d.ts +31 -31
  41. package/dist/services/class.d.ts +5 -5
  42. package/dist/services/class.js +3 -3
  43. package/dist/services/class.js.map +1 -1
  44. package/dist/services/event.d.ts +2 -2
  45. package/dist/services/file.d.ts +22 -5
  46. package/dist/services/file.d.ts.map +1 -1
  47. package/dist/services/file.js +81 -45
  48. package/dist/services/file.js.map +1 -1
  49. package/dist/services/folder.d.ts +97 -2
  50. package/dist/services/folder.d.ts.map +1 -1
  51. package/dist/services/folder.js +158 -87
  52. package/dist/services/folder.js.map +1 -1
  53. package/dist/services/labChat.d.ts +2 -2
  54. package/dist/services/permissions.d.ts +188 -0
  55. package/dist/services/permissions.d.ts.map +1 -0
  56. package/dist/services/permissions.js +661 -0
  57. package/dist/services/permissions.js.map +1 -0
  58. package/package.json +1 -1
  59. package/prisma/migrations/20260510170000_add_folder_permissions/migration.sql +93 -0
  60. package/prisma/schema.prisma +74 -0
  61. package/src/middleware/security.ts +1 -1
  62. package/src/models/cloudStorage.ts +134 -1
  63. package/src/models/file.ts +2 -37
  64. package/src/routers/_app.ts +2 -0
  65. package/src/routers/folder.ts +32 -2
  66. package/src/routers/permissions.ts +71 -0
  67. package/src/services/class.ts +1 -1
  68. package/src/services/file.ts +76 -47
  69. package/src/services/folder.ts +180 -91
  70. package/src/services/permissions.ts +778 -0
  71. package/tests/unit/services/permissions.test.ts +463 -0
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0be713b0-75ed-5799-8084-cfdbc770063e")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="26810bf7-c72f-5655-8994-96075d84b6fe")}catch(e){}}();
3
3
  import { prisma } from "../lib/prisma.js";
4
4
  const folderSelect = {
5
5
  id: true,
@@ -154,10 +154,121 @@ export function renameFolder(folderId, name, color) {
154
154
  select: folderSelect,
155
155
  });
156
156
  }
157
- export async function listFolderBreadcrumbs(folderId) {
157
+ /**
158
+ * Build the breadcrumb chain for a folder.
159
+ *
160
+ * Without `viewerId`: walks all the way up to the scope root (owner-style
161
+ * view).
162
+ *
163
+ * With `viewerId`: walks up but stops at the highest folder the viewer is
164
+ * actually granted on, OR at the scope root if the viewer is a scope member.
165
+ * This is the Drive-style "Shared with me" behavior — a grantee never sees
166
+ * ancestor folders they weren't shared on. The result for a grantee browsing
167
+ * `Owner/PrivateA/SharedB/C` (granted on B) reads `SharedB / C` — the ancestor
168
+ * `PrivateA` is correctly hidden.
169
+ */
170
+ export async function listFolderBreadcrumbs(folderId, viewerId) {
158
171
  const breadcrumbs = [];
159
172
  let currentId = folderId;
160
173
  let visits = 0;
174
+ let viewerGrantedFolderIds = null;
175
+ let viewerScopeMember = false;
176
+ if (viewerId) {
177
+ const grants = await prisma.permission.findMany({
178
+ where: {
179
+ OR: [
180
+ { principalType: "USER", userId: viewerId },
181
+ {
182
+ principalType: "TAG",
183
+ tag: { users: { some: { id: viewerId } } },
184
+ },
185
+ {
186
+ principalType: "CLASS",
187
+ class: {
188
+ OR: [
189
+ { teachers: { some: { id: viewerId } } },
190
+ { students: { some: { id: viewerId } } },
191
+ ],
192
+ },
193
+ },
194
+ {
195
+ principalType: "CLASS_TEACHERS",
196
+ class: { teachers: { some: { id: viewerId } } },
197
+ },
198
+ {
199
+ principalType: "CLASS_STUDENTS",
200
+ class: { students: { some: { id: viewerId } } },
201
+ },
202
+ {
203
+ principalType: "DEPARTMENT",
204
+ department: { members: { some: { id: viewerId } } },
205
+ },
206
+ {
207
+ principalType: "SCHOOL",
208
+ school: {
209
+ memberships: { some: { userId: viewerId, status: "ACTIVE" } },
210
+ },
211
+ },
212
+ ],
213
+ },
214
+ select: { folderId: true },
215
+ });
216
+ viewerGrantedFolderIds = new Set(grants.map((g) => g.folderId).filter((x) => Boolean(x)));
217
+ // Determine if the viewer is a member of the *target* folder's scope —
218
+ // if so, breadcrumbs go all the way to the scope root as normal.
219
+ const scope = await resolveFolderScope(folderId);
220
+ if (scope) {
221
+ switch (scope.kind) {
222
+ case "CLASS": {
223
+ const c = await prisma.class.findFirst({
224
+ where: {
225
+ id: scope.classId,
226
+ OR: [
227
+ { teachers: { some: { id: viewerId } } },
228
+ { students: { some: { id: viewerId } } },
229
+ ],
230
+ },
231
+ select: { id: true },
232
+ });
233
+ viewerScopeMember = Boolean(c);
234
+ if (!viewerScopeMember && scope.classId) {
235
+ const klass = await prisma.class.findUnique({
236
+ where: { id: scope.classId },
237
+ select: { schoolId: true },
238
+ });
239
+ if (klass?.schoolId) {
240
+ const m = await prisma.schoolMembership.findFirst({
241
+ where: {
242
+ schoolId: klass.schoolId,
243
+ userId: viewerId,
244
+ role: "ADMIN",
245
+ status: "ACTIVE",
246
+ },
247
+ select: { id: true },
248
+ });
249
+ viewerScopeMember = Boolean(m);
250
+ }
251
+ }
252
+ break;
253
+ }
254
+ case "SCHOOL_STORAGE": {
255
+ const m = await prisma.schoolMembership.findFirst({
256
+ where: {
257
+ schoolId: scope.schoolId,
258
+ userId: viewerId,
259
+ status: "ACTIVE",
260
+ },
261
+ select: { id: true },
262
+ });
263
+ viewerScopeMember = Boolean(m);
264
+ break;
265
+ }
266
+ case "PERSONAL":
267
+ viewerScopeMember = scope.userId === viewerId;
268
+ break;
269
+ }
270
+ }
271
+ }
161
272
  while (currentId && visits < 64) {
162
273
  const folder = await prisma.folder.findUnique({
163
274
  where: { id: currentId },
@@ -166,10 +277,19 @@ export async function listFolderBreadcrumbs(folderId) {
166
277
  if (!folder)
167
278
  break;
168
279
  breadcrumbs.unshift({ id: folder.id, name: folder.name });
280
+ // Viewer-aware truncation: when the viewer is *not* a scope member but
281
+ // is a grantee, stop at the highest granted ancestor (we walk up one
282
+ // more step into the granted folder, then stop there).
283
+ if (viewerId &&
284
+ !viewerScopeMember &&
285
+ viewerGrantedFolderIds &&
286
+ viewerGrantedFolderIds.has(folder.id)) {
287
+ break;
288
+ }
169
289
  currentId = folder.parentFolderId;
170
290
  visits += 1;
171
291
  }
172
292
  return breadcrumbs;
173
293
  }
174
294
  //# sourceMappingURL=cloudStorage.js.map
175
- //# debugId=0be713b0-75ed-5799-8084-cfdbc770063e
295
+ //# debugId=26810bf7-c72f-5655-8994-96075d84b6fe
@@ -1 +1 @@
1
- {"version":3,"file":"cloudStorage.js","sources":["models/cloudStorage.ts"],"sourceRoot":"/","sourcesContent":["/**\n * CloudStorage model — scope-aware folder helpers used by the institution and\n * personal cloud storage UI. Sits on top of the same Folder/File tables that\n * back class folders, but adds scope (school/personal) and ownership semantics.\n */\nimport type { FolderScope } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\n\nexport type CloudScope =\n | { kind: \"CLASS\"; classId: string }\n | { kind: \"SCHOOL_STORAGE\"; schoolId: string }\n | { kind: \"PERSONAL\"; userId: string };\n\nconst folderSelect = {\n id: true,\n name: true,\n color: true,\n scope: true,\n classId: true,\n schoolStorageId: true,\n personalOwnerId: true,\n parentFolderId: true,\n} as const;\n\nexport async function findOrCreateScopeRoot(scope: CloudScope) {\n switch (scope.kind) {\n case \"CLASS\": {\n let root = await prisma.folder.findFirst({\n where: { classId: scope.classId, parentFolderId: null },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Class Files\",\n scope: \"CLASS\" as FolderScope,\n class: { connect: { id: scope.classId } },\n },\n });\n } else if (root.scope !== \"CLASS\") {\n root = await prisma.folder.update({\n where: { id: root.id },\n data: { scope: \"CLASS\" as FolderScope },\n });\n }\n return root;\n }\n case \"SCHOOL_STORAGE\": {\n let root = await prisma.folder.findFirst({\n where: { schoolStorageId: scope.schoolId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Cloud Storage\",\n scope: \"SCHOOL_STORAGE\" as FolderScope,\n schoolStorage: { connect: { id: scope.schoolId } },\n },\n });\n }\n return root;\n }\n case \"PERSONAL\": {\n let root = await prisma.folder.findFirst({\n where: { personalOwnerId: scope.userId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"My Files\",\n scope: \"PERSONAL\" as FolderScope,\n personalOwner: { connect: { id: scope.userId } },\n },\n });\n }\n return root;\n }\n }\n}\n\n/**\n * Walk up the folder tree until we reach a root with a scope owner. Used to\n * permission-check sub-folders (which only carry a parentFolderId).\n */\nexport async function resolveFolderScope(\n folderId: string\n): Promise<CloudScope | null> {\n let currentId: string | null = folderId;\n let visits = 0;\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: folderSelect,\n });\n if (!folder) return null;\n if (folder.classId) return { kind: \"CLASS\", classId: folder.classId };\n if (folder.schoolStorageId)\n return { kind: \"SCHOOL_STORAGE\", schoolId: folder.schoolStorageId };\n if (folder.personalOwnerId)\n return { kind: \"PERSONAL\", userId: folder.personalOwnerId };\n if (!folder.parentFolderId) return null;\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return null;\n}\n\nconst fileSelect = {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadedAt: true,\n user: { select: { id: true, username: true } },\n} as const;\n\nconst childFolderSelect = {\n id: true,\n name: true,\n color: true,\n _count: { select: { files: true, childFolders: true } },\n} as const;\n\nexport function findFolderWithContents(folderId: string) {\n return prisma.folder.findFirst({\n where: { id: folderId },\n include: {\n files: {\n where: { uploadStatus: \"COMPLETED\" },\n select: fileSelect,\n orderBy: { uploadedAt: \"desc\" },\n },\n childFolders: {\n select: childFolderSelect,\n orderBy: { name: \"asc\" },\n },\n parentFolder: { select: { id: true, name: true } },\n },\n });\n}\n\nexport function createSubFolder(data: {\n name: string;\n parentFolderId: string;\n color?: string;\n}) {\n return prisma.folder.create({\n data: {\n name: data.name,\n color: data.color,\n parentFolder: { connect: { id: data.parentFolderId } },\n },\n select: folderSelect,\n });\n}\n\nexport function findFolderById(folderId: string) {\n return prisma.folder.findUnique({\n where: { id: folderId },\n select: folderSelect,\n });\n}\n\nexport function deleteFolderById(folderId: string) {\n return prisma.folder.delete({ where: { id: folderId } });\n}\n\nexport function renameFolder(folderId: string, name: string, color?: string) {\n return prisma.folder.update({\n where: { id: folderId },\n data: {\n name,\n ...(color ? { color } : {}),\n },\n select: folderSelect,\n });\n}\n\nexport async function listFolderBreadcrumbs(folderId: string) {\n const breadcrumbs: { id: string; name: string }[] = [];\n let currentId: string | null = folderId;\n let visits = 0;\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: { id: true, name: true, parentFolderId: true },\n });\n if (!folder) break;\n breadcrumbs.unshift({ id: folder.id, name: folder.name });\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return breadcrumbs;\n}\n"],"names":[],"mappings":"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","debug_id":"0be713b0-75ed-5799-8084-cfdbc770063e"}
1
+ {"version":3,"file":"cloudStorage.js","sources":["models/cloudStorage.ts"],"sourceRoot":"/","sourcesContent":["/**\n * CloudStorage model — scope-aware folder helpers used by the institution and\n * personal cloud storage UI. Sits on top of the same Folder/File tables that\n * back class folders, but adds scope (school/personal) and ownership semantics.\n */\nimport type { FolderScope } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\n\nexport type CloudScope =\n | { kind: \"CLASS\"; classId: string }\n | { kind: \"SCHOOL_STORAGE\"; schoolId: string }\n | { kind: \"PERSONAL\"; userId: string };\n\nconst folderSelect = {\n id: true,\n name: true,\n color: true,\n scope: true,\n classId: true,\n schoolStorageId: true,\n personalOwnerId: true,\n parentFolderId: true,\n} as const;\n\nexport async function findOrCreateScopeRoot(scope: CloudScope) {\n switch (scope.kind) {\n case \"CLASS\": {\n let root = await prisma.folder.findFirst({\n where: { classId: scope.classId, parentFolderId: null },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Class Files\",\n scope: \"CLASS\" as FolderScope,\n class: { connect: { id: scope.classId } },\n },\n });\n } else if (root.scope !== \"CLASS\") {\n root = await prisma.folder.update({\n where: { id: root.id },\n data: { scope: \"CLASS\" as FolderScope },\n });\n }\n return root;\n }\n case \"SCHOOL_STORAGE\": {\n let root = await prisma.folder.findFirst({\n where: { schoolStorageId: scope.schoolId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"Cloud Storage\",\n scope: \"SCHOOL_STORAGE\" as FolderScope,\n schoolStorage: { connect: { id: scope.schoolId } },\n },\n });\n }\n return root;\n }\n case \"PERSONAL\": {\n let root = await prisma.folder.findFirst({\n where: { personalOwnerId: scope.userId },\n });\n if (!root) {\n root = await prisma.folder.create({\n data: {\n name: \"My Files\",\n scope: \"PERSONAL\" as FolderScope,\n personalOwner: { connect: { id: scope.userId } },\n },\n });\n }\n return root;\n }\n }\n}\n\n/**\n * Walk up the folder tree until we reach a root with a scope owner. Used to\n * permission-check sub-folders (which only carry a parentFolderId).\n */\nexport async function resolveFolderScope(\n folderId: string\n): Promise<CloudScope | null> {\n let currentId: string | null = folderId;\n let visits = 0;\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: folderSelect,\n });\n if (!folder) return null;\n if (folder.classId) return { kind: \"CLASS\", classId: folder.classId };\n if (folder.schoolStorageId)\n return { kind: \"SCHOOL_STORAGE\", schoolId: folder.schoolStorageId };\n if (folder.personalOwnerId)\n return { kind: \"PERSONAL\", userId: folder.personalOwnerId };\n if (!folder.parentFolderId) return null;\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return null;\n}\n\nconst fileSelect = {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadedAt: true,\n user: { select: { id: true, username: true } },\n} as const;\n\nconst childFolderSelect = {\n id: true,\n name: true,\n color: true,\n _count: { select: { files: true, childFolders: true } },\n} as const;\n\nexport function findFolderWithContents(folderId: string) {\n return prisma.folder.findFirst({\n where: { id: folderId },\n include: {\n files: {\n where: { uploadStatus: \"COMPLETED\" },\n select: fileSelect,\n orderBy: { uploadedAt: \"desc\" },\n },\n childFolders: {\n select: childFolderSelect,\n orderBy: { name: \"asc\" },\n },\n parentFolder: { select: { id: true, name: true } },\n },\n });\n}\n\nexport function createSubFolder(data: {\n name: string;\n parentFolderId: string;\n color?: string;\n}) {\n return prisma.folder.create({\n data: {\n name: data.name,\n color: data.color,\n parentFolder: { connect: { id: data.parentFolderId } },\n },\n select: folderSelect,\n });\n}\n\nexport function findFolderById(folderId: string) {\n return prisma.folder.findUnique({\n where: { id: folderId },\n select: folderSelect,\n });\n}\n\nexport function deleteFolderById(folderId: string) {\n return prisma.folder.delete({ where: { id: folderId } });\n}\n\nexport function renameFolder(folderId: string, name: string, color?: string) {\n return prisma.folder.update({\n where: { id: folderId },\n data: {\n name,\n ...(color ? { color } : {}),\n },\n select: folderSelect,\n });\n}\n\n/**\n * Build the breadcrumb chain for a folder.\n *\n * Without `viewerId`: walks all the way up to the scope root (owner-style\n * view).\n *\n * With `viewerId`: walks up but stops at the highest folder the viewer is\n * actually granted on, OR at the scope root if the viewer is a scope member.\n * This is the Drive-style \"Shared with me\" behavior — a grantee never sees\n * ancestor folders they weren't shared on. The result for a grantee browsing\n * `Owner/PrivateA/SharedB/C` (granted on B) reads `SharedB / C` — the ancestor\n * `PrivateA` is correctly hidden.\n */\nexport async function listFolderBreadcrumbs(\n folderId: string,\n viewerId?: string\n) {\n const breadcrumbs: { id: string; name: string }[] = [];\n let currentId: string | null = folderId;\n let visits = 0;\n\n let viewerGrantedFolderIds: Set<string> | null = null;\n let viewerScopeMember = false;\n\n if (viewerId) {\n const grants = await prisma.permission.findMany({\n where: {\n OR: [\n { principalType: \"USER\", userId: viewerId },\n {\n principalType: \"TAG\",\n tag: { users: { some: { id: viewerId } } },\n },\n {\n principalType: \"CLASS\",\n class: {\n OR: [\n { teachers: { some: { id: viewerId } } },\n { students: { some: { id: viewerId } } },\n ],\n },\n },\n {\n principalType: \"CLASS_TEACHERS\",\n class: { teachers: { some: { id: viewerId } } },\n },\n {\n principalType: \"CLASS_STUDENTS\",\n class: { students: { some: { id: viewerId } } },\n },\n {\n principalType: \"DEPARTMENT\",\n department: { members: { some: { id: viewerId } } },\n },\n {\n principalType: \"SCHOOL\",\n school: {\n memberships: { some: { userId: viewerId, status: \"ACTIVE\" } },\n },\n },\n ],\n },\n select: { folderId: true },\n });\n viewerGrantedFolderIds = new Set(\n grants.map((g) => g.folderId).filter((x): x is string => Boolean(x))\n );\n\n // Determine if the viewer is a member of the *target* folder's scope —\n // if so, breadcrumbs go all the way to the scope root as normal.\n const scope = await resolveFolderScope(folderId);\n if (scope) {\n switch (scope.kind) {\n case \"CLASS\": {\n const c = await prisma.class.findFirst({\n where: {\n id: scope.classId,\n OR: [\n { teachers: { some: { id: viewerId } } },\n { students: { some: { id: viewerId } } },\n ],\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(c);\n if (!viewerScopeMember && scope.classId) {\n const klass = await prisma.class.findUnique({\n where: { id: scope.classId },\n select: { schoolId: true },\n });\n if (klass?.schoolId) {\n const m = await prisma.schoolMembership.findFirst({\n where: {\n schoolId: klass.schoolId,\n userId: viewerId,\n role: \"ADMIN\",\n status: \"ACTIVE\",\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(m);\n }\n }\n break;\n }\n case \"SCHOOL_STORAGE\": {\n const m = await prisma.schoolMembership.findFirst({\n where: {\n schoolId: scope.schoolId,\n userId: viewerId,\n status: \"ACTIVE\",\n },\n select: { id: true },\n });\n viewerScopeMember = Boolean(m);\n break;\n }\n case \"PERSONAL\":\n viewerScopeMember = scope.userId === viewerId;\n break;\n }\n }\n }\n\n while (currentId && visits < 64) {\n const folder = await prisma.folder.findUnique({\n where: { id: currentId },\n select: { id: true, name: true, parentFolderId: true },\n });\n if (!folder) break;\n breadcrumbs.unshift({ id: folder.id, name: folder.name });\n\n // Viewer-aware truncation: when the viewer is *not* a scope member but\n // is a grantee, stop at the highest granted ancestor (we walk up one\n // more step into the granted folder, then stop there).\n if (\n viewerId &&\n !viewerScopeMember &&\n viewerGrantedFolderIds &&\n viewerGrantedFolderIds.has(folder.id)\n ) {\n break;\n }\n\n currentId = folder.parentFolderId;\n visits += 1;\n }\n return breadcrumbs;\n}\n"],"names":[],"mappings":"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","debug_id":"26810bf7-c72f-5655-8994-96075d84b6fe"}
@@ -255,8 +255,8 @@ export declare function findAssignmentWithClass(assignmentId: string): import(".
255
255
  type: import(".prisma/client").$Enums.AssignmentType;
256
256
  id: string;
257
257
  createdAt: Date | null;
258
- modifiedAt: Date | null;
259
258
  classId: string;
259
+ modifiedAt: Date | null;
260
260
  title: string;
261
261
  instructions: string;
262
262
  dueDate: Date;
@@ -302,8 +302,8 @@ export declare function attachAssignmentToEvent(assignmentId: string, eventId: s
302
302
  type: import(".prisma/client").$Enums.AssignmentType;
303
303
  id: string;
304
304
  createdAt: Date | null;
305
- modifiedAt: Date | null;
306
305
  classId: string;
306
+ modifiedAt: Date | null;
307
307
  title: string;
308
308
  instructions: string;
309
309
  dueDate: Date;
@@ -349,8 +349,8 @@ export declare function detachAssignmentFromEvent(assignmentId: string): import(
349
349
  type: import(".prisma/client").$Enums.AssignmentType;
350
350
  id: string;
351
351
  createdAt: Date | null;
352
- modifiedAt: Date | null;
353
352
  classId: string;
353
+ modifiedAt: Date | null;
354
354
  title: string;
355
355
  instructions: string;
356
356
  dueDate: Date;
@@ -3,9 +3,6 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
3
3
  assignment: ({
4
4
  class: {
5
5
  school: ({
6
- users: {
7
- id: string;
8
- }[];
9
6
  memberships: {
10
7
  userId: string;
11
8
  }[];
@@ -48,8 +45,8 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
48
45
  type: import(".prisma/client").$Enums.AssignmentType;
49
46
  id: string;
50
47
  createdAt: Date | null;
51
- modifiedAt: Date | null;
52
48
  classId: string;
49
+ modifiedAt: Date | null;
53
50
  title: string;
54
51
  instructions: string;
55
52
  dueDate: Date;
@@ -74,9 +71,6 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
74
71
  assignment: {
75
72
  class: {
76
73
  school: ({
77
- users: {
78
- id: string;
79
- }[];
80
74
  memberships: {
81
75
  userId: string;
82
76
  }[];
@@ -97,6 +91,14 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
97
91
  verified: boolean;
98
92
  schoolId: string | null;
99
93
  }[];
94
+ students: {
95
+ id: string;
96
+ username: string;
97
+ email: string;
98
+ password: string;
99
+ verified: boolean;
100
+ schoolId: string | null;
101
+ }[];
100
102
  } & {
101
103
  name: string;
102
104
  id: string;
@@ -111,8 +113,8 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
111
113
  type: import(".prisma/client").$Enums.AssignmentType;
112
114
  id: string;
113
115
  createdAt: Date | null;
114
- modifiedAt: Date | null;
115
116
  classId: string;
117
+ modifiedAt: Date | null;
116
118
  title: string;
117
119
  instructions: string;
118
120
  dueDate: Date;
@@ -162,9 +164,6 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
162
164
  assignment: {
163
165
  class: {
164
166
  school: ({
165
- users: {
166
- id: string;
167
- }[];
168
167
  memberships: {
169
168
  userId: string;
170
169
  }[];
@@ -185,6 +184,14 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
185
184
  verified: boolean;
186
185
  schoolId: string | null;
187
186
  }[];
187
+ students: {
188
+ id: string;
189
+ username: string;
190
+ email: string;
191
+ password: string;
192
+ verified: boolean;
193
+ schoolId: string | null;
194
+ }[];
188
195
  } & {
189
196
  name: string;
190
197
  id: string;
@@ -199,8 +206,8 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
199
206
  type: import(".prisma/client").$Enums.AssignmentType;
200
207
  id: string;
201
208
  createdAt: Date | null;
202
- modifiedAt: Date | null;
203
209
  classId: string;
210
+ modifiedAt: Date | null;
204
211
  title: string;
205
212
  instructions: string;
206
213
  dueDate: Date;
@@ -249,9 +256,6 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
249
256
  folder: ({
250
257
  class: ({
251
258
  school: ({
252
- users: {
253
- id: string;
254
- }[];
255
259
  memberships: {
256
260
  userId: string;
257
261
  }[];
@@ -303,9 +307,6 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
303
307
  announcement: ({
304
308
  class: {
305
309
  school: ({
306
- users: {
307
- id: string;
308
- }[];
309
310
  memberships: {
310
311
  userId: string;
311
312
  }[];
@@ -347,8 +348,8 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
347
348
  } & {
348
349
  id: string;
349
350
  createdAt: Date;
350
- modifiedAt: Date | null;
351
351
  classId: string;
352
+ modifiedAt: Date | null;
352
353
  teacherId: string;
353
354
  remarks: string;
354
355
  }) | null;
@@ -358,6 +359,7 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
358
359
  name: string;
359
360
  id: string;
360
361
  userId: string | null;
362
+ folderId: string | null;
361
363
  size: number | null;
362
364
  uploadedAt: Date | null;
363
365
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -374,7 +376,6 @@ export declare function findFileById(fileId: string): import(".prisma/client").P
374
376
  submissionId: string | null;
375
377
  annotationId: string | null;
376
378
  classDraftId: string | null;
377
- folderId: string | null;
378
379
  conversationId: string | null;
379
380
  messageId: string | null;
380
381
  announcementId: string | null;
@@ -415,6 +416,7 @@ export declare function findFileWithFolder(fileId: string): import(".prisma/clie
415
416
  name: string;
416
417
  id: string;
417
418
  userId: string | null;
419
+ folderId: string | null;
418
420
  size: number | null;
419
421
  uploadedAt: Date | null;
420
422
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -431,7 +433,6 @@ export declare function findFileWithFolder(fileId: string): import(".prisma/clie
431
433
  submissionId: string | null;
432
434
  annotationId: string | null;
433
435
  classDraftId: string | null;
434
- folderId: string | null;
435
436
  conversationId: string | null;
436
437
  messageId: string | null;
437
438
  announcementId: string | null;
@@ -451,6 +452,7 @@ export declare function findFileWithFolderAndThumbnail(fileId: string): import("
451
452
  name: string;
452
453
  id: string;
453
454
  userId: string | null;
455
+ folderId: string | null;
454
456
  size: number | null;
455
457
  uploadedAt: Date | null;
456
458
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -467,7 +469,6 @@ export declare function findFileWithFolderAndThumbnail(fileId: string): import("
467
469
  submissionId: string | null;
468
470
  annotationId: string | null;
469
471
  classDraftId: string | null;
470
- folderId: string | null;
471
472
  conversationId: string | null;
472
473
  messageId: string | null;
473
474
  announcementId: string | null;
@@ -500,6 +501,7 @@ export declare function findFileWithFolderAndThumbnail(fileId: string): import("
500
501
  name: string;
501
502
  id: string;
502
503
  userId: string | null;
504
+ folderId: string | null;
503
505
  size: number | null;
504
506
  uploadedAt: Date | null;
505
507
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -516,7 +518,6 @@ export declare function findFileWithFolderAndThumbnail(fileId: string): import("
516
518
  submissionId: string | null;
517
519
  annotationId: string | null;
518
520
  classDraftId: string | null;
519
- folderId: string | null;
520
521
  conversationId: string | null;
521
522
  messageId: string | null;
522
523
  announcementId: string | null;
@@ -568,6 +569,7 @@ export declare function updateFileFolder(fileId: string, folderId: string): impo
568
569
  name: string;
569
570
  id: string;
570
571
  userId: string | null;
572
+ folderId: string | null;
571
573
  size: number | null;
572
574
  uploadedAt: Date | null;
573
575
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -584,7 +586,6 @@ export declare function updateFileFolder(fileId: string, folderId: string): impo
584
586
  submissionId: string | null;
585
587
  annotationId: string | null;
586
588
  classDraftId: string | null;
587
- folderId: string | null;
588
589
  conversationId: string | null;
589
590
  messageId: string | null;
590
591
  announcementId: string | null;
@@ -608,6 +609,7 @@ export declare function updateFileName(fileId: string, name: string): import(".p
608
609
  name: string;
609
610
  id: string;
610
611
  userId: string | null;
612
+ folderId: string | null;
611
613
  size: number | null;
612
614
  uploadedAt: Date | null;
613
615
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -624,7 +626,6 @@ export declare function updateFileName(fileId: string, name: string): import(".p
624
626
  submissionId: string | null;
625
627
  annotationId: string | null;
626
628
  classDraftId: string | null;
627
- folderId: string | null;
628
629
  conversationId: string | null;
629
630
  messageId: string | null;
630
631
  announcementId: string | null;
@@ -643,6 +644,7 @@ export declare function deleteFileById(fileId: string): import(".prisma/client")
643
644
  name: string;
644
645
  id: string;
645
646
  userId: string | null;
647
+ folderId: string | null;
646
648
  size: number | null;
647
649
  uploadedAt: Date | null;
648
650
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
@@ -659,7 +661,6 @@ export declare function deleteFileById(fileId: string): import(".prisma/client")
659
661
  submissionId: string | null;
660
662
  annotationId: string | null;
661
663
  classDraftId: string | null;
662
- folderId: string | null;
663
664
  conversationId: string | null;
664
665
  messageId: string | null;
665
666
  announcementId: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"/","sources":["models/file.ts"],"names":[],"mappings":"AAwBA,0GAA0G;AAC1G,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmF1C;AAED,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOhD;AAED,+CAA+C;AAC/C,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQ5D;AAED,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;GAK9C;AAED,6BAA6B;AAC7B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQhE;AAED,qBAAqB;AACrB,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQ1D;AAED,4BAA4B;AAC5B,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAI5C"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"/","sources":["models/file.ts"],"names":[],"mappings":"AAqBA,0GAA0G;AAC1G,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmD1C;AAED,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOhD;AAED,+CAA+C;AAC/C,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQ5D;AAED,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;GAK9C;AAED,6BAA6B;AAC7B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQhE;AAED,qBAAqB;AACrB,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAQ1D;AAED,4BAA4B;AAC5B,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAI5C"}
@@ -2,16 +2,13 @@
2
2
  * File model – file lookup by ID, folder, assignment, submission. Permission checks.
3
3
  */
4
4
 
5
- !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]="fab4a93e-e5c9-5485-b331-b3e1d8dd0d22")}catch(e){}}();
5
+ !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]="d3a26ae1-962d-534f-8e7b-4f9095d9b91d")}catch(e){}}();
6
6
  import { prisma } from "../lib/prisma.js";
7
7
  const classFileAccessInclude = {
8
8
  students: true,
9
9
  teachers: true,
10
10
  school: {
11
11
  include: {
12
- users: {
13
- select: { id: true },
14
- },
15
12
  memberships: {
16
13
  where: {
17
14
  role: "ADMIN",
@@ -40,23 +37,7 @@ export function findFileById(fileId) {
40
37
  assignment: {
41
38
  include: {
42
39
  class: {
43
- include: {
44
- teachers: true,
45
- school: {
46
- include: {
47
- users: {
48
- select: { id: true },
49
- },
50
- memberships: {
51
- where: {
52
- role: "ADMIN",
53
- status: "ACTIVE",
54
- },
55
- select: { userId: true },
56
- },
57
- },
58
- },
59
- },
40
+ include: classFileAccessInclude,
60
41
  },
61
42
  },
62
43
  },
@@ -68,23 +49,7 @@ export function findFileById(fileId) {
68
49
  assignment: {
69
50
  include: {
70
51
  class: {
71
- include: {
72
- teachers: true,
73
- school: {
74
- include: {
75
- users: {
76
- select: { id: true },
77
- },
78
- memberships: {
79
- where: {
80
- role: "ADMIN",
81
- status: "ACTIVE",
82
- },
83
- select: { userId: true },
84
- },
85
- },
86
- },
87
- },
52
+ include: classFileAccessInclude,
88
53
  },
89
54
  },
90
55
  },
@@ -160,4 +125,4 @@ export function deleteFileById(fileId) {
160
125
  });
161
126
  }
162
127
  //# sourceMappingURL=file.js.map
163
- //# debugId=fab4a93e-e5c9-5485-b331-b3e1d8dd0d22
128
+ //# debugId=d3a26ae1-962d-534f-8e7b-4f9095d9b91d
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","sources":["models/file.ts"],"sourceRoot":"/","sourcesContent":["/**\n * File model – file lookup by ID, folder, assignment, submission. Permission checks.\n */\nimport { prisma } from \"../lib/prisma.js\";\n\nconst classFileAccessInclude = {\n students: true,\n teachers: true,\n school: {\n include: {\n users: {\n select: { id: true },\n },\n memberships: {\n where: {\n role: \"ADMIN\" as const,\n status: \"ACTIVE\" as const,\n },\n select: { userId: true },\n },\n },\n },\n} as const;\n\n/** @returns File with assignment, submission, annotations, folder, announcement for permission checks. */\nexport function findFileById(fileId: string) {\n return prisma.file.findUnique({\n where: { id: fileId },\n include: {\n assignment: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n submission: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: {\n teachers: true,\n school: {\n include: {\n users: {\n select: { id: true },\n },\n memberships: {\n where: {\n role: \"ADMIN\" as const,\n status: \"ACTIVE\" as const,\n },\n select: { userId: true },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n annotations: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: {\n teachers: true,\n school: {\n include: {\n users: {\n select: { id: true },\n },\n memberships: {\n where: {\n role: \"ADMIN\" as const,\n status: \"ACTIVE\" as const,\n },\n select: { userId: true },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n folder: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n announcement: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n },\n });\n}\n\n/** @returns File with folder and class. */\nexport function findFileWithFolder(fileId: string) {\n return prisma.file.findUnique({\n where: { id: fileId },\n include: {\n folder: { include: { class: true } },\n },\n });\n}\n\n/** @returns File with folder and thumbnail. */\nexport function findFileWithFolderAndThumbnail(fileId: string) {\n return prisma.file.findUnique({\n where: { id: fileId },\n include: {\n folder: { include: { class: true } },\n thumbnail: true,\n },\n });\n}\n\n/** @returns Folder by ID with class. */\nexport function findFolderById(folderId: string) {\n return prisma.folder.findUnique({\n where: { id: folderId },\n include: { class: true },\n });\n}\n\n/** Move file to a folder. */\nexport function updateFileFolder(fileId: string, folderId: string) {\n return prisma.file.update({\n where: { id: fileId },\n data: { folderId },\n include: {\n user: { select: { id: true, username: true } },\n },\n });\n}\n\n/** Rename a file. */\nexport function updateFileName(fileId: string, name: string) {\n return prisma.file.update({\n where: { id: fileId },\n data: { name },\n include: {\n user: { select: { id: true, username: true } },\n },\n });\n}\n\n/** Delete a file record. */\nexport function deleteFileById(fileId: string) {\n return prisma.file.delete({\n where: { id: fileId },\n });\n}\n"],"names":[],"mappings":"AAAA;;GAEG;;;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,sBAAsB,GAAG;IAC7B,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,MAAM,EAAE;QACN,OAAO,EAAE;YACP,KAAK,EAAE;gBACL,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;aACrB;YACD,WAAW,EAAE;gBACX,KAAK,EAAE;oBACL,IAAI,EAAE,OAAgB;oBACtB,MAAM,EAAE,QAAiB;iBAC1B;gBACD,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;aACzB;SACF;KACF;CACO,CAAC;AAEX,0GAA0G;AAC1G,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,OAAO,EAAE;YACP,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,OAAO,EAAE,sBAAsB;qBAChC;iBACF;aACF;YACD,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,OAAO,EAAE,IAAI;oBACb,UAAU,EAAE;wBACV,OAAO,EAAE;4BACP,KAAK,EAAE;gCACL,OAAO,EAAE;oCACP,QAAQ,EAAE,IAAI;oCACd,MAAM,EAAE;wCACN,OAAO,EAAE;4CACP,KAAK,EAAE;gDACL,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;6CACrB;4CACD,WAAW,EAAE;gDACX,KAAK,EAAE;oDACL,IAAI,EAAE,OAAgB;oDACtB,MAAM,EAAE,QAAiB;iDAC1B;gDACD,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;6CACzB;yCACF;qCACF;iCACF;6BACF;yBACF;qBACF;iBACF;aACF;YACD,WAAW,EAAE;gBACX,OAAO,EAAE;oBACP,OAAO,EAAE,IAAI;oBACb,UAAU,EAAE;wBACV,OAAO,EAAE;4BACP,KAAK,EAAE;gCACL,OAAO,EAAE;oCACP,QAAQ,EAAE,IAAI;oCACd,MAAM,EAAE;wCACN,OAAO,EAAE;4CACP,KAAK,EAAE;gDACL,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;6CACrB;4CACD,WAAW,EAAE;gDACX,KAAK,EAAE;oDACL,IAAI,EAAE,OAAgB;oDACtB,MAAM,EAAE,QAAiB;iDAC1B;gDACD,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;6CACzB;yCACF;qCACF;iCACF;6BACF;yBACF;qBACF;iBACF;aACF;YACD,MAAM,EAAE;gBACN,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,OAAO,EAAE,sBAAsB;qBAChC;iBACF;aACF;YACD,YAAY,EAAE;gBACZ,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,OAAO,EAAE,sBAAsB;qBAChC;iBACF;aACF;SACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,OAAO,EAAE;YACP,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;SACrC;KACF,CAAC,CAAC;AACL,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,8BAA8B,CAAC,MAAc;IAC3D,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,OAAO,EAAE;YACP,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACpC,SAAS,EAAE,IAAI;SAChB;KACF,CAAC,CAAC;AACL,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;QACvB,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;KACzB,CAAC,CAAC;AACL,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,QAAgB;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,IAAI,EAAE,EAAE,QAAQ,EAAE;QAClB,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;SAC/C;KACF,CAAC,CAAC;AACL,CAAC;AAED,qBAAqB;AACrB,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,IAAY;IACzD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,IAAI,EAAE,EAAE,IAAI,EAAE;QACd,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;SAC/C;KACF,CAAC,CAAC;AACL,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;KACtB,CAAC,CAAC;AACL,CAAC","debug_id":"fab4a93e-e5c9-5485-b331-b3e1d8dd0d22"}
1
+ {"version":3,"file":"file.js","sources":["models/file.ts"],"sourceRoot":"/","sourcesContent":["/**\n * File model – file lookup by ID, folder, assignment, submission. Permission checks.\n */\nimport { prisma } from \"../lib/prisma.js\";\n\nconst classFileAccessInclude = {\n students: true,\n teachers: true,\n school: {\n include: {\n memberships: {\n where: {\n role: \"ADMIN\" as const,\n status: \"ACTIVE\" as const,\n },\n select: { userId: true },\n },\n },\n },\n} as const;\n\n/** @returns File with assignment, submission, annotations, folder, announcement for permission checks. */\nexport function findFileById(fileId: string) {\n return prisma.file.findUnique({\n where: { id: fileId },\n include: {\n assignment: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n submission: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n },\n },\n annotations: {\n include: {\n student: true,\n assignment: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n },\n },\n folder: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n announcement: {\n include: {\n class: {\n include: classFileAccessInclude,\n },\n },\n },\n },\n });\n}\n\n/** @returns File with folder and class. */\nexport function findFileWithFolder(fileId: string) {\n return prisma.file.findUnique({\n where: { id: fileId },\n include: {\n folder: { include: { class: true } },\n },\n });\n}\n\n/** @returns File with folder and thumbnail. */\nexport function findFileWithFolderAndThumbnail(fileId: string) {\n return prisma.file.findUnique({\n where: { id: fileId },\n include: {\n folder: { include: { class: true } },\n thumbnail: true,\n },\n });\n}\n\n/** @returns Folder by ID with class. */\nexport function findFolderById(folderId: string) {\n return prisma.folder.findUnique({\n where: { id: folderId },\n include: { class: true },\n });\n}\n\n/** Move file to a folder. */\nexport function updateFileFolder(fileId: string, folderId: string) {\n return prisma.file.update({\n where: { id: fileId },\n data: { folderId },\n include: {\n user: { select: { id: true, username: true } },\n },\n });\n}\n\n/** Rename a file. */\nexport function updateFileName(fileId: string, name: string) {\n return prisma.file.update({\n where: { id: fileId },\n data: { name },\n include: {\n user: { select: { id: true, username: true } },\n },\n });\n}\n\n/** Delete a file record. */\nexport function deleteFileById(fileId: string) {\n return prisma.file.delete({\n where: { id: fileId },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"d3a26ae1-962d-534f-8e7b-4f9095d9b91d"}
@@ -81,8 +81,8 @@ export declare function findLabChatByMember(labChatId: string, userId: string):
81
81
  id: string;
82
82
  createdAt: Date;
83
83
  updatedAt: Date;
84
- conversationId: string;
85
84
  classId: string;
85
+ conversationId: string;
86
86
  title: string;
87
87
  createdById: string;
88
88
  }) | null, null, import("@prisma/client/runtime/library.js").DefaultArgs, {
@@ -102,8 +102,8 @@ export declare function findLabChatByTeacher(labChatId: string, userId: string):
102
102
  id: string;
103
103
  createdAt: Date;
104
104
  updatedAt: Date;
105
- conversationId: string;
106
105
  classId: string;
106
+ conversationId: string;
107
107
  title: string;
108
108
  createdById: string;
109
109
  }) | null, null, import("@prisma/client/runtime/library.js").DefaultArgs, {
@@ -162,8 +162,8 @@ export declare function findLabChatById(labChatId: string): import(".prisma/clie
162
162
  id: string;
163
163
  createdAt: Date;
164
164
  updatedAt: Date;
165
- conversationId: string;
166
165
  classId: string;
166
+ conversationId: string;
167
167
  title: string;
168
168
  createdById: string;
169
169
  }) | null, null, import("@prisma/client/runtime/library.js").DefaultArgs, {
@@ -234,8 +234,8 @@ export declare function findLabChatsByClass(classId: string): import(".prisma/cl
234
234
  id: string;
235
235
  createdAt: Date;
236
236
  updatedAt: Date;
237
- conversationId: string;
238
237
  classId: string;
238
+ conversationId: string;
239
239
  title: string;
240
240
  createdById: string;
241
241
  })[]>;
@@ -249,8 +249,8 @@ export declare function findLabChatForPost(labChatId: string, userId: string): i
249
249
  id: string;
250
250
  createdAt: Date;
251
251
  updatedAt: Date;
252
- conversationId: string;
253
252
  classId: string;
253
+ conversationId: string;
254
254
  title: string;
255
255
  createdById: string;
256
256
  }) | null, null, import("@prisma/client/runtime/library.js").DefaultArgs, {
@@ -266,8 +266,8 @@ export declare function findLabChatForDelete(labChatId: string, userId: string):
266
266
  id: string;
267
267
  createdAt: Date;
268
268
  updatedAt: Date;
269
- conversationId: string;
270
269
  classId: string;
270
+ conversationId: string;
271
271
  title: string;
272
272
  createdById: string;
273
273
  } | null, null, import("@prisma/client/runtime/library.js").DefaultArgs, {
@@ -78,8 +78,8 @@ export declare function updateAssignmentOrder(id: string, order: number): import
78
78
  type: import(".prisma/client").$Enums.AssignmentType;
79
79
  id: string;
80
80
  createdAt: Date | null;
81
- modifiedAt: Date | null;
82
81
  classId: string;
82
+ modifiedAt: Date | null;
83
83
  title: string;
84
84
  instructions: string;
85
85
  dueDate: Date;