@studious-lms/server 1.4.5 → 1.4.7

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 (167) hide show
  1. package/dist/lib/config/cors.d.ts.map +1 -1
  2. package/dist/lib/config/cors.js +8 -3
  3. package/dist/lib/config/cors.js.map +1 -1
  4. package/dist/models/access.d.ts +112 -0
  5. package/dist/models/access.d.ts.map +1 -0
  6. package/dist/models/access.js +49 -0
  7. package/dist/models/access.js.map +1 -0
  8. package/dist/models/announcement.d.ts +17 -2
  9. package/dist/models/announcement.d.ts.map +1 -1
  10. package/dist/models/announcement.js +21 -4
  11. package/dist/models/announcement.js.map +1 -1
  12. package/dist/models/assignment.d.ts +37 -37
  13. package/dist/models/assignment.d.ts.map +1 -1
  14. package/dist/models/assignment.js +15 -15
  15. package/dist/models/assignment.js.map +1 -1
  16. package/dist/models/attendance.d.ts +1 -1
  17. package/dist/models/attendance.d.ts.map +1 -1
  18. package/dist/models/attendance.js +6 -5
  19. package/dist/models/attendance.js.map +1 -1
  20. package/dist/models/auth.d.ts +5 -5
  21. package/dist/models/auth.d.ts.map +1 -1
  22. package/dist/models/auth.js +7 -11
  23. package/dist/models/auth.js.map +1 -1
  24. package/dist/models/class.d.ts +26 -26
  25. package/dist/models/class.d.ts.map +1 -1
  26. package/dist/models/class.js +7 -11
  27. package/dist/models/class.js.map +1 -1
  28. package/dist/models/comment.d.ts +233 -6
  29. package/dist/models/comment.d.ts.map +1 -1
  30. package/dist/models/comment.js +133 -3
  31. package/dist/models/comment.js.map +1 -1
  32. package/dist/models/conversation.d.ts +7 -7
  33. package/dist/models/event.d.ts +52 -3
  34. package/dist/models/event.d.ts.map +1 -1
  35. package/dist/models/event.js +56 -6
  36. package/dist/models/event.js.map +1 -1
  37. package/dist/models/file.d.ts +178 -103
  38. package/dist/models/file.d.ts.map +1 -1
  39. package/dist/models/file.js +54 -14
  40. package/dist/models/file.js.map +1 -1
  41. package/dist/models/folder.d.ts +22 -22
  42. package/dist/models/folder.d.ts.map +1 -1
  43. package/dist/models/folder.js +5 -4
  44. package/dist/models/folder.js.map +1 -1
  45. package/dist/models/labChat.d.ts +20 -20
  46. package/dist/models/labChat.d.ts.map +1 -1
  47. package/dist/models/labChat.js +8 -10
  48. package/dist/models/labChat.js.map +1 -1
  49. package/dist/models/marketing.d.ts +1 -1
  50. package/dist/models/message.d.ts +6 -6
  51. package/dist/models/newtonChat.d.ts +5 -5
  52. package/dist/models/school.d.ts +361 -0
  53. package/dist/models/school.d.ts.map +1 -0
  54. package/dist/models/school.js +314 -0
  55. package/dist/models/school.js.map +1 -0
  56. package/dist/models/section.d.ts +1 -1
  57. package/dist/models/section.d.ts.map +1 -1
  58. package/dist/models/section.js +5 -4
  59. package/dist/models/section.js.map +1 -1
  60. package/dist/models/user.d.ts +4 -4
  61. package/dist/models/worksheet.d.ts +67 -65
  62. package/dist/models/worksheet.d.ts.map +1 -1
  63. package/dist/models/worksheet.js +6 -14
  64. package/dist/models/worksheet.js.map +1 -1
  65. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  66. package/dist/pipelines/aiLabChat.js +4 -3
  67. package/dist/pipelines/aiLabChat.js.map +1 -1
  68. package/dist/pipelines/gradeWorksheet.d.ts +2 -2
  69. package/dist/pipelines/pdfToWorksheet.d.ts +1 -1
  70. package/dist/routers/_app.d.ts +2462 -1104
  71. package/dist/routers/_app.d.ts.map +1 -1
  72. package/dist/routers/_app.js +4 -2
  73. package/dist/routers/_app.js.map +1 -1
  74. package/dist/routers/announcement.d.ts +13 -5
  75. package/dist/routers/announcement.d.ts.map +1 -1
  76. package/dist/routers/announcement.js +25 -5
  77. package/dist/routers/announcement.js.map +1 -1
  78. package/dist/routers/assignment.d.ts +266 -36
  79. package/dist/routers/assignment.d.ts.map +1 -1
  80. package/dist/routers/assignment.js +221 -3
  81. package/dist/routers/assignment.js.map +1 -1
  82. package/dist/routers/class.d.ts +21 -20
  83. package/dist/routers/class.d.ts.map +1 -1
  84. package/dist/routers/class.js +3 -2
  85. package/dist/routers/class.js.map +1 -1
  86. package/dist/routers/comment.d.ts +40 -3
  87. package/dist/routers/comment.d.ts.map +1 -1
  88. package/dist/routers/comment.js +14 -5
  89. package/dist/routers/comment.js.map +1 -1
  90. package/dist/routers/conversation.d.ts +6 -6
  91. package/dist/routers/event.d.ts +21 -2
  92. package/dist/routers/event.d.ts.map +1 -1
  93. package/dist/routers/file.d.ts +2 -2
  94. package/dist/routers/folder.d.ts +24 -24
  95. package/dist/routers/labChat.d.ts +7 -7
  96. package/dist/routers/school.d.ts +384 -0
  97. package/dist/routers/school.d.ts.map +1 -0
  98. package/dist/routers/school.js +79 -0
  99. package/dist/routers/school.js.map +1 -0
  100. package/dist/routers/search.d.ts +1 -1
  101. package/dist/routers/worksheet.d.ts +23 -21
  102. package/dist/routers/worksheet.d.ts.map +1 -1
  103. package/dist/services/announcement.d.ts.map +1 -1
  104. package/dist/services/announcement.js +10 -6
  105. package/dist/services/announcement.js.map +1 -1
  106. package/dist/services/assignment.d.ts +36 -36
  107. package/dist/services/class.d.ts +20 -19
  108. package/dist/services/class.d.ts.map +1 -1
  109. package/dist/services/class.js +22 -3
  110. package/dist/services/class.js.map +1 -1
  111. package/dist/services/comment.d.ts +31 -5
  112. package/dist/services/comment.d.ts.map +1 -1
  113. package/dist/services/comment.js +102 -19
  114. package/dist/services/comment.js.map +1 -1
  115. package/dist/services/conversation.d.ts +6 -6
  116. package/dist/services/event.d.ts +21 -2
  117. package/dist/services/event.d.ts.map +1 -1
  118. package/dist/services/event.js +11 -7
  119. package/dist/services/event.js.map +1 -1
  120. package/dist/services/file.d.ts +2 -2
  121. package/dist/services/file.d.ts.map +1 -1
  122. package/dist/services/file.js +17 -8
  123. package/dist/services/file.js.map +1 -1
  124. package/dist/services/folder.d.ts +24 -24
  125. package/dist/services/labChat.d.ts +7 -7
  126. package/dist/services/school.d.ts +299 -0
  127. package/dist/services/school.d.ts.map +1 -0
  128. package/dist/services/school.js +225 -0
  129. package/dist/services/school.js.map +1 -0
  130. package/dist/services/worksheet.d.ts +23 -21
  131. package/dist/services/worksheet.d.ts.map +1 -1
  132. package/package.json +1 -1
  133. package/prisma/migrations/20260502230500_assignment_submission_comments/migration.sql +60 -0
  134. package/prisma/migrations/20260504071500_school_memberships/migration.sql +81 -0
  135. package/prisma/schema.prisma +75 -2
  136. package/src/lib/config/cors.ts +8 -1
  137. package/src/models/access.ts +48 -0
  138. package/src/models/announcement.ts +19 -2
  139. package/src/models/assignment.ts +17 -13
  140. package/src/models/attendance.ts +4 -3
  141. package/src/models/auth.ts +8 -9
  142. package/src/models/class.ts +8 -9
  143. package/src/models/comment.ts +137 -1
  144. package/src/models/event.ts +54 -4
  145. package/src/models/file.ts +53 -12
  146. package/src/models/folder.ts +3 -2
  147. package/src/models/labChat.ts +9 -8
  148. package/src/models/school.ts +361 -0
  149. package/src/models/section.ts +3 -2
  150. package/src/models/worksheet.ts +7 -12
  151. package/src/pipelines/aiLabChat.ts +2 -1
  152. package/src/routers/_app.ts +2 -0
  153. package/src/routers/announcement.ts +30 -4
  154. package/src/routers/assignment.ts +243 -2
  155. package/src/routers/class.ts +1 -0
  156. package/src/routers/comment.ts +19 -2
  157. package/src/routers/school.ts +122 -0
  158. package/src/services/announcement.ts +12 -4
  159. package/src/services/class.ts +25 -2
  160. package/src/services/comment.ts +122 -18
  161. package/src/services/event.ts +23 -6
  162. package/src/services/file.ts +20 -6
  163. package/src/services/school.ts +308 -0
  164. package/tests/lib/cors.test.ts +13 -1
  165. package/tests/routers/assignment.test.ts +68 -2
  166. package/tests/routers/comment.test.ts +42 -1
  167. package/vercel.json +8 -0
