@studious-lms/server 1.6.0 → 1.8.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 (212) hide show
  1. package/dist/models/access.d.ts +21 -66
  2. package/dist/models/access.d.ts.map +1 -1
  3. package/dist/models/access.js +10 -26
  4. package/dist/models/access.js.map +1 -1
  5. package/dist/models/agenda.d.ts +2 -0
  6. package/dist/models/agenda.d.ts.map +1 -1
  7. package/dist/models/announcement.d.ts +2 -1
  8. package/dist/models/announcement.d.ts.map +1 -1
  9. package/dist/models/announcement.js +2 -3
  10. package/dist/models/announcement.js.map +1 -1
  11. package/dist/models/assignment.d.ts +7 -0
  12. package/dist/models/assignment.d.ts.map +1 -1
  13. package/dist/models/auth.d.ts +2 -4
  14. package/dist/models/auth.d.ts.map +1 -1
  15. package/dist/models/auth.js +2 -3
  16. package/dist/models/auth.js.map +1 -1
  17. package/dist/models/class.d.ts +32 -4
  18. package/dist/models/class.d.ts.map +1 -1
  19. package/dist/models/class.js +15 -2
  20. package/dist/models/class.js.map +1 -1
  21. package/dist/models/cloudStorage.d.ts +140 -0
  22. package/dist/models/cloudStorage.d.ts.map +1 -0
  23. package/dist/models/cloudStorage.js +175 -0
  24. package/dist/models/cloudStorage.js.map +1 -0
  25. package/dist/models/comment.d.ts +0 -24
  26. package/dist/models/comment.d.ts.map +1 -1
  27. package/dist/models/comment.js +2 -6
  28. package/dist/models/comment.js.map +1 -1
  29. package/dist/models/conversation.d.ts +38 -22
  30. package/dist/models/conversation.d.ts.map +1 -1
  31. package/dist/models/conversation.js +50 -2
  32. package/dist/models/conversation.js.map +1 -1
  33. package/dist/models/department.d.ts +154 -0
  34. package/dist/models/department.d.ts.map +1 -0
  35. package/dist/models/department.js +135 -0
  36. package/dist/models/department.js.map +1 -0
  37. package/dist/models/event.d.ts +7 -1
  38. package/dist/models/event.d.ts.map +1 -1
  39. package/dist/models/event.js +2 -5
  40. package/dist/models/event.js.map +1 -1
  41. package/dist/models/file.d.ts +25 -10
  42. package/dist/models/file.d.ts.map +1 -1
  43. package/dist/models/file.js +2 -5
  44. package/dist/models/file.js.map +1 -1
  45. package/dist/models/folder.d.ts +47 -0
  46. package/dist/models/folder.d.ts.map +1 -1
  47. package/dist/models/labChat.d.ts +5 -3
  48. package/dist/models/labChat.d.ts.map +1 -1
  49. package/dist/models/message.d.ts +46 -12
  50. package/dist/models/message.d.ts.map +1 -1
  51. package/dist/models/message.js +19 -10
  52. package/dist/models/message.js.map +1 -1
  53. package/dist/models/newtonChat.d.ts +2 -1
  54. package/dist/models/newtonChat.d.ts.map +1 -1
  55. package/dist/models/school.d.ts +45 -12
  56. package/dist/models/school.d.ts.map +1 -1
  57. package/dist/models/school.js +19 -2
  58. package/dist/models/school.js.map +1 -1
  59. package/dist/models/section.d.ts +1 -0
  60. package/dist/models/section.d.ts.map +1 -1
  61. package/dist/models/tag.d.ts +117 -0
  62. package/dist/models/tag.d.ts.map +1 -0
  63. package/dist/models/tag.js +113 -0
  64. package/dist/models/tag.js.map +1 -0
  65. package/dist/models/worksheet.d.ts +3 -0
  66. package/dist/models/worksheet.d.ts.map +1 -1
  67. package/dist/pipelines/aiLabChatContract.d.ts +8 -8
  68. package/dist/routers/_app.d.ts +3556 -2298
  69. package/dist/routers/_app.d.ts.map +1 -1
  70. package/dist/routers/_app.js +6 -2
  71. package/dist/routers/_app.js.map +1 -1
  72. package/dist/routers/agenda.d.ts +2 -0
  73. package/dist/routers/agenda.d.ts.map +1 -1
  74. package/dist/routers/announcement.d.ts.map +1 -1
  75. package/dist/routers/announcement.js +3 -5
  76. package/dist/routers/announcement.js.map +1 -1
  77. package/dist/routers/assignment.d.ts +18 -9
  78. package/dist/routers/assignment.d.ts.map +1 -1
  79. package/dist/routers/assignment.js +2 -3
  80. package/dist/routers/assignment.js.map +1 -1
  81. package/dist/routers/class.d.ts +87 -14
  82. package/dist/routers/class.d.ts.map +1 -1
  83. package/dist/routers/class.js +5 -2
  84. package/dist/routers/class.js.map +1 -1
  85. package/dist/routers/conversation.d.ts +20 -5
  86. package/dist/routers/conversation.d.ts.map +1 -1
  87. package/dist/routers/conversation.js +6 -3
  88. package/dist/routers/conversation.js.map +1 -1
  89. package/dist/routers/department.d.ts +293 -0
  90. package/dist/routers/department.d.ts.map +1 -0
  91. package/dist/routers/department.js +93 -0
  92. package/dist/routers/department.js.map +1 -0
  93. package/dist/routers/event.d.ts +2 -1
  94. package/dist/routers/event.d.ts.map +1 -1
  95. package/dist/routers/file.d.ts +8 -6
  96. package/dist/routers/file.d.ts.map +1 -1
  97. package/dist/routers/file.js +26 -17
  98. package/dist/routers/file.js.map +1 -1
  99. package/dist/routers/folder.d.ts +139 -186
  100. package/dist/routers/folder.d.ts.map +1 -1
  101. package/dist/routers/folder.js +82 -90
  102. package/dist/routers/folder.js.map +1 -1
  103. package/dist/routers/labChat.d.ts +11 -2
  104. package/dist/routers/labChat.d.ts.map +1 -1
  105. package/dist/routers/message.d.ts +20 -2
  106. package/dist/routers/message.d.ts.map +1 -1
  107. package/dist/routers/newtonChat.d.ts +9 -0
  108. package/dist/routers/newtonChat.d.ts.map +1 -1
  109. package/dist/routers/school.d.ts +160 -17
  110. package/dist/routers/school.d.ts.map +1 -1
  111. package/dist/routers/school.js +46 -3
  112. package/dist/routers/school.js.map +1 -1
  113. package/dist/routers/tag.d.ts +119 -0
  114. package/dist/routers/tag.d.ts.map +1 -0
  115. package/dist/routers/tag.js +52 -0
  116. package/dist/routers/tag.js.map +1 -0
  117. package/dist/routers/worksheet.d.ts +1 -0
  118. package/dist/routers/worksheet.d.ts.map +1 -1
  119. package/dist/seedDatabase.d.ts +0 -1
  120. package/dist/seedDatabase.d.ts.map +1 -1
  121. package/dist/services/agenda.d.ts +2 -0
  122. package/dist/services/agenda.d.ts.map +1 -1
  123. package/dist/services/assignment.d.ts +18 -9
  124. package/dist/services/assignment.d.ts.map +1 -1
  125. package/dist/services/assignment.js +4 -4
  126. package/dist/services/assignment.js.map +1 -1
  127. package/dist/services/class.d.ts +87 -14
  128. package/dist/services/class.d.ts.map +1 -1
  129. package/dist/services/class.js +91 -4
  130. package/dist/services/class.js.map +1 -1
  131. package/dist/services/conversation.d.ts +21 -5
  132. package/dist/services/conversation.d.ts.map +1 -1
  133. package/dist/services/conversation.js +22 -3
  134. package/dist/services/conversation.js.map +1 -1
  135. package/dist/services/department.d.ts +200 -0
  136. package/dist/services/department.d.ts.map +1 -0
  137. package/dist/services/department.js +368 -0
  138. package/dist/services/department.js.map +1 -0
  139. package/dist/services/event.d.ts +2 -1
  140. package/dist/services/event.d.ts.map +1 -1
  141. package/dist/services/file.d.ts +85 -0
  142. package/dist/services/file.d.ts.map +1 -1
  143. package/dist/services/file.js +92 -2
  144. package/dist/services/file.js.map +1 -1
  145. package/dist/services/folder.d.ts +125 -149
  146. package/dist/services/folder.d.ts.map +1 -1
  147. package/dist/services/folder.js +258 -199
  148. package/dist/services/folder.js.map +1 -1
  149. package/dist/services/labChat.d.ts +11 -2
  150. package/dist/services/labChat.d.ts.map +1 -1
  151. package/dist/services/labChat.js +8 -7
  152. package/dist/services/labChat.js.map +1 -1
  153. package/dist/services/message.d.ts +20 -2
  154. package/dist/services/message.d.ts.map +1 -1
  155. package/dist/services/message.js +7 -23
  156. package/dist/services/message.js.map +1 -1
  157. package/dist/services/newtonChat.d.ts +9 -0
  158. package/dist/services/newtonChat.d.ts.map +1 -1
  159. package/dist/services/newtonChat.js +7 -6
  160. package/dist/services/newtonChat.js.map +1 -1
  161. package/dist/services/school.d.ts +142 -15
  162. package/dist/services/school.d.ts.map +1 -1
  163. package/dist/services/school.js +308 -54
  164. package/dist/services/school.js.map +1 -1
  165. package/dist/services/tag.d.ts +71 -0
  166. package/dist/services/tag.d.ts.map +1 -0
  167. package/dist/services/tag.js +178 -0
  168. package/dist/services/tag.js.map +1 -0
  169. package/dist/services/worksheet.d.ts +1 -0
  170. package/dist/services/worksheet.d.ts.map +1 -1
  171. package/dist/utils/aiUser.d.ts.map +1 -1
  172. package/dist/utils/aiUser.js +2 -3
  173. package/dist/utils/aiUser.js.map +1 -1
  174. package/package.json +1 -1
  175. package/prisma/migrations/20260509220000_institution_overhaul/migration.sql +184 -0
  176. package/prisma/schema.prisma +119 -35
  177. package/src/models/access.ts +8 -24
  178. package/src/models/announcement.ts +0 -1
  179. package/src/models/auth.ts +0 -1
  180. package/src/models/class.ts +13 -0
  181. package/src/models/cloudStorage.ts +194 -0
  182. package/src/models/comment.ts +0 -4
  183. package/src/models/conversation.ts +54 -0
  184. package/src/models/department.ts +159 -0
  185. package/src/models/event.ts +0 -3
  186. package/src/models/file.ts +0 -3
  187. package/src/models/message.ts +19 -5
  188. package/src/models/school.ts +26 -0
  189. package/src/models/tag.ts +140 -0
  190. package/src/routers/_app.ts +4 -0
  191. package/src/routers/announcement.ts +1 -3
  192. package/src/routers/assignment.ts +0 -1
  193. package/src/routers/class.ts +3 -0
  194. package/src/routers/conversation.ts +7 -0
  195. package/src/routers/department.ts +174 -0
  196. package/src/routers/file.ts +29 -26
  197. package/src/routers/folder.ts +100 -121
  198. package/src/routers/school.ts +83 -0
  199. package/src/routers/tag.ts +78 -0
  200. package/src/services/assignment.ts +2 -3
  201. package/src/services/class.ts +108 -3
  202. package/src/services/conversation.ts +30 -0
  203. package/src/services/department.ts +478 -0
  204. package/src/services/file.ts +100 -0
  205. package/src/services/folder.ts +313 -240
  206. package/src/services/labChat.ts +7 -6
  207. package/src/services/message.ts +5 -21
  208. package/src/services/newtonChat.ts +5 -5
  209. package/src/services/school.ts +369 -57
  210. package/src/services/tag.ts +229 -0
  211. package/src/utils/aiUser.ts +0 -1
  212. package/tests/integration/routers/conversation.test.ts +121 -2