@@ -1 +1 @@
1
- {"version":3,"file":"comment.js","sources":["services/comment.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Comment service – get comments, replies, and reactions. Used for announcements and worksheets.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findCommentById,\n findRepliesByCommentId,\n createComment,\n findReactionByUserAndComment,\n upsertReaction,\n deleteReactionById,\n findCommentWithAnnouncement,\n getReactionCountsByCommentId,\n} from \"../models/comment.js\";\n\n/** Get a comment by ID. */\nexport async function getComment(id: string) {\n const comment = await findCommentById(id);\n if (!comment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Comment not found\",\n });\n }\n return comment;\n}\n\n/** Get all replies to a comment. */\nexport async function getReplies(commentId: string) {\n return findRepliesByCommentId(commentId);\n}\n\n/** Create a reply to an existing comment. */\nexport async function replyToComment(\n userId: string,\n parentCommentId: string,\n content: string\n) {\n return createComment({\n parentCommentId,\n content,\n authorId: userId,\n });\n}\n\n/** Add or update a reaction (emoji) on a comment. */\nexport async function addReaction(\n userId: string,\n commentId: string,\n type: string\n) {\n const comment = await findCommentById(commentId);\n if (!comment) {\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Unexpected error\",\n });\n }\n\n const reaction = await upsertReaction({ userId, commentId, type });\n return { reaction };\n}\n\n/** Remove user's reaction from a comment. */\nexport async function removeReaction(userId: string, commentId: string) {\n const reaction = await findReactionByUserAndComment(userId, commentId);\n if (!reaction) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Reaction not found\",\n });\n }\n await deleteReactionById(reaction.id);\n return { success: true };\n}\n\nexport async function getReactions(userId: string, commentId: string) {\n const comment = await findCommentWithAnnouncement(commentId);\n if (!comment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Comment not found\",\n });\n }\n\n const reactionCounts = await getReactionCountsByCommentId(commentId);\n const userReaction = await findReactionByUserAndComment(userId, commentId);\n\n const counts = {\n THUMBSUP: 0,\n CELEBRATE: 0,\n CARE: 0,\n HEART: 0,\n IDEA: 0,\n HAPPY: 0,\n };\n reactionCounts.forEach((item) => {\n counts[item.type as keyof typeof counts] = item._count.type;\n });\n\n return {\n counts,\n userReaction: userReaction?.type || null,\n total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),\n };\n}\n"],"names":[],"mappings":"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","debug_id":"381af507-0513-5413-ae9b-6bcb211761ee"}
1
+ {"version":3,"file":"comment.js","sources":["services/comment.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Comment service – get comments, replies, and reactions.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n findCommentById,\n findRepliesByCommentId,\n createComment,\n findReactionByUserAndComment,\n upsertReaction,\n deleteReactionById,\n findCommentWithContext,\n getReactionCountsByCommentId,\n} from \"../models/comment.js\";\n\nfunction getCommentClass(comment: any) {\n return (\n comment.announcement?.class ??\n comment.assignment?.class ??\n comment.submission?.assignment?.class ??\n comment.studentQuestionProgress?.studentWorksheetResponse?.submission?.assignment?.class ??\n comment.parentComment?.announcement?.class ??\n comment.parentComment?.assignment?.class ??\n comment.parentComment?.submission?.assignment?.class ??\n comment.parentComment?.studentQuestionProgress?.studentWorksheetResponse?.submission?.assignment?.class ??\n null\n );\n}\n\nfunction getPrivateStudentId(comment: any) {\n return (\n comment.submission?.studentId ??\n comment.studentQuestionProgress?.studentWorksheetResponse?.submission?.studentId ??\n comment.studentQuestionProgress?.studentId ??\n comment.parentComment?.submission?.studentId ??\n comment.parentComment?.studentQuestionProgress?.studentWorksheetResponse?.submission?.studentId ??\n comment.parentComment?.studentQuestionProgress?.studentId ??\n null\n );\n}\n\nfunction isClassTeacher(comment: any, userId: string) {\n const classData = getCommentClass(comment);\n return Boolean(\n classData?.teachers?.some((teacher: { id: string }) => teacher.id === userId) ||\n classData?.school?.memberships?.some(\n (admin: { userId: string }) => admin.userId === userId\n ) ||\n classData?.school?.users?.some((admin: { id: string }) => admin.id === userId)\n );\n}\n\nfunction isClassMember(comment: any, userId: string) {\n const classData = getCommentClass(comment);\n return (\n classData?.teachers?.some((teacher: { id: string }) => teacher.id === userId) ||\n classData?.students?.some((student: { id: string }) => student.id === userId) ||\n classData?.school?.memberships?.some((admin: { userId: string }) => admin.userId === userId) ||\n classData?.school?.users?.some((admin: { id: string }) => admin.id === userId) ||\n false\n );\n}\n\nfunction canReadComment(comment: any, userId: string) {\n const privateStudentId = getPrivateStudentId(comment);\n if (privateStudentId) {\n return privateStudentId === userId || isClassTeacher(comment, userId);\n }\n return isClassMember(comment, userId);\n}\n\nfunction canDeleteComment(comment: any, userId: string) {\n return comment.authorId === userId || isClassTeacher(comment, userId);\n}\n\nasync function requireCommentAccess(commentId: string, userId: string) {\n const comment = await findCommentWithContext(commentId);\n if (!comment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Comment not found\",\n });\n }\n if (!canReadComment(comment, userId)) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You do not have access to this comment\",\n });\n }\n return comment;\n}\n\n/** Get a comment by ID. */\nexport async function getComment(userId: string, id: string) {\n await requireCommentAccess(id, userId);\n const comment = await findCommentById(id);\n if (!comment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Comment not found\",\n });\n }\n return comment;\n}\n\n/** Get all replies to a comment. */\nexport async function getReplies(userId: string, commentId: string) {\n await requireCommentAccess(commentId, userId);\n return findRepliesByCommentId(commentId);\n}\n\n/** Create a reply to an existing comment. */\nexport async function replyToComment(\n userId: string,\n parentCommentId: string,\n content: string\n) {\n const parentComment = await requireCommentAccess(parentCommentId, userId);\n return createComment({\n parentCommentId,\n content,\n authorId: userId,\n announcementId: parentComment.announcementId,\n assignmentId: parentComment.assignmentId,\n submissionId: parentComment.submissionId,\n studentQuestionProgressId: parentComment.studentQuestionProgressId,\n });\n}\n\n/** Update a comment authored by the user. */\nexport async function updateComment(userId: string, id: string, content: string) {\n const comment = await requireCommentAccess(id, userId);\n if (comment.authorId !== userId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only the comment author can update this comment\",\n });\n }\n\n return prisma.comment.update({\n where: { id },\n data: { content },\n });\n}\n\n/** Delete a comment authored by the user or moderated by a class teacher. */\nexport async function deleteComment(userId: string, id: string) {\n const comment = await requireCommentAccess(id, userId);\n if (!canDeleteComment(comment, userId)) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only the comment author or class teachers can delete comments\",\n });\n }\n\n await prisma.comment.delete({ where: { id } });\n return { success: true };\n}\n\n/** Add or update a reaction (emoji) on a comment. */\nexport async function addReaction(\n userId: string,\n commentId: string,\n type: string\n) {\n await requireCommentAccess(commentId, userId);\n\n const reaction = await upsertReaction({ userId, commentId, type });\n return { reaction };\n}\n\n/** Remove user's reaction from a comment. */\nexport async function removeReaction(userId: string, commentId: string) {\n await requireCommentAccess(commentId, userId);\n const reaction = await findReactionByUserAndComment(userId, commentId);\n if (!reaction) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Reaction not found\",\n });\n }\n await deleteReactionById(reaction.id);\n return { success: true };\n}\n\nexport async function getReactions(userId: string, commentId: string) {\n await requireCommentAccess(commentId, userId);\n\n const reactionCounts = await getReactionCountsByCommentId(commentId);\n const userReaction = await findReactionByUserAndComment(userId, commentId);\n\n const counts = {\n THUMBSUP: 0,\n CELEBRATE: 0,\n CARE: 0,\n HEART: 0,\n IDEA: 0,\n HAPPY: 0,\n };\n reactionCounts.forEach((item) => {\n counts[item.type as keyof typeof counts] = item._count.type;\n });\n\n return {\n counts,\n userReaction: userReaction?.type || null,\n total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),\n };\n}\n"],"names":[],"mappings":"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","debug_id":"3b159ee4-0b57-5fbd-abfa-06234f7273dd"}
@@ -20,9 +20,9 @@ export declare function listConversations(userId: string): Promise<{
20
20
  };
21
21
  } & {
22
22
  id: string;
23
+ role: import(".prisma/client").$Enums.ConversationRole;
23
24
  userId: string;
24
25
  conversationId: string;
25
- role: import(".prisma/client").$Enums.ConversationRole;
26
26
  joinedAt: Date;
27
27
  hiddenAt: Date | null;
28
28
  lastViewedAt: Date | null;
@@ -40,8 +40,8 @@ export declare function listConversations(userId: string): Promise<{
40
40
  status: import(".prisma/client").$Enums.GenerationStatus | null;
41
41
  meta: import("@prisma/client/runtime/library.js").JsonValue | null;
42
42
  id: string;
43
- conversationId: string;
44
43
  createdAt: Date;
44
+ conversationId: string;
45
45
  content: string;
46
46
  senderId: string;
47
47
  };
@@ -64,9 +64,9 @@ export declare function createConversationRecord(userId: string, type: "DM" | "G
64
64
  };
65
65
  } & {
66
66
  id: string;
67
+ role: import(".prisma/client").$Enums.ConversationRole;
67
68
  userId: string;
68
69
  conversationId: string;
69
- role: import(".prisma/client").$Enums.ConversationRole;
70
70
  joinedAt: Date;
71
71
  hiddenAt: Date | null;
72
72
  lastViewedAt: Date | null;
@@ -92,9 +92,9 @@ export declare function getConversation(userId: string, conversationId: string):
92
92
  };