@@ -1,16 +1,20 @@
1
1
  import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure } from "../trpc.js";
2
3
  import {
3
- createTRPCRouter,
4
- protectedProcedure,
5
- protectedTeacherProcedure,
6
- } from "../trpc.js";
7
- import {
4
+ deleteFileGeneric,
8
5
  getFileSignedUrl,
9
- moveFile,
10
- renameFile,
11
- deleteFileRecord,
6
+ moveFileScoped,
7
+ renameFileScoped,
12
8
  } from "../services/file.js";
13
9
 
10
+ /**
11
+ * File router. Backed by the same unified scope-aware permission model as the
12
+ * folder router. The class files page and the cloud-storage page (personal /
13
+ * institution) both run through the same endpoints.
14
+ *
15
+ * Permissions are derived from the file's parent folder scope, so callers
16
+ * never need to send `classId` / `scope` — just the file id.
17
+ */
14
18
  export const fileRouter = createTRPCRouter({
15
19
  getSignedUrl: protectedProcedure
16
20
  .input(z.object({ fileId: z.string() }))
@@ -18,38 +22,37 @@ export const fileRouter = createTRPCRouter({
18
22
  getFileSignedUrl(ctx.user!.id, input.fileId)
19
23
  ),
20
24
 
21
- move: protectedTeacherProcedure
25
+ move: protectedProcedure
22
26
  .input(
23
27
  z.object({
24
28
  fileId: z.string(),
25
29
  targetFolderId: z.string(),
26
- classId: z.string(),
27
30
  })
28
31
  )
29
- .mutation(({ input }) =>
30
- moveFile(input.fileId, input.targetFolderId)
32
+ .mutation(({ ctx, input }) =>
33
+ moveFileScoped(ctx.user!.id, {
34
+ fileId: input.fileId,
35
+ targetFolderId: input.targetFolderId,
36
+ })
31
37
  ),
32
38
 
33
- rename: protectedTeacherProcedure
39
+ rename: protectedProcedure
34
40
  .input(
35
41
  z.object({
36
42
  fileId: z.string(),
37
- newName: z.string(),
38
- classId: z.string(),
43
+ newName: z.string().min(1),
39
44
  })
40
45
  )
41
- .mutation(({ input }) =>
42
- renameFile(input.fileId, input.newName)
46
+ .mutation(({ ctx, input }) =>
47
+ renameFileScoped(ctx.user!.id, {
48
+ fileId: input.fileId,
49
+ newName: input.newName,
50
+ })
43
51
  ),
44
52
 
45
- delete: protectedTeacherProcedure
46
- .input(
47
- z.object({
48
- fileId: z.string(),
49
- classId: z.string(),
50
- })
51
- )
52
- .mutation(({ input }) =>
53
- deleteFileRecord(input.fileId, input.classId)
53
+ delete: protectedProcedure
54
+ .input(z.object({ fileId: z.string() }))
55
+ .mutation(({ ctx, input }) =>
56
+ deleteFileGeneric(ctx.user!.id, input.fileId)
54
57
  ),
55
58
  });
@@ -2,175 +2,154 @@ import { z } from "zod";
2
2
  import {
3
3
  createTRPCRouter,
4
4
  protectedProcedure,
5
- protectedClassMemberProcedure,
6
- protectedTeacherProcedure,
7
5
  } from "../trpc.js";
8
6
  import {
7
+ browseFolder,
8
+ confirmFolderUpload,
9
9
  createFolderRecord,
10
+ deleteFolder,
10
11
  getFolder,
11
- getChildFolders,
12
- getFolderChildren,
12
+ getFolderParents,
13
+ getFolderUploadUrls,
13
14
  getRootFolder,
14
- uploadFilesToFolder,
15
- deleteFolderRecord,
15
+ listUserScopes,
16
16
  moveFolder,
17
17
  updateFolderRecord,
18
- getFolderParents,
19
- getFolderUploadUrls,
20
18
  } from "../services/folder.js";
21
19
 
22
- const createFolderSchema = z.object({
23
- name: z.string(),
24
- parentFolderId: z.string().optional(),
25
- color: z.string().optional(),
26
- });
27
-
28
20
  const directFileSchema = z.object({
29
21
  name: z.string(),
30
22
  type: z.string(),
31
23
  size: z.number(),
32
24
  });
33
25
 
34
- const uploadFilesToFolderSchema = z.object({
35
- folderId: z.string(),
36
- files: z.array(directFileSchema),
37
- });
38
-
39
- const getRootFolderSchema = z.object({
40
- classId: z.string(),
41
- });
26
+ /**
27
+ * Cloud scope discriminator. Folders live under one of:
28
+ * - CLASS: a class shared-files area
29
+ * - SCHOOL_STORAGE: institution-wide files
30
+ * - PERSONAL: a single user's storage
31
+ *
32
+ * All folder operations identify the location they apply to with either a
33
+ * `folderId` (for an existing folder) or a `scope` (when no folder exists yet,
34
+ * e.g. browsing a scope root). There is no `classId` shorthand — use
35
+ * `{ scope: { kind: "CLASS", classId } }` instead.
36
+ */
37
+ const cloudScopeSchema = z.discriminatedUnion("kind", [
38
+ z.object({ kind: z.literal("CLASS"), classId: z.string() }),
39
+ z.object({ kind: z.literal("SCHOOL_STORAGE"), schoolId: z.string() }),
40
+ z.object({ kind: z.literal("PERSONAL"), userId: z.string() }),
41
+ ]);
42
42
 
43
43
  export const folderRouter = createTRPCRouter({
44
- create: protectedTeacherProcedure
45
- .input(createFolderSchema)
46
- .mutation(async ({ ctx, input }) => {
47
- if (!ctx.user) {
48
- throw new Error("You must be logged in to create a folder");
49
- }
50
- return createFolderRecord(ctx.user.id, {
51
- classId: input.classId,
52
- name: input.name,
53
- parentFolderId: input.parentFolderId,
54
- color: input.color,
55
- });
56
- }),
57
-
58
- get: protectedClassMemberProcedure
59
- .input(z.object({ folderId: z.string(), classId: z.string() }))
60
- .query(({ input }) => getFolder(input.folderId)),
61
-
62
- getChildFolders: protectedClassMemberProcedure
63
- .input(z.object({ classId: z.string() }))
64
- .query(({ input }) => getChildFolders(input.classId)),
44
+ /** Browse a folder (or scope root) with breadcrumbs and access flag. */
45
+ browse: protectedProcedure
46
+ .input(
47
+ z.union([
48
+ z.object({ folderId: z.string() }),
49
+ z.object({ scope: cloudScopeSchema }),
50
+ ])
51
+ )
52
+ .query(({ ctx, input }) => browseFolder(ctx.user!.id, input)),
65
53
 
66
- getFolderChildren: protectedClassMemberProcedure
67
- .input(z.object({ folderId: z.string(), classId: z.string() }))
68
- .query(({ input }) =>
69
- getFolderChildren(input.folderId, input.classId)
70
- ),
54
+ /** What scopes can this user pick from in the cloud-storage UI? */
55
+ listScopes: protectedProcedure.query(({ ctx }) =>
56
+ listUserScopes(ctx.user!.id)
57
+ ),
71
58
 
72
- getRootFolder: protectedClassMemberProcedure
73
- .input(getRootFolderSchema)
74
- .query(({ input }) => getRootFolder(input.classId)),
59
+ /** Root folder for any scope (no folderId). */
60
+ getRootFolder: protectedProcedure
61
+ .input(z.object({ scope: cloudScopeSchema }))
62
+ .query(({ ctx, input }) => getRootFolder(ctx.user!.id, input.scope)),
75
63
 
76
- uploadFiles: protectedTeacherProcedure
77
- .input(uploadFilesToFolderSchema)
78
- .mutation(async ({ ctx, input }) => {
79
- if (!ctx.user) {
80
- throw new Error("You must be logged in to upload files");
81
- }
82
- return uploadFilesToFolder(
83
- ctx.user.id,
84
- input.classId,
85
- input.folderId,
86
- input.files
87
- );
88
- }),
64
+ get: protectedProcedure
65
+ .input(z.object({ folderId: z.string() }))
66
+ .query(({ ctx, input }) => getFolder(ctx.user!.id, input.folderId)),
89
67
 
90
- delete: protectedTeacherProcedure
91
- .input(z.object({ classId: z.string(), folderId: z.string() }))
92
- .mutation(async ({ ctx, input }) => {
93
- if (!ctx.user) {
94
- throw new Error("You must be logged in to delete folders");
95
- }
96
- return deleteFolderRecord(ctx.user.id, input.classId, input.folderId);
97
- }),
68
+ getParents: protectedProcedure
69
+ .input(z.object({ folderId: z.string() }))
70
+ .query(({ ctx, input }) => getFolderParents(ctx.user!.id, input.folderId)),
98
71
 
99
- move: protectedTeacherProcedure
72
+ create: protectedProcedure
100
73
  .input(
101
74
  z.object({
102
- folderId: z.string(),
103
- targetParentFolderId: z.string(),
104
- classId: z.string(),
75
+ name: z.string().min(1),
76
+ parentFolderId: z.string().nullable().optional(),
77
+ color: z.string().optional(),
78
+ scope: cloudScopeSchema.optional(),
105
79
  })
106
80
  )
107
- .mutation(async ({ ctx, input }) => {
108
- if (!ctx.user) {
109
- throw new Error("You must be logged in to move folders");
110
- }
111
- return moveFolder(
112
- ctx.user.id,
113
- input.folderId,
114
- input.targetParentFolderId,
115
- input.classId
116
- );
117
- }),
81
+ .mutation(({ ctx, input }) =>
82
+ createFolderRecord(ctx.user!.id, {
83
+ name: input.name,
84
+ parentFolderId: input.parentFolderId ?? undefined,
85
+ color: input.color,
86
+ scope: input.scope,
87
+ })
88
+ ),
118
89
 
119
- update: protectedTeacherProcedure
90
+ update: protectedProcedure
120
91
  .input(
121
92
  z.object({
122
93
  folderId: z.string(),
123
- name: z.string(),
94
+ name: z.string().optional(),
124
95
  color: z.string().optional(),
125
- classId: z.string(),
126
96
  })
127
97
  )
128
- .mutation(({ input }) =>
129
- updateFolderRecord(
130
- input.folderId,
131
- input.name,
132
- input.color,
133
- input.classId
134
- )
98
+ .mutation(({ ctx, input }) =>
99
+ updateFolderRecord(ctx.user!.id, {
100
+ folderId: input.folderId,
101
+ name: input.name,
102
+ color: input.color,
103
+ })
135
104
  ),
136
105
 
137
- getParents: protectedProcedure
106
+ delete: protectedProcedure
138
107
  .input(z.object({ folderId: z.string() }))
139
- .query(({ input }) => getFolderParents(input.folderId)),
108
+ .mutation(({ ctx, input }) => deleteFolder(ctx.user!.id, input.folderId)),
109
+
110
+ move: protectedProcedure
111
+ .input(
112
+ z.object({
113
+ folderId: z.string(),
114
+ targetParentFolderId: z.string(),
115
+ })
116
+ )
117
+ .mutation(({ ctx, input }) =>
118
+ moveFolder(ctx.user!.id, {
119
+ folderId: input.folderId,
120
+ targetParentFolderId: input.targetParentFolderId,
121
+ })
122
+ ),
140
123
 
141
- getFolderUploadUrls: protectedTeacherProcedure
124
+ /** Get signed upload URLs for direct-to-GCS upload. */
125
+ getFolderUploadUrls: protectedProcedure
142
126
  .input(
143
127
  z.object({
144
- classId: z.string(),
145
128
  folderId: z.string(),
146
129
  files: z.array(directFileSchema),
147
130
  })
148
131
  )
149
- .mutation(async ({ ctx, input }) => {
150
- if (!ctx.user) {
151
- throw new Error("You must be logged in to upload files");
152
- }
153
- return getFolderUploadUrls(
154
- ctx.user.id,
155
- input.classId,
156
- input.folderId,
157
- input.files
158
- );
159
- }),
132
+ .mutation(({ ctx, input }) =>
133
+ getFolderUploadUrls(ctx.user!.id, {
134
+ folderId: input.folderId,
135
+ files: input.files,
136
+ })
137
+ ),
160
138
 
161
- confirmFolderUpload: protectedTeacherProcedure
139
+ /** Acknowledge a direct upload completed (or failed). */
140
+ confirmFolderUpload: protectedProcedure
162
141
  .input(
163
142
  z.object({
164
143
  fileId: z.string(),
165
144
  uploadSuccess: z.boolean(),
145
+ errorMessage: z.string().optional(),
166
146
  })
167
147
  )
168
- .mutation(async ({ ctx, input }) => {
169
- if (!ctx.user) {
170
- throw new Error("You must be logged in to confirm uploads");
171
- }
172
- const { confirmDirectUpload } = await import("../lib/fileUpload.js");
173
- await confirmDirectUpload(input.fileId, input.uploadSuccess);
174
- return { success: true };
175
- }),
148
+ .mutation(({ ctx, input }) =>
149
+ confirmFolderUpload(ctx.user!.id, {
150
+ fileId: input.fileId,
151
+ uploadSuccess: input.uploadSuccess,
152
+ errorMessage: input.errorMessage,
153
+ })
154
+ ),
176
155
  });
@@ -7,18 +7,24 @@ import {
7
7
  import {
8
8
  acceptInvite,
9
9
  addMember,
10
+ confirmSchoolAssetUpload,
10
11
  createInvite,
11
12
  createSchoolClass,
12
13
  createSchoolRecord,
13
14
  getAdminOverview,
14
15
  getCurrentSchool,
15
16
  getInvite,
17
+ getSchoolAssetUploadUrl,
18
+ getStudentTimeline,
16
19
  listClasses,
17
20
  listInvites,
21
+ listMemberIds,
18
22
  listMembers,
19
23
  removeMember,
20
24
  revokeInvite,
25
+ searchMembers,
21
26
  updateMemberRole,
27
+ updateSchoolDetails,
22
28
  } from "../services/school.js";
23
29
 
24
30
  const schoolRoleSchema = z.enum(["ADMIN", "TEACHER", "STUDENT"]);
@@ -38,12 +44,88 @@ export const schoolRouter = createTRPCRouter({
38
44
  .input(
39
45
  z.object({
40
46
  name: z.string().min(1),
47
+ description: z.string().optional(),
41
48
  subdomain: z.string().min(1).optional(),
42
49
  logoId: z.string().optional(),
43
50
  })
44
51
  )
45
52
  .mutation(({ ctx, input }) => createSchoolRecord(ctx.user!.id, input)),
46
53
 
54
+ update: protectedProcedure
55
+ .input(
56
+ z.object({
57
+ schoolId: z.string(),
58
+ name: z.string().min(1).optional(),
59
+ description: z.string().nullable().optional(),
60
+ subdomain: z.string().nullable().optional(),
61
+ logoId: z.string().nullable().optional(),
62
+ })
63
+ )
64
+ .mutation(({ ctx, input }) => updateSchoolDetails(ctx.user!.id, input)),
65
+
66
+ searchMembers: protectedProcedure
67
+ .input(
68
+ z.object({
69
+ schoolId: z.string(),
70
+ query: z.string(),
71
+ limit: z.number().min(1).max(50).optional(),
72
+ role: schoolRoleSchema.optional(),
73
+ })
74
+ )
75
+ .query(({ ctx, input }) =>
76
+ searchMembers(
77
+ ctx.user!.id,
78
+ input.schoolId,
79
+ input.query,
80
+ input.limit,
81
+ input.role
82
+ )
83
+ ),
84
+
85
+ listMemberIds: protectedProcedure
86
+ .input(schoolIdSchema)
87
+ .query(({ ctx, input }) => listMemberIds(ctx.user!.id, input.schoolId)),
88
+
89
+ getAssetUploadUrl: protectedProcedure
90
+ .input(
91
+ z.object({
92
+ fileName: z.string(),
93
+ fileType: z.string(),
94
+ fileSize: z.number(),
95
+ })
96
+ )
97
+ .mutation(({ ctx, input }) =>
98
+ getSchoolAssetUploadUrl(ctx.user!.id, input)
99
+ ),
100
+
101
+ confirmAssetUpload: protectedProcedure
102
+ .input(
103
+ z.object({
104
+ fileId: z.string(),
105
+ uploadSuccess: z.boolean(),
106
+ })
107
+ )
108
+ .mutation(({ ctx, input }) =>
109
+ confirmSchoolAssetUpload(
110
+ ctx.user!.id,
111
+ input.fileId,
112
+ input.uploadSuccess
113
+ )
114
+ ),
115
+
116
+ getStudentTimeline: protectedProcedure
117
+ .input(
118
+ z.object({
119
+ schoolId: z.string(),
120
+ studentId: z.string(),
121
+ from: z.date().optional(),
122
+ to: z.date().optional(),
123
+ })
124
+ )
125
+ .query(({ ctx, input }) =>
126
+ getStudentTimeline(ctx.user!.id, input)
127
+ ),
128
+
47
129
  getAdminOverview: protectedProcedure
48
130
  .input(schoolIdSchema)
49
131
  .query(({ ctx, input }) => getAdminOverview(ctx.user!.id, input.schoolId)),
@@ -60,6 +142,7 @@ export const schoolRouter = createTRPCRouter({
60
142
  section: z.string(),
61
143
  subject: z.string(),
62
144
  color: z.string().optional(),
145
+ departmentId: z.string().optional(),
63
146
  teachers: z.array(z.string()).optional(),
64
147
  students: z.array(z.string()).optional(),
65
148
  })
@@ -0,0 +1,78 @@
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure } from "../trpc.js";
3
+ import {
4
+ attachTag,
5
+ createTagRecord,
6
+ deleteTagRecord,
7
+ detachTag,
8
+ getAttachedTags,
9
+ listTags,
10
+ updateTagRecord,
11
+ } from "../services/tag.js";
12
+
13
+ const tagTargetSchema = z.enum(["USER", "CLASS", "RUBRIC", "BOUNDARY"]);
14
+
15
+ export const tagRouter = createTRPCRouter({
16
+ list: protectedProcedure
17
+ .input(
18
+ z.object({
19
+ schoolId: z.string(),
20
+ departmentId: z.string().nullable().optional(),
21
+ })
22
+ )
23
+ .query(({ ctx, input }) => listTags(ctx.user!.id, input)),
24
+
25
+ create: protectedProcedure
26
+ .input(
27
+ z.object({
28
+ schoolId: z.string(),
29
+ departmentId: z.string().nullable().optional(),
30
+ name: z.string().min(1),
31
+ color: z.string().optional(),
32
+ })
33
+ )
34
+ .mutation(({ ctx, input }) => createTagRecord(ctx.user!.id, input)),
35
+
36
+ update: protectedProcedure
37
+ .input(
38
+ z.object({
39
+ tagId: z.string(),
40
+ name: z.string().min(1).optional(),
41
+ color: z.string().optional(),
42
+ departmentId: z.string().nullable().optional(),
43
+ })
44
+ )
45
+ .mutation(({ ctx, input }) => updateTagRecord(ctx.user!.id, input)),
46
+
47
+ delete: protectedProcedure
48
+ .input(z.object({ tagId: z.string() }))
49
+ .mutation(({ ctx, input }) =>
50
+ deleteTagRecord(ctx.user!.id, input.tagId)
51
+ ),
52
+
53
+ attach: protectedProcedure
54
+ .input(
55
+ z.object({
56
+ tagId: z.string(),
57
+ target: tagTargetSchema,
58
+ targetId: z.string(),
59
+ })
60
+ )
61
+ .mutation(({ ctx, input }) => attachTag(ctx.user!.id, input)),
62
+
63
+ detach: protectedProcedure
64
+ .input(
65
+ z.object({
66
+ tagId: z.string(),
67
+ target: tagTargetSchema,
68
+ targetId: z.string(),
69
+ })
70
+ )
71
+ .mutation(({ ctx, input }) => detachTag(ctx.user!.id, input)),
72
+
73
+ getAttached: protectedProcedure
74
+ .input(
75
+ z.object({ target: tagTargetSchema, targetId: z.string() })
76
+ )
77
+ .query(({ ctx, input }) => getAttachedTags(ctx.user!.id, input)),
78
+ });
@@ -24,9 +24,8 @@ import {
24
24
  import { createDirectUploadFiles, type UploadedFile } from "../lib/fileUpload.js";
25
25
  import { deleteFile } from "../lib/googleCloudStorage.js";
26
26
  import { sendToMultiple } from "./notification.js";
27
- import { enqueueRunJob, QUEUE_NAMES } from "../workers/queues.js";
27
+ import { enqueueRunJob } from "../workers/queues.js";
28
28
  import { logger } from "../utils/logger.js";
29
- import { RunJobType } from "@prisma/client";
30
29
 
31
30
  export async function assignmentExists(id: string) {
32
31
  const assignment = await findAssignmentById(id);
@@ -670,7 +669,7 @@ export async function updateSubmissionRecord(
670
669
  for (const wr of worksheetResponses) {
671
670
  try {
672
671
  await enqueueRunJob(
673
- QUEUE_NAMES.GRADE_WORKSHEET as RunJobType,
672
+ "GRADE_WORKSHEET",
674
673
  { worksheetResponseId: wr.id },
675
674
  { classId: submission.assignment.classId, userId },
676
675
  );