93
93
  } & {
94
94
  id: string;
95
+ role: import(".prisma/client").$Enums.ConversationRole;
95
96
  userId: string;
96
97
  conversationId: string;
97
- role: import(".prisma/client").$Enums.ConversationRole;
98
98
  joinedAt: Date;
99
99
  hiddenAt: Date | null;
100
100
  lastViewedAt: Date | null;
@@ -120,9 +120,9 @@ export declare function addMemberToConversation(userId: string, conversationId:
120
120
  };
121
121
  } & {
122
122
  id: string;
123
+ role: import(".prisma/client").$Enums.ConversationRole;
123
124
  userId: string;
124
125
  conversationId: string;
125
- role: import(".prisma/client").$Enums.ConversationRole;
126
126
  joinedAt: Date;
127
127
  hiddenAt: Date | null;
128
128
  lastViewedAt: Date | null;
@@ -148,9 +148,9 @@ export declare function removeMemberFromConversation(userId: string, conversatio
148
148
  };
149
149
  } & {
150
150
  id: string;
151
+ role: import(".prisma/client").$Enums.ConversationRole;
151
152
  userId: string;
152
153
  conversationId: string;
153
- role: import(".prisma/client").$Enums.ConversationRole;
154
154
  joinedAt: Date;
155
155
  hiddenAt: Date | null;
156
156
  lastViewedAt: Date | null;
@@ -10,7 +10,26 @@ export declare function getEvent(userId: string, id: string): Promise<{
10
10
  role: import(".prisma/client").$Enums.UserRole;
11
11
  schoolId: string | null;
12
12
  } | null;
13
- class: {
13
+ class: ({
14
+ school: ({
15
+ users: {
16
+ id: string;
17
+ }[];
18
+ memberships: {
19
+ userId: string;
20
+ }[];
21
+ } & {
22
+ name: string;
23
+ id: string;
24
+ logoId: string | null;
25
+ subdomain: string | null;
26
+ createdAt: Date;
27
+ updatedAt: Date;
28
+ }) | null;
29
+ teachers: {
30
+ id: string;
31
+ }[];
32
+ } & {
14
33
  name: string;
15
34
  id: string;
16
35
  schoolId: string | null;
@@ -18,7 +37,7 @@ export declare function getEvent(userId: string, id: string): Promise<{
18
37
  color: string | null;
19
38
  section: string;
20
39
  syllabus: string | null;
21
- } | null;
40
+ }) | null;
22
41
  assignmentsAttached: {
23
42
  type: import(".prisma/client").$Enums.AssignmentType;
24
43
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"event.d.ts","sourceRoot":"/","sources":["services/event.ts"],"names":[],"mappings":"AAmBA,6DAA6D;AAC7D,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAexD;AAED,wCAAwC;AACxC,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;;;;;;;;;;GAwBF;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;;;;;;;;;;;;GA2BF;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;GAiBjE;AAUD,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCrB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkBrB;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;GAkBhB"}
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"/","sources":["services/event.ts"],"names":[],"mappings":"AAmBA,6DAA6D;AAC7D,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAexD;AAED,wCAAwC;AACxC,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;;;;;;;;;;GAwBF;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;;;;;;;;;;;;GA2BF;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;GAiBjE;AAuBD,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CrB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkBrB;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;GAkBhB"}
@@ -3,7 +3,7 @@
3
3
  * Handles assignment attachment/detachment.
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]="04996008-f63b-58af-ad46-976c9d537646")}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]="b9df3493-e672-5d24-b6f8-26b10b12a289")}catch(e){}}();
7
7
  import { TRPCError } from "@trpc/server";
8
8
  import { parseISO } from "date-fns";
9
9
  import { findEventById, findClassWithTeachers, createEvent, updateEvent, deleteEventById, findEventWithClass, findAssignmentWithClass, attachAssignmentToEvent, detachAssignmentFromEvent, findAvailableAssignmentsForClass, } from "../models/event.js";
@@ -16,7 +16,7 @@ export async function getEvent(userId, id) {
16
16
  message: "Event not found",
17
17
  });
18
18
  }
19
- if (event.userId !== userId) {
19
+ if (!canModifyEvent(event, userId)) {
20
20
  throw new TRPCError({
21
21
  code: "UNAUTHORIZED",
22
22
  message: "You are not authorized to view this event",
@@ -55,7 +55,7 @@ export async function updateEventRecord(userId, id, data) {
55
55
  message: "Event not found",
56
56
  });
57
57
  }
58
- if (event.userId !== userId) {
58
+ if (!canModifyEvent(event, userId)) {
59
59
  throw new TRPCError({
60
60
  code: "UNAUTHORIZED",
61
61
  message: "You are not authorized to update this event",
@@ -80,7 +80,7 @@ export async function deleteEventRecord(userId, id) {
80
80
  message: "Event not found",
81
81
  });
82
82
  }
83
- if (event.userId !== userId) {
83
+ if (!canModifyEvent(event, userId)) {
84
84
  throw new TRPCError({
85
85
  code: "UNAUTHORIZED",
86
86
  message: "You are not authorized to delete this event",
@@ -92,7 +92,9 @@ export async function deleteEventRecord(userId, id) {
92
92
  function canModifyEvent(event, userId) {
93
93
  if (event.userId === userId)
94
94
  return true;
95
- return event.class?.teachers.some((t) => t.id === userId) ?? false;
95
+ return Boolean(event.class?.teachers.some((t) => t.id === userId) ||
96
+ event.class?.school?.memberships?.some((admin) => admin.userId === userId) ||
97
+ event.class?.school?.users.some((admin) => admin.id === userId));
96
98
  }
97
99
  export async function attachAssignment(userId, eventId, assignmentId) {
98
100
  const event = await findEventWithClass(eventId);
@@ -115,7 +117,9 @@ export async function attachAssignment(userId, eventId, assignmentId) {
115
117
  message: "Assignment not found",
116
118
  });
117
119
  }
118
- if (!assignment.class.teachers.some((t) => t.id === userId)) {
120
+ if (!assignment.class.teachers.some((t) => t.id === userId) &&
121
+ !assignment.class.school?.memberships?.some((admin) => admin.userId === userId) &&
122
+ !assignment.class.school?.users.some((admin) => admin.id === userId)) {
119
123
  throw new TRPCError({
120
124
  code: "UNAUTHORIZED",
121
125
  message: "You are not authorized to modify this assignment",
@@ -165,4 +169,4 @@ export async function getAvailableAssignments(userId, eventId) {
165
169
  return { assignments };
166
170
  }
167
171
  //# sourceMappingURL=event.js.map
168
- //# debugId=04996008-f63b-58af-ad46-976c9d537646
172
+ //# debugId=b9df3493-e672-5d24-b6f8-26b10b12a289
@@ -1 +1 @@
1
- {"version":3,"file":"event.js","sources":["services/event.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Event service – CRUD for calendar events. Supports personal and class events.\n * Handles assignment attachment/detachment.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { parseISO } from \"date-fns\";\nimport {\n findEventById,\n findClassWithTeachers,\n createEvent,\n updateEvent,\n deleteEventById,\n findEventWithClass,\n findAssignmentWithClass,\n attachAssignmentToEvent,\n detachAssignmentFromEvent,\n findAvailableAssignmentsForClass,\n} from \"../models/event.js\";\n\n/** Get a single event. Personal events require ownership. */\nexport async function getEvent(userId: string, id: string) {\n const event = await findEventById(id);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (event.userId !== userId) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this event\",\n });\n }\n return { event };\n}\n\n/** Create a personal or class event. */\nexport async function createEventRecord(\n userId: string,\n input: {\n name?: string;\n location?: string;\n remarks?: string;\n startTime: string;\n endTime: string;\n classId?: string;\n color?: string;\n }\n) {\n if (input.classId) {\n const classData = await findClassWithTeachers(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to create events for this class\",\n });\n }\n }\n\n const event = await createEvent({\n name: input.name,\n location: input.location,\n remarks: input.remarks,\n startTime: parseISO(input.startTime),\n endTime: parseISO(input.endTime),\n userId,\n color: input.color,\n classId: input.classId,\n });\n\n return { event };\n}\n\nexport async function updateEventRecord(\n userId: string,\n id: string,\n data: {\n name?: string;\n location?: string;\n remarks?: string;\n startTime?: string;\n endTime?: string;\n color?: string;\n classId?: string;\n }\n) {\n const event = await findEventById(id);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (event.userId !== userId) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this event\",\n });\n }\n\n const updatedEvent = await updateEvent(id, {\n name: data.name,\n location: data.location,\n remarks: data.remarks,\n startTime: data.startTime ? parseISO(data.startTime) : undefined,\n endTime: data.endTime ? parseISO(data.endTime) : undefined,\n color: data.color,\n classId: data.classId,\n });\n\n return { event: updatedEvent };\n}\n\nexport async function deleteEventRecord(userId: string, id: string) {\n const event = await findEventById(id);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (event.userId !== userId) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to delete this event\",\n });\n }\n\n await deleteEventById(id);\n return { success: true };\n}\n\nfunction canModifyEvent(\n event: { userId: string | null; class?: { teachers: { id: string }[] } | null },\n userId: string\n) {\n if (event.userId === userId) return true;\n return event.class?.teachers.some((t) => t.id === userId) ?? false;\n}\n\nexport async function attachAssignment(\n userId: string,\n eventId: string,\n assignmentId: string\n) {\n const event = await findEventWithClass(eventId);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to modify this event\",\n });\n }\n\n const assignment = await findAssignmentWithClass(assignmentId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n if (!assignment.class.teachers.some((t) => t.id === userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to modify this assignment\",\n });\n }\n if (event.classId !== assignment.classId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Event and assignment must belong to the same class\",\n });\n }\n\n const updatedAssignment = await attachAssignmentToEvent(\n assignmentId,\n eventId\n );\n return { assignment: updatedAssignment };\n}\n\nexport async function detachAssignment(\n userId: string,\n eventId: string,\n assignmentId: string\n) {\n const event = await findEventWithClass(eventId);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to modify this event\",\n });\n }\n\n const updatedAssignment = await detachAssignmentFromEvent(assignmentId);\n return { assignment: updatedAssignment };\n}\n\nexport async function getAvailableAssignments(\n userId: string,\n eventId: string\n) {\n const event = await findEventWithClass(eventId);\n if (!event || !event.classId) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to access this event\",\n });\n }\n\n const assignments = await findAvailableAssignmentsForClass(event.classId);\n return { assignments };\n}\n"],"names":[],"mappings":"AAAA;;;GAGG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,gCAAgC,GACjC,MAAM,oBAAoB,CAAC;AAE5B,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAc,EAAE,EAAU;IACvD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,2CAA2C;SACrD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,KAQC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACrE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,wDAAwD;aAClE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QACpC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;QAChC,MAAM;QACN,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,EAAU,EACV,IAQC;IAED,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,WAAW,CAAC,EAAE,EAAE;QACzC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;QAChE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;QAC1D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,EAAU;IAChE,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,eAAe,CAAC,EAAE,CAAC,CAAC;IAC1B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,cAAc,CACrB,KAA+E,EAC/E,MAAc;IAEd,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,OAAe,EACf,YAAoB;IAEpB,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,YAAY,CAAC,CAAC;IAC/D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,sBAAsB;SAChC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,kDAAkD;SAC5D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;QACzC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,oDAAoD;SAC9D,CAAC,CAAC;IACL,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAM,uBAAuB,CACrD,YAAY,EACZ,OAAO,CACR,CAAC;IACF,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,OAAe,EACf,YAAoB;IAEpB,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAM,yBAAyB,CAAC,YAAY,CAAC,CAAC;IACxE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAc,EACd,OAAe;IAEf,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,gCAAgC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1E,OAAO,EAAE,WAAW,EAAE,CAAC;AACzB,CAAC","debug_id":"04996008-f63b-58af-ad46-976c9d537646"}
1
+ {"version":3,"file":"event.js","sources":["services/event.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Event service – CRUD for calendar events. Supports personal and class events.\n * Handles assignment attachment/detachment.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { parseISO } from \"date-fns\";\nimport {\n findEventById,\n findClassWithTeachers,\n createEvent,\n updateEvent,\n deleteEventById,\n findEventWithClass,\n findAssignmentWithClass,\n attachAssignmentToEvent,\n detachAssignmentFromEvent,\n findAvailableAssignmentsForClass,\n} from \"../models/event.js\";\n\n/** Get a single event. Personal events require ownership. */\nexport async function getEvent(userId: string, id: string) {\n const event = await findEventById(id);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this event\",\n });\n }\n return { event };\n}\n\n/** Create a personal or class event. */\nexport async function createEventRecord(\n userId: string,\n input: {\n name?: string;\n location?: string;\n remarks?: string;\n startTime: string;\n endTime: string;\n classId?: string;\n color?: string;\n }\n) {\n if (input.classId) {\n const classData = await findClassWithTeachers(input.classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to create events for this class\",\n });\n }\n }\n\n const event = await createEvent({\n name: input.name,\n location: input.location,\n remarks: input.remarks,\n startTime: parseISO(input.startTime),\n endTime: parseISO(input.endTime),\n userId,\n color: input.color,\n classId: input.classId,\n });\n\n return { event };\n}\n\nexport async function updateEventRecord(\n userId: string,\n id: string,\n data: {\n name?: string;\n location?: string;\n remarks?: string;\n startTime?: string;\n endTime?: string;\n color?: string;\n classId?: string;\n }\n) {\n const event = await findEventById(id);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this event\",\n });\n }\n\n const updatedEvent = await updateEvent(id, {\n name: data.name,\n location: data.location,\n remarks: data.remarks,\n startTime: data.startTime ? parseISO(data.startTime) : undefined,\n endTime: data.endTime ? parseISO(data.endTime) : undefined,\n color: data.color,\n classId: data.classId,\n });\n\n return { event: updatedEvent };\n}\n\nexport async function deleteEventRecord(userId: string, id: string) {\n const event = await findEventById(id);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to delete this event\",\n });\n }\n\n await deleteEventById(id);\n return { success: true };\n}\n\nfunction canModifyEvent(\n event: {\n userId: string | null;\n class?: {\n teachers: { id: string }[];\n school?: {\n users: { id: string }[];\n memberships?: { userId: string }[];\n } | null;\n } | null;\n },\n userId: string\n) {\n if (event.userId === userId) return true;\n return Boolean(\n event.class?.teachers.some((t) => t.id === userId) ||\n event.class?.school?.memberships?.some((admin) => admin.userId === userId) ||\n event.class?.school?.users.some((admin) => admin.id === userId)\n );\n}\n\nexport async function attachAssignment(\n userId: string,\n eventId: string,\n assignmentId: string\n) {\n const event = await findEventWithClass(eventId);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to modify this event\",\n });\n }\n\n const assignment = await findAssignmentWithClass(assignmentId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n if (\n !assignment.class.teachers.some((t) => t.id === userId) &&\n !assignment.class.school?.memberships?.some((admin) => admin.userId === userId) &&\n !assignment.class.school?.users.some((admin) => admin.id === userId)\n ) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to modify this assignment\",\n });\n }\n if (event.classId !== assignment.classId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Event and assignment must belong to the same class\",\n });\n }\n\n const updatedAssignment = await attachAssignmentToEvent(\n assignmentId,\n eventId\n );\n return { assignment: updatedAssignment };\n}\n\nexport async function detachAssignment(\n userId: string,\n eventId: string,\n assignmentId: string\n) {\n const event = await findEventWithClass(eventId);\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to modify this event\",\n });\n }\n\n const updatedAssignment = await detachAssignmentFromEvent(assignmentId);\n return { assignment: updatedAssignment };\n}\n\nexport async function getAvailableAssignments(\n userId: string,\n eventId: string\n) {\n const event = await findEventWithClass(eventId);\n if (!event || !event.classId) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found\",\n });\n }\n if (!canModifyEvent(event, userId)) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to access this event\",\n });\n }\n\n const assignments = await findAvailableAssignmentsForClass(event.classId);\n return { assignments };\n}\n"],"names":[],"mappings":"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","debug_id":"b9df3493-e672-5d24-b6f8-26b10b12a289"}
@@ -12,8 +12,8 @@ export declare function moveFile(fileId: string, targetFolderId: string): Promis
12
12
  type: string;
13
13
  name: string;
14
14
  id: string;
15
- size: number | null;
16
15
  userId: string | null;
16
+ size: number | null;
17
17
  uploadedAt: Date | null;
18
18
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
19
19
  uploadUrl: string | null;
@@ -45,8 +45,8 @@ export declare function renameFile(fileId: string, newName: string): Promise<{
45
45
  type: string;
46
46
  name: string;
47
47
  id: string;
48
- size: number | null;
49
48
  userId: string | null;
49
+ size: number | null;
50
50
  uploadedAt: Date | null;
51
51
  uploadStatus: import(".prisma/client").$Enums.UploadStatus;
52
52
  uploadUrl: string | null;
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"/","sources":["services/file.ts"],"names":[],"mappings":"AA2BA,0EAA0E;AAC1E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;GAkFpE;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,12 +3,16 @@
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]="c9a35639-e642-5ed5-b260-9bacbf8cb7e9")}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]="4ce26498-ba77-5a67-910b-428e64d7047b")}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
11
  import { findConversationMembership } from "../models/message.js";
12
+ function isSchoolAdminForClass(classData, userId) {
13
+ return Boolean(classData?.school?.memberships?.some((membership) => membership.userId === userId) ||
14
+ classData?.school?.users?.some((admin) => admin.id === userId));
15
+ }
12
16
  /** Generate a signed URL for file download. Validates user has access. */
13
17
  export async function getFileSignedUrl(userId, fileId) {
14
18
  const file = await findFileById(fileId);
@@ -22,26 +26,30 @@ export async function getFileSignedUrl(userId, fileId) {
22
26
  if (file.assignment?.class) {
23
27
  const isTeacher = file.assignment.class.teachers.some((t) => t.id === userId);
24
28
  const isStudent = file.assignment.class.students.some((s) => s.id === userId);
25
- logger.info(`Assignment file access check - userId: ${userId}, isTeacher: ${isTeacher}, isStudent: ${isStudent}`);
26
- hasAccess = isTeacher || isStudent;
29
+ const isSchoolAdmin = isSchoolAdminForClass(file.assignment.class, userId);
30
+ logger.info(`Assignment file access check - userId: ${userId}, isTeacher: ${isTeacher}, isStudent: ${isStudent}, isSchoolAdmin: ${isSchoolAdmin}`);
31
+ hasAccess = isTeacher || isStudent || isSchoolAdmin;
27
32
  }
28
33
  if (file.announcement?.class) {
29
34
  const ann = file.announcement;
30
35
  const isTeacher = ann.class.teachers.some((t) => t.id === userId);
31
36
  const isStudent = ann.class.students.some((s) => s.id === userId);
32
- hasAccess = hasAccess || isTeacher || isStudent;
37
+ const isSchoolAdmin = isSchoolAdminForClass(ann.class, userId);
38
+ hasAccess = hasAccess || isTeacher || isStudent || isSchoolAdmin;
33
39
  }
34
40
  if (file.submission?.assignment?.classId) {
35
41
  hasAccess =
36
42
  hasAccess ||
37
43
  file.submission.studentId === userId ||
38
- file.submission.assignment.class.teachers.some((t) => t.id === userId);
44
+ file.submission.assignment.class.teachers.some((t) => t.id === userId) ||
45
+ isSchoolAdminForClass(file.submission.assignment.class, userId);
39
46
  }
40
47
  if (file.annotations?.assignment?.classId) {
41
48
  hasAccess =
42
49
  hasAccess ||
43
50
  file.annotations.studentId === userId ||
44
- file.annotations.assignment.class.teachers.some((t) => t.id === userId);
51
+ file.annotations.assignment.class.teachers.some((t) => t.id === userId) ||
52
+ isSchoolAdminForClass(file.annotations.assignment.class, userId);
45
53
  }
46
54
  if (file.userId === userId) {
47
55
  hasAccess = true;
@@ -53,7 +61,8 @@ export async function getFileSignedUrl(userId, fileId) {
53
61
  if (file.folder?.class) {
54
62
  const isTeacher = file.folder.class.teachers.some((t) => t.id === userId);
55
63
  const isStudent = file.folder.class.students.some((s) => s.id === userId);
56
- hasAccess = hasAccess || isTeacher || isStudent;
64
+ const isSchoolAdmin = isSchoolAdminForClass(file.folder.class, userId);
65
+ hasAccess = hasAccess || isTeacher || isStudent || isSchoolAdmin;
57
66
  }
58
67
  if (!hasAccess) {
59
68
  throw new TRPCError({
@@ -135,4 +144,4 @@ export async function deleteFileRecord(fileId, classId) {
135
144
  return { success: true };
136
145
  }
137
146
  //# sourceMappingURL=file.js.map
138
- //# debugId=c9a35639-e642-5ed5-b260-9bacbf8cb7e9
147
+ //# debugId=4ce26498-ba77-5a67-910b-428e64d7047b
@@ -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\";\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"}
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\nfunction isSchoolAdminForClass(classData: any, userId: string) {\n return Boolean(\n classData?.school?.memberships?.some(\n (membership: { userId: string }) => membership.userId === userId\n ) ||\n classData?.school?.users?.some((admin: { id: string }) => admin.id === userId)\n );\n}\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 const isSchoolAdmin = isSchoolAdminForClass(file.assignment.class, userId);\n logger.info(\n `Assignment file access check - userId: ${userId}, isTeacher: ${isTeacher}, isStudent: ${isStudent}, isSchoolAdmin: ${isSchoolAdmin}`\n );\n hasAccess = isTeacher || isStudent || isSchoolAdmin;\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 const isSchoolAdmin = isSchoolAdminForClass(ann.class, userId);\n hasAccess = hasAccess || isTeacher || isStudent || isSchoolAdmin;\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 isSchoolAdminForClass(file.submission.assignment.class, 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 isSchoolAdminForClass(file.annotations.assignment.class, 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 const isSchoolAdmin = isSchoolAdminForClass(file.folder.class, userId);\n hasAccess = hasAccess || isTeacher || isStudent || isSchoolAdmin;\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":"4ce26498-ba77-5a67-910b-428e64d7047b"}
@@ -9,21 +9,21 @@ export declare function createFolderRecord(userId: string, input: {
9
9
  type: string;
10
10
  name: string;
11
11
  id: string;
12
- size: number | null;
13
- uploadedAt: Date | null;
14
12
  user: {
15
13
  id: string;
16
14
  username: string;
17
15
  } | null;
16
+ size: number | null;
17
+ uploadedAt: Date | null;
18
18
  }[];
19
19
  childFolders: {
20
20
  name: string;
21
21
  id: string;
22
+ color: string | null;
22
23
  _count: {
23
24
  files: number;
24
25
  childFolders: number;
25
26
  };
26
- color: string | null;
27
27
  }[];
28
28
  } & {
29
29
  name: string;
@@ -38,21 +38,21 @@ export declare function getFolder(folderId: string): Promise<{
38
38
  type: string;
39
39
  name: string;
40
40
  id: string;
41
- size: number | null;
42
- uploadedAt: Date | null;
43
41
  user: {
44
42
  id: string;
45
43
  username: string;
46
44
  } | null;
45
+ size: number | null;
46
+ uploadedAt: Date | null;
47
47
  }[];
48
48
  childFolders: {
49
49
  name: string;
50
50
  id: string;
51
+ color: string | null;
51
52
  _count: {
52
53
  files: number;
53
54
  childFolders: number;
54
55
  };
55
- color: string | null;
56
56
  }[];
57
57
  parentFolder: {
58
58
  name: string;
@@ -70,21 +70,21 @@ export declare function getChildFolders(classId: string): Promise<({
70
70
  type: string;
71
71
  name: string;
72
72
  id: string;
73
- size: number | null;
74
- uploadedAt: Date | null;
75
73
  user: {
76
74
  id: string;
77
75
  username: string;
78
76
  } | null;
77
+ size: number | null;
78
+ uploadedAt: Date | null;
79
79
  }[];
80
80
  childFolders: {
81
81
  name: string;
82
82
  id: string;
83
+ color: string | null;
83
84
  _count: {
84
85
  files: number;
85
86
  childFolders: number;
86
87
  };
87
- color: string | null;
88
88
  }[];
89
89
  } & {
90
90
  name: string;
@@ -98,21 +98,21 @@ export declare function getFolderChildren(folderId: string, classId: string): Pr
98
98
  type: string;
99
99
  name: string;
100
100
  id: string;
101
- size: number | null;
102
- uploadedAt: Date | null;
103
101
  user: {
104
102
  id: string;
105
103
  username: string;
106
104
  } | null;
105
+ size: number | null;
106
+ uploadedAt: Date | null;
107
107
  }[];
108
108
  childFolders: {
109
109
  name: string;
110
110
  id: string;
111
+ color: string | null;
111
112
  _count: {
112
113
  files: number;
113
114
  childFolders: number;
114
115
  };
115
- color: string | null;
116
116
  }[];
117
117
  } & {
118
118
  name: string;
@@ -126,24 +126,24 @@ export declare function getRootFolder(classId: string): Promise<{
126
126
  type: string;
127
127
  name: string;
128
128
  id: string;
129
- size: number | null;
130
- uploadedAt: Date | null;
131
129
  user: {
132
130
  id: string;
133
131
  username: string;
134
132
  } | null;
133
+ size: number | null;
134
+ uploadedAt: Date | null;
135
135
  }[];
136
136
  childFolders: {
137
137
  name: string;
138
138
  id: string;
139
- files: {
140
- id: string;
141
- }[];
139
+ color: string | null;
142
140
  _count: {
143
141
  files: number;
144
142
  childFolders: number;
145
143
  };
146
- color: string | null;
144
+ files: {
145
+ id: string;
146
+ }[];
147
147
  childFolders: {
148
148
  id: string;
149
149
  }[];
@@ -173,21 +173,21 @@ export declare function moveFolder(userId: string, folderId: string, targetParen
173
173
  type: string;
174
174
  name: string;
175
175
  id: string;
176
- size: number | null;
177
- uploadedAt: Date | null;
178
176
  user: {
179
177
  id: string;
180
178
  username: string;
181
179
  } | null;
180
+ size: number | null;
181
+ uploadedAt: Date | null;
182
182
  }[];
183
183
  childFolders: {
184
184
  name: string;
185
185
  id: string;
186
+ color: string | null;
186
187
  _count: {
187
188
  files: number;
188
189
  childFolders: number;
189
190
  };
190
- color: string | null;
191
191
  }[];
192
192
  } & {
193
193
  name: string;
@@ -201,21 +201,21 @@ export declare function updateFolderRecord(folderId: string, name: string, color
201
201
  type: string;
202
202
  name: string;
203
203
  id: string;
204
- size: number | null;
205
- uploadedAt: Date | null;
206
204
  user: {
207
205
  id: string;
208
206
  username: string;
209
207
  } | null;
208
+ size: number | null;
209
+ uploadedAt: Date | null;
210
210
  }[];
211
211
  childFolders: {
212
212
  name: string;
213
213
  id: string;
214
+ color: string | null;
214
215
  _count: {
215
216
  files: number;
216
217
  childFolders: number;
217
218
  };
218
- color: string | null;
219
219
  }[];
220
220
  } & {
221
221
  name: string;