@studious-lms/server 1.4.6 → 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.
- package/dist/models/access.d.ts +112 -0
- package/dist/models/access.d.ts.map +1 -0
- package/dist/models/access.js +49 -0
- package/dist/models/access.js.map +1 -0
- package/dist/models/announcement.d.ts +17 -2
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/announcement.js +21 -4
- package/dist/models/announcement.js.map +1 -1
- package/dist/models/assignment.d.ts +37 -37
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/assignment.js +15 -15
- package/dist/models/assignment.js.map +1 -1
- package/dist/models/attendance.d.ts +1 -1
- package/dist/models/attendance.d.ts.map +1 -1
- package/dist/models/attendance.js +6 -5
- package/dist/models/attendance.js.map +1 -1
- package/dist/models/auth.d.ts +5 -5
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/auth.js +7 -11
- package/dist/models/auth.js.map +1 -1
- package/dist/models/class.d.ts +26 -26
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/class.js +7 -11
- package/dist/models/class.js.map +1 -1
- package/dist/models/comment.d.ts +134 -70
- package/dist/models/comment.d.ts.map +1 -1
- package/dist/models/comment.js +30 -42
- package/dist/models/comment.js.map +1 -1
- package/dist/models/conversation.d.ts +7 -7
- package/dist/models/event.d.ts +52 -3
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/event.js +56 -6
- package/dist/models/event.js.map +1 -1
- package/dist/models/file.d.ts +178 -103
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/file.js +54 -14
- package/dist/models/file.js.map +1 -1
- package/dist/models/folder.d.ts +22 -22
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/folder.js +5 -4
- package/dist/models/folder.js.map +1 -1
- package/dist/models/labChat.d.ts +20 -20
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/labChat.js +8 -10
- package/dist/models/labChat.js.map +1 -1
- package/dist/models/marketing.d.ts +1 -1
- package/dist/models/message.d.ts +6 -6
- package/dist/models/newtonChat.d.ts +5 -5
- package/dist/models/school.d.ts +361 -0
- package/dist/models/school.d.ts.map +1 -0
- package/dist/models/school.js +314 -0
- package/dist/models/school.js.map +1 -0
- package/dist/models/section.d.ts +1 -1
- package/dist/models/section.d.ts.map +1 -1
- package/dist/models/section.js +5 -4
- package/dist/models/section.js.map +1 -1
- package/dist/models/user.d.ts +4 -4
- package/dist/models/worksheet.d.ts +65 -65
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/models/worksheet.js +6 -14
- package/dist/models/worksheet.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts +2 -2
- package/dist/pipelines/pdfToWorksheet.d.ts +1 -1
- package/dist/routers/_app.d.ts +2855 -2051
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/announcement.d.ts +5 -5
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +25 -5
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +44 -44
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +24 -11
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/class.d.ts +21 -20
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +3 -2
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/comment.d.ts +5 -5
- package/dist/routers/conversation.d.ts +6 -6
- package/dist/routers/event.d.ts +21 -2
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/file.d.ts +2 -2
- package/dist/routers/folder.d.ts +24 -24
- package/dist/routers/labChat.d.ts +7 -7
- package/dist/routers/school.d.ts +384 -0
- package/dist/routers/school.d.ts.map +1 -0
- package/dist/routers/school.js +79 -0
- package/dist/routers/school.js.map +1 -0
- package/dist/routers/search.d.ts +1 -1
- package/dist/routers/worksheet.d.ts +21 -21
- package/dist/services/announcement.d.ts.map +1 -1
- package/dist/services/announcement.js +10 -6
- package/dist/services/announcement.js.map +1 -1
- package/dist/services/assignment.d.ts +36 -36
- package/dist/services/class.d.ts +20 -19
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +22 -3
- package/dist/services/class.js.map +1 -1
- package/dist/services/comment.d.ts +5 -5
- package/dist/services/comment.d.ts.map +1 -1
- package/dist/services/comment.js +8 -3
- package/dist/services/comment.js.map +1 -1
- package/dist/services/conversation.d.ts +6 -6
- package/dist/services/event.d.ts +21 -2
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/event.js +11 -7
- package/dist/services/event.js.map +1 -1
- package/dist/services/file.d.ts +2 -2
- package/dist/services/file.d.ts.map +1 -1
- package/dist/services/file.js +17 -8
- package/dist/services/file.js.map +1 -1
- package/dist/services/folder.d.ts +24 -24
- package/dist/services/labChat.d.ts +7 -7
- package/dist/services/school.d.ts +299 -0
- package/dist/services/school.d.ts.map +1 -0
- package/dist/services/school.js +225 -0
- package/dist/services/school.js.map +1 -0
- package/dist/services/worksheet.d.ts +21 -21
- package/package.json +1 -1
- package/prisma/migrations/20260504071500_school_memberships/migration.sql +81 -0
- package/prisma/schema.prisma +66 -2
- package/src/models/access.ts +48 -0
- package/src/models/announcement.ts +19 -2
- package/src/models/assignment.ts +17 -13
- package/src/models/attendance.ts +4 -3
- package/src/models/auth.ts +8 -9
- package/src/models/class.ts +8 -9
- package/src/models/comment.ts +29 -40
- package/src/models/event.ts +54 -4
- package/src/models/file.ts +53 -12
- package/src/models/folder.ts +3 -2
- package/src/models/labChat.ts +9 -8
- package/src/models/school.ts +361 -0
- package/src/models/section.ts +3 -2
- package/src/models/worksheet.ts +7 -12
- package/src/routers/_app.ts +2 -0
- package/src/routers/announcement.ts +30 -4
- package/src/routers/assignment.ts +27 -9
- package/src/routers/class.ts +1 -0
- package/src/routers/school.ts +122 -0
- package/src/services/announcement.ts +12 -4
- package/src/services/class.ts +25 -2
- package/src/services/comment.ts +10 -1
- package/src/services/event.ts +23 -6
- package/src/services/file.ts +20 -6
- package/src/services/school.ts +308 -0
|
@@ -15,9 +15,9 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
|
|
|
15
15
|
options: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
16
16
|
id: string;
|
|
17
17
|
createdAt: Date;
|
|
18
|
+
updatedAt: Date;
|
|
18
19
|
order: number | null;
|
|
19
20
|
points: number;
|
|
20
|
-
updatedAt: Date;
|
|
21
21
|
worksheetId: string;
|
|
22
22
|
markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
23
23
|
question: string;
|
|
@@ -27,8 +27,8 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
|
|
|
27
27
|
name: string;
|
|
28
28
|
id: string;
|
|
29
29
|
createdAt: Date;
|
|
30
|
-
classId: string;
|
|
31
30
|
updatedAt: Date;
|
|
31
|
+
classId: string;
|
|
32
32
|
}>;
|
|
33
33
|
/** Check if worksheet exists. */
|
|
34
34
|
export declare function worksheetExists(id: string): Promise<boolean>;
|
|
@@ -40,8 +40,8 @@ export declare function listWorksheets(classId: string): Promise<{
|
|
|
40
40
|
name: string;
|
|
41
41
|
id: string;
|
|
42
42
|
createdAt: Date;
|
|
43
|
-
classId: string;
|
|
44
43
|
updatedAt: Date;
|
|
44
|
+
classId: string;
|
|
45
45
|
}[]>;
|
|
46
46
|
export declare function updateWorksheetRecord(worksheetId: string, userId: string, data: {
|
|
47
47
|
name?: string;
|
|
@@ -49,22 +49,22 @@ export declare function updateWorksheetRecord(worksheetId: string, userId: strin
|
|
|
49
49
|
name: string;
|
|
50
50
|
id: string;
|
|
51
51
|
createdAt: Date;
|
|
52
|
-
classId: string;
|
|
53
52
|
updatedAt: Date;
|
|
53
|
+
classId: string;
|
|
54
54
|
}>;
|
|
55
55
|
export declare function deleteWorksheetRecord(worksheetId: string, userId: string): Promise<{
|
|
56
56
|
name: string;
|
|
57
57
|
id: string;
|
|
58
58
|
createdAt: Date;
|
|
59
|
-
classId: string;
|
|
60
59
|
updatedAt: Date;
|
|
60
|
+
classId: string;
|
|
61
61
|
}>;
|
|
62
62
|
export declare function createWorksheetRecord(classId: string, name: string): Promise<{
|
|
63
63
|
name: string;
|
|
64
64
|
id: string;
|
|
65
65
|
createdAt: Date;
|
|
66
|
-
classId: string;
|
|
67
66
|
updatedAt: Date;
|
|
67
|
+
classId: string;
|
|
68
68
|
}>;
|
|
69
69
|
/**
|
|
70
70
|
* Create a worksheet by converting a PDF (URL) into structured questions.
|
|
@@ -74,8 +74,8 @@ export declare function createWorksheetFromPdf(classId: string, url: string): Pr
|
|
|
74
74
|
name: string;
|
|
75
75
|
id: string;
|
|
76
76
|
createdAt: Date;
|
|
77
|
-
classId: string;
|
|
78
77
|
updatedAt: Date;
|
|
78
|
+
classId: string;
|
|
79
79
|
}>;
|
|
80
80
|
/**
|
|
81
81
|
* Create a worksheet from a PDF that was uploaded through the direct-upload flow.
|
|
@@ -84,8 +84,8 @@ export declare function createWorksheetFromUploadedPdf(classId: string, fileId:
|
|
|
84
84
|
name: string;
|
|
85
85
|
id: string;
|
|
86
86
|
createdAt: Date;
|
|
87
|
-
classId: string;
|
|
88
87
|
updatedAt: Date;
|
|
88
|
+
classId: string;
|
|
89
89
|
}>;
|
|
90
90
|
export declare function addQuestionToWorksheet(worksheetId: string, userId: string, data: {
|
|
91
91
|
question: string;
|
|
@@ -99,9 +99,9 @@ export declare function addQuestionToWorksheet(worksheetId: string, userId: stri
|
|
|
99
99
|
options: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
100
100
|
id: string;
|
|
101
101
|
createdAt: Date;
|
|
102
|
+
updatedAt: Date;
|
|
102
103
|
order: number | null;
|
|
103
104
|
points: number;
|
|
104
|
-
updatedAt: Date;
|
|
105
105
|
worksheetId: string;
|
|
106
106
|
markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
107
107
|
question: string;
|
|
@@ -122,9 +122,9 @@ export declare function updateWorksheetQuestionRecord(worksheetId: string, quest
|
|
|
122
122
|
options: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
123
123
|
id: string;
|
|
124
124
|
createdAt: Date;
|
|
125
|
+
updatedAt: Date;
|
|
125
126
|
order: number | null;
|
|
126
127
|
points: number;
|
|
127
|
-
updatedAt: Date;
|
|
128
128
|
worksheetId: string;
|
|
129
129
|
markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
130
130
|
question: string;
|
|
@@ -135,9 +135,9 @@ export declare function deleteWorksheetQuestionRecord(worksheetId: string, quest
|
|
|
135
135
|
options: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
136
136
|
id: string;
|
|
137
137
|
createdAt: Date;
|
|
138
|
+
updatedAt: Date;
|
|
138
139
|
order: number | null;
|
|
139
140
|
points: number;
|
|
140
|
-
updatedAt: Date;
|
|
141
141
|
worksheetId: string;
|
|
142
142
|
markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
143
143
|
question: string;
|
|
@@ -152,6 +152,7 @@ export declare function getWorksheetSubmission(worksheetId: string, submissionId
|
|
|
152
152
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
153
153
|
id: string;
|
|
154
154
|
createdAt: Date;
|
|
155
|
+
updatedAt: Date | null;
|
|
155
156
|
studentId: string;
|
|
156
157
|
questionId: string;
|
|
157
158
|
response: string;
|
|
@@ -159,17 +160,16 @@ export declare function getWorksheetSubmission(worksheetId: string, submissionId
|
|
|
159
160
|
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
160
161
|
points: number;
|
|
161
162
|
feedback: string | null;
|
|
162
|
-
updatedAt: Date | null;
|
|
163
163
|
studentWorksheetResponseId: string | null;
|
|
164
164
|
})[];
|
|
165
165
|
} & {
|
|
166
166
|
id: string;
|
|
167
|
-
submissionId: string | null;
|
|
168
167
|
createdAt: Date;
|
|
168
|
+
updatedAt: Date;
|
|
169
|
+
submissionId: string | null;
|
|
169
170
|
studentId: string;
|
|
170
171
|
submittedAt: Date | null;
|
|
171
172
|
submitted: boolean;
|
|
172
|
-
updatedAt: Date;
|
|
173
173
|
worksheetId: string;
|
|
174
174
|
}>;
|
|
175
175
|
export declare function answerWorksheetQuestion(worksheetResponseId: string, questionId: string, response: string, userId: string): Promise<{
|
|
@@ -177,6 +177,7 @@ export declare function answerWorksheetQuestion(worksheetResponseId: string, que
|
|
|
177
177
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
178
178
|
id: string;
|
|
179
179
|
createdAt: Date;
|
|
180
|
+
updatedAt: Date | null;
|
|
180
181
|
studentId: string;
|
|
181
182
|
questionId: string;
|
|
182
183
|
response: string;
|
|
@@ -184,23 +185,23 @@ export declare function answerWorksheetQuestion(worksheetResponseId: string, que
|
|
|
184
185
|
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
185
186
|
points: number;
|
|
186
187
|
feedback: string | null;
|
|
187
|
-
updatedAt: Date | null;
|
|
188
188
|
studentWorksheetResponseId: string | null;
|
|
189
189
|
}[];
|
|
190
190
|
} & {
|
|
191
191
|
id: string;
|
|
192
|
-
submissionId: string | null;
|
|
193
192
|
createdAt: Date;
|
|
193
|
+
updatedAt: Date;
|
|
194
|
+
submissionId: string | null;
|
|
194
195
|
studentId: string;
|
|
195
196
|
submittedAt: Date | null;
|
|
196
197
|
submitted: boolean;
|
|
197
|
-
updatedAt: Date;
|
|
198
198
|
worksheetId: string;
|
|
199
199
|
}>;
|
|
200
200
|
export declare function cancelGrading(worksheetResponseId: string, progressId: string, userId: string): Promise<{
|
|
201
201
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
202
202
|
id: string;
|
|
203
203
|
createdAt: Date;
|
|
204
|
+
updatedAt: Date | null;
|
|
204
205
|
studentId: string;
|
|
205
206
|
questionId: string;
|
|
206
207
|
response: string;
|
|
@@ -208,13 +209,13 @@ export declare function cancelGrading(worksheetResponseId: string, progressId: s
|
|
|
208
209
|
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
209
210
|
points: number;
|
|
210
211
|
feedback: string | null;
|
|
211
|
-
updatedAt: Date | null;
|
|
212
212
|
studentWorksheetResponseId: string | null;
|
|
213
213
|
}>;
|
|
214
214
|
export declare function regradeQuestion(worksheetResponseId: string, progressId: string, userId: string): Promise<{
|
|
215
215
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
216
216
|
id: string;
|
|
217
217
|
createdAt: Date;
|
|
218
|
+
updatedAt: Date | null;
|
|
218
219
|
studentId: string;
|
|
219
220
|
questionId: string;
|
|
220
221
|
response: string;
|
|
@@ -222,7 +223,6 @@ export declare function regradeQuestion(worksheetResponseId: string, progressId:
|
|
|
222
223
|
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
223
224
|
points: number;
|
|
224
225
|
feedback: string | null;
|
|
225
|
-
updatedAt: Date | null;
|
|
226
226
|
studentWorksheetResponseId: string | null;
|
|
227
227
|
}>;
|
|
228
228
|
export declare function gradeAnswer(questionId: string, studentWorksheetResponseId: string, userId: string, data: {
|
|
@@ -236,6 +236,7 @@ export declare function gradeAnswer(questionId: string, studentWorksheetResponse
|
|
|
236
236
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
237
237
|
id: string;
|
|
238
238
|
createdAt: Date;
|
|
239
|
+
updatedAt: Date | null;
|
|
239
240
|
studentId: string;
|
|
240
241
|
questionId: string;
|
|
241
242
|
response: string;
|
|
@@ -243,16 +244,15 @@ export declare function gradeAnswer(questionId: string, studentWorksheetResponse
|
|
|
243
244
|
markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
244
245
|
points: number;
|
|
245
246
|
feedback: string | null;
|
|
246
|
-
updatedAt: Date | null;
|
|
247
247
|
studentWorksheetResponseId: string | null;
|
|
248
248
|
}>;
|
|
249
249
|
export declare function addCommentToResponse(responseId: string, comment: string, authorId: string): Promise<{
|
|
250
250
|
status: import(".prisma/client").$Enums.GenerationStatus | null;
|
|
251
251
|
id: string;
|
|
252
|
+
createdAt: Date;
|
|
252
253
|
assignmentId: string | null;
|
|
253
254
|
submissionId: string | null;
|
|
254
255
|
announcementId: string | null;
|
|
255
|
-
createdAt: Date;
|
|
256
256
|
modifiedAt: Date | null;
|
|
257
257
|
content: string;
|
|
258
258
|
authorId: string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
-- Add durable multi-school membership and invite support.
|
|
2
|
+
|
|
3
|
+
CREATE TYPE "SchoolMembershipRole" AS ENUM ('ADMIN', 'TEACHER', 'STUDENT');
|
|
4
|
+
CREATE TYPE "SchoolMembershipStatus" AS ENUM ('ACTIVE', 'INVITED', 'SUSPENDED');
|
|
5
|
+
CREATE TYPE "SchoolInviteStatus" AS ENUM ('PENDING', 'ACCEPTED', 'REVOKED', 'EXPIRED');
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "School"
|
|
8
|
+
ALTER COLUMN "logoId" DROP NOT NULL,
|
|
9
|
+
ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
|
11
|
+
|
|
12
|
+
CREATE TABLE "SchoolMembership" (
|
|
13
|
+
"id" TEXT NOT NULL,
|
|
14
|
+
"schoolId" TEXT NOT NULL,
|
|
15
|
+
"userId" TEXT NOT NULL,
|
|
16
|
+
"role" "SchoolMembershipRole" NOT NULL,
|
|
17
|
+
"status" "SchoolMembershipStatus" NOT NULL DEFAULT 'ACTIVE',
|
|
18
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
19
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
20
|
+
|
|
21
|
+
CONSTRAINT "SchoolMembership_pkey" PRIMARY KEY ("id")
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE "SchoolInvite" (
|
|
25
|
+
"id" TEXT NOT NULL,
|
|
26
|
+
"token" TEXT NOT NULL,
|
|
27
|
+
"email" TEXT NOT NULL,
|
|
28
|
+
"role" "SchoolMembershipRole" NOT NULL,
|
|
29
|
+
"status" "SchoolInviteStatus" NOT NULL DEFAULT 'PENDING',
|
|
30
|
+
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
31
|
+
"acceptedAt" TIMESTAMP(3),
|
|
32
|
+
"revokedAt" TIMESTAMP(3),
|
|
33
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
34
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
35
|
+
"schoolId" TEXT NOT NULL,
|
|
36
|
+
"invitedById" TEXT NOT NULL,
|
|
37
|
+
|
|
38
|
+
CONSTRAINT "SchoolInvite_pkey" PRIMARY KEY ("id")
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
INSERT INTO "SchoolMembership" ("id", "schoolId", "userId", "role", "status", "createdAt", "updatedAt")
|
|
42
|
+
SELECT
|
|
43
|
+
"id",
|
|
44
|
+
"schoolId",
|
|
45
|
+
"id",
|
|
46
|
+
CASE
|
|
47
|
+
WHEN "role" = 'ADMIN' THEN 'ADMIN'::"SchoolMembershipRole"
|
|
48
|
+
WHEN "role" = 'TEACHER' THEN 'TEACHER'::"SchoolMembershipRole"
|
|
49
|
+
ELSE 'STUDENT'::"SchoolMembershipRole"
|
|
50
|
+
END,
|
|
51
|
+
'ACTIVE'::"SchoolMembershipStatus",
|
|
52
|
+
CURRENT_TIMESTAMP,
|
|
53
|
+
CURRENT_TIMESTAMP
|
|
54
|
+
FROM "User"
|
|
55
|
+
WHERE "schoolId" IS NOT NULL
|
|
56
|
+
ON CONFLICT DO NOTHING;
|
|
57
|
+
|
|
58
|
+
CREATE UNIQUE INDEX "SchoolMembership_schoolId_userId_key" ON "SchoolMembership"("schoolId", "userId");
|
|
59
|
+
CREATE INDEX "SchoolMembership_userId_idx" ON "SchoolMembership"("userId");
|
|
60
|
+
CREATE INDEX "SchoolMembership_schoolId_role_idx" ON "SchoolMembership"("schoolId", "role");
|
|
61
|
+
CREATE INDEX "SchoolMembership_schoolId_status_idx" ON "SchoolMembership"("schoolId", "status");
|
|
62
|
+
|
|
63
|
+
CREATE UNIQUE INDEX "SchoolInvite_token_key" ON "SchoolInvite"("token");
|
|
64
|
+
CREATE INDEX "SchoolInvite_schoolId_status_idx" ON "SchoolInvite"("schoolId", "status");
|
|
65
|
+
CREATE INDEX "SchoolInvite_email_idx" ON "SchoolInvite"("email");
|
|
66
|
+
|
|
67
|
+
ALTER TABLE "SchoolMembership"
|
|
68
|
+
ADD CONSTRAINT "SchoolMembership_schoolId_fkey"
|
|
69
|
+
FOREIGN KEY ("schoolId") REFERENCES "School"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
70
|
+
|
|
71
|
+
ALTER TABLE "SchoolMembership"
|
|
72
|
+
ADD CONSTRAINT "SchoolMembership_userId_fkey"
|
|
73
|
+
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
74
|
+
|
|
75
|
+
ALTER TABLE "SchoolInvite"
|
|
76
|
+
ADD CONSTRAINT "SchoolInvite_schoolId_fkey"
|
|
77
|
+
FOREIGN KEY ("schoolId") REFERENCES "School"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
78
|
+
|
|
79
|
+
ALTER TABLE "SchoolInvite"
|
|
80
|
+
ADD CONSTRAINT "SchoolInvite_invitedById_fkey"
|
|
81
|
+
FOREIGN KEY ("invitedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
package/prisma/schema.prisma
CHANGED
|
@@ -81,15 +81,38 @@ enum SubmissionRecommendationState {
|
|
|
81
81
|
DISMISSED
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
enum SchoolMembershipRole {
|
|
85
|
+
ADMIN
|
|
86
|
+
TEACHER
|
|
87
|
+
STUDENT
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
enum SchoolMembershipStatus {
|
|
91
|
+
ACTIVE
|
|
92
|
+
INVITED
|
|
93
|
+
SUSPENDED
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
enum SchoolInviteStatus {
|
|
97
|
+
PENDING
|
|
98
|
+
ACCEPTED
|
|
99
|
+
REVOKED
|
|
100
|
+
EXPIRED
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
model School {
|
|
85
104
|
id String @id @default(uuid())
|
|
86
105
|
name String
|
|
87
|
-
logo File
|
|
88
|
-
logoId String
|
|
106
|
+
logo File? @relation(references: [id], fields: [logoId])
|
|
107
|
+
logoId String?
|
|
89
108
|
subdomain String? @unique
|
|
109
|
+
createdAt DateTime @default(now())
|
|
110
|
+
updatedAt DateTime @updatedAt
|
|
90
111
|
|
|
91
112
|
|
|
92
113
|
users User[]
|
|
114
|
+
memberships SchoolMembership[]
|
|
115
|
+
invites SchoolInvite[]
|
|
93
116
|
classes Class[]
|
|
94
117
|
}
|
|
95
118
|
|
|
@@ -123,6 +146,8 @@ model User {
|
|
|
123
146
|
|
|
124
147
|
school School? @relation(fields: [schoolId], references: [id])
|
|
125
148
|
schoolId String?
|
|
149
|
+
schoolMemberships SchoolMembership[]
|
|
150
|
+
sentSchoolInvites SchoolInvite[] @relation("SchoolInviteInvitedBy")
|
|
126
151
|
|
|
127
152
|
// Chat relations
|
|
128
153
|
conversationMemberships ConversationMember[]
|
|
@@ -134,6 +159,45 @@ model User {
|
|
|
134
159
|
|
|
135
160
|
}
|
|
136
161
|
|
|
162
|
+
model SchoolMembership {
|
|
163
|
+
id String @id @default(uuid())
|
|
164
|
+
schoolId String
|
|
165
|
+
userId String
|
|
166
|
+
role SchoolMembershipRole
|
|
167
|
+
status SchoolMembershipStatus @default(ACTIVE)
|
|
168
|
+
createdAt DateTime @default(now())
|
|
169
|
+
updatedAt DateTime @updatedAt
|
|
170
|
+
|
|
171
|
+
school School @relation(fields: [schoolId], references: [id], onDelete: Cascade)
|
|
172
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
173
|
+
|
|
174
|
+
@@unique([schoolId, userId])
|
|
175
|
+
@@index([userId])
|
|
176
|
+
@@index([schoolId, role])
|
|
177
|
+
@@index([schoolId, status])
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
model SchoolInvite {
|
|
181
|
+
id String @id @default(uuid())
|
|
182
|
+
token String @unique
|
|
183
|
+
email String
|
|
184
|
+
role SchoolMembershipRole
|
|
185
|
+
status SchoolInviteStatus @default(PENDING)
|
|
186
|
+
expiresAt DateTime
|
|
187
|
+
acceptedAt DateTime?
|
|
188
|
+
revokedAt DateTime?
|
|
189
|
+
createdAt DateTime @default(now())
|
|
190
|
+
updatedAt DateTime @updatedAt
|
|
191
|
+
|
|
192
|
+
schoolId String
|
|
193
|
+
school School @relation(fields: [schoolId], references: [id], onDelete: Cascade)
|
|
194
|
+
invitedById String
|
|
195
|
+
invitedBy User @relation("SchoolInviteInvitedBy", fields: [invitedById], references: [id], onDelete: Cascade)
|
|
196
|
+
|
|
197
|
+
@@index([schoolId, status])
|
|
198
|
+
@@index([email])
|
|
199
|
+
}
|
|
200
|
+
|
|
137
201
|
model UserProfile {
|
|
138
202
|
id String @id @default(uuid())
|
|
139
203
|
userId String @unique
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared class access filters.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const schoolAdminForClassWhere = (userId: string) => ({
|
|
6
|
+
OR: [
|
|
7
|
+
{
|
|
8
|
+
school: {
|
|
9
|
+
is: {
|
|
10
|
+
memberships: {
|
|
11
|
+
some: {
|
|
12
|
+
userId,
|
|
13
|
+
role: "ADMIN" as const,
|
|
14
|
+
status: "ACTIVE" as const,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
school: {
|
|
22
|
+
is: {
|
|
23
|
+
users: {
|
|
24
|
+
some: {
|
|
25
|
+
id: userId,
|
|
26
|
+
role: "ADMIN" as const,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const classTeacherOrSchoolAdminWhere = (userId: string) => ({
|
|
36
|
+
OR: [
|
|
37
|
+
{ teachers: { some: { id: userId } } },
|
|
38
|
+
schoolAdminForClassWhere(userId),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const classMemberOrSchoolAdminWhere = (userId: string) => ({
|
|
43
|
+
OR: [
|
|
44
|
+
{ teachers: { some: { id: userId } } },
|
|
45
|
+
{ students: { some: { id: userId } } },
|
|
46
|
+
schoolAdminForClassWhere(userId),
|
|
47
|
+
],
|
|
48
|
+
});
|
|
@@ -56,13 +56,30 @@ export function findAnnouncementByIdAndClass(id: string, classId: string) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
/** @returns Announcement by ID with class and
|
|
59
|
+
/** @returns Announcement by ID with class teachers and school admins. */
|
|
60
60
|
export function findAnnouncementWithClass(id: string) {
|
|
61
61
|
return prisma.announcement.findUnique({
|
|
62
62
|
where: { id },
|
|
63
63
|
include: {
|
|
64
64
|
class: {
|
|
65
|
-
include: {
|
|
65
|
+
include: {
|
|
66
|
+
teachers: true,
|
|
67
|
+
school: {
|
|
68
|
+
include: {
|
|
69
|
+
users: {
|
|
70
|
+
where: { role: "ADMIN" as const },
|
|
71
|
+
select: { id: true },
|
|
72
|
+
},
|
|
73
|
+
memberships: {
|
|
74
|
+
where: {
|
|
75
|
+
role: "ADMIN" as const,
|
|
76
|
+
status: "ACTIVE" as const,
|
|
77
|
+
},
|
|
78
|
+
select: { userId: true },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
66
83
|
},
|
|
67
84
|
attachments: {
|
|
68
85
|
select: {
|
package/src/models/assignment.ts
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* Assignment model – assignments, submissions, attachments.
|
|
3
3
|
*/
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
|
+
import {
|
|
6
|
+
classMemberOrSchoolAdminWhere,
|
|
7
|
+
classTeacherOrSchoolAdminWhere,
|
|
8
|
+
schoolAdminForClassWhere,
|
|
9
|
+
} from "./access.js";
|
|
5
10
|
|
|
6
11
|
const assignmentGetInclude = {
|
|
7
12
|
submissions: {
|
|
@@ -57,7 +62,13 @@ export function findAssignmentWithDetails(id: string) {
|
|
|
57
62
|
/** @returns Assignment with attachments for delete. */
|
|
58
63
|
export function findAssignmentForDelete(id: string, teacherId: string) {
|
|
59
64
|
return prisma.assignment.findFirst({
|
|
60
|
-
where: {
|
|
65
|
+
where: {
|
|
66
|
+
id,
|
|
67
|
+
OR: [
|
|
68
|
+
{ teacherId },
|
|
69
|
+
{ class: schoolAdminForClassWhere(teacherId) },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
61
72
|
include: {
|
|
62
73
|
attachments: { include: { thumbnail: true } },
|
|
63
74
|
submissions: {
|
|
@@ -115,12 +126,7 @@ export function findSubmissionByIdForClassMember(submissionId: string, classId:
|
|
|
115
126
|
id: submissionId,
|
|
116
127
|
assignment: {
|
|
117
128
|
classId,
|
|
118
|
-
class:
|
|
119
|
-
OR: [
|
|
120
|
-
{ teachers: { some: { id: userId } } },
|
|
121
|
-
{ students: { some: { id: userId } } },
|
|
122
|
-
],
|
|
123
|
-
},
|
|
129
|
+
class: classMemberOrSchoolAdminWhere(userId),
|
|
124
130
|
},
|
|
125
131
|
},
|
|
126
132
|
include: {
|
|
@@ -148,9 +154,7 @@ export function findSubmissionForUpdate(submissionId: string, userId: string) {
|
|
|
148
154
|
{ studentId: userId },
|
|
149
155
|
{
|
|
150
156
|
assignment: {
|
|
151
|
-
class:
|
|
152
|
-
teachers: { some: { id: userId } },
|
|
153
|
-
},
|
|
157
|
+
class: classTeacherOrSchoolAdminWhere(userId),
|
|
154
158
|
},
|
|
155
159
|
},
|
|
156
160
|
],
|
|
@@ -204,7 +208,7 @@ export function findAssignmentWithClassForTeacher(assignmentId: string, teacherI
|
|
|
204
208
|
return prisma.assignment.findFirst({
|
|
205
209
|
where: {
|
|
206
210
|
id: assignmentId,
|
|
207
|
-
class:
|
|
211
|
+
class: classTeacherOrSchoolAdminWhere(teacherId),
|
|
208
212
|
},
|
|
209
213
|
include: { class: true },
|
|
210
214
|
});
|
|
@@ -216,7 +220,7 @@ export function findSubmissionsByAssignmentForTeacher(assignmentId: string, teac
|
|
|
216
220
|
where: {
|
|
217
221
|
assignment: {
|
|
218
222
|
id: assignmentId,
|
|
219
|
-
class:
|
|
223
|
+
class: classTeacherOrSchoolAdminWhere(teacherId),
|
|
220
224
|
},
|
|
221
225
|
},
|
|
222
226
|
include: {
|
|
@@ -251,7 +255,7 @@ export function findSubmissionForTeacherUpdate(submissionId: string, teacherId:
|
|
|
251
255
|
where: {
|
|
252
256
|
id: submissionId,
|
|
253
257
|
assignment: {
|
|
254
|
-
class:
|
|
258
|
+
class: classTeacherOrSchoolAdminWhere(teacherId),
|
|
255
259
|
},
|
|
256
260
|
},
|
|
257
261
|
include: {
|
package/src/models/attendance.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Attendance model – attendance records, present/late/absent students per event.
|
|
3
3
|
*/
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
|
+
import { classTeacherOrSchoolAdminWhere } from "./access.js";
|
|
5
6
|
|
|
6
7
|
const attendanceInclude = {
|
|
7
8
|
event: {
|
|
@@ -105,12 +106,12 @@ export function findClassWithStudents(classId: string) {
|
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
/** @returns Class if user is a teacher. */
|
|
109
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
109
110
|
export function findClassWithTeacher(classId: string, teacherId: string) {
|
|
110
|
-
return prisma.class.
|
|
111
|
+
return prisma.class.findFirst({
|
|
111
112
|
where: {
|
|
112
113
|
id: classId,
|
|
113
|
-
|
|
114
|
+
...classTeacherOrSchoolAdminWhere(teacherId),
|
|
114
115
|
},
|
|
115
116
|
});
|
|
116
117
|
}
|
package/src/models/auth.ts
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
import { SessionType } from "@prisma/client";
|
|
6
6
|
import { getRedis } from "../lib/redis.js";
|
|
7
7
|
import { prisma } from "../lib/prisma.js";
|
|
8
|
+
import {
|
|
9
|
+
classMemberOrSchoolAdminWhere,
|
|
10
|
+
classTeacherOrSchoolAdminWhere,
|
|
11
|
+
} from "./access.js";
|
|
8
12
|
|
|
9
13
|
const AUTH_SESSION_CACHE_PREFIX = "auth-session:";
|
|
10
14
|
|
|
@@ -246,9 +250,7 @@ export async function findUserBySessionToken(token: string) {
|
|
|
246
250
|
/** @returns Class IDs where user is a teacher. */
|
|
247
251
|
export function findTeacherClassesByUserId(userId: string) {
|
|
248
252
|
return prisma.class.findMany({
|
|
249
|
-
where:
|
|
250
|
-
teachers: { some: { id: userId } },
|
|
251
|
-
},
|
|
253
|
+
where: classTeacherOrSchoolAdminWhere(userId),
|
|
252
254
|
select: { id: true },
|
|
253
255
|
});
|
|
254
256
|
}
|
|
@@ -258,20 +260,17 @@ export function findClassWithMember(classId: string, userId: string) {
|
|
|
258
260
|
return prisma.class.findFirst({
|
|
259
261
|
where: {
|
|
260
262
|
id: classId,
|
|
261
|
-
|
|
262
|
-
{ students: { some: { id: userId } } },
|
|
263
|
-
{ teachers: { some: { id: userId } } },
|
|
264
|
-
],
|
|
263
|
+
...classMemberOrSchoolAdminWhere(userId),
|
|
265
264
|
},
|
|
266
265
|
});
|
|
267
266
|
}
|
|
268
267
|
|
|
269
|
-
/** @returns Class if user is a teacher. */
|
|
268
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
270
269
|
export function findClassWithTeacher(classId: string, teacherId: string) {
|
|
271
270
|
return prisma.class.findFirst({
|
|
272
271
|
where: {
|
|
273
272
|
id: classId,
|
|
274
|
-
|
|
273
|
+
...classTeacherOrSchoolAdminWhere(teacherId),
|
|
275
274
|
},
|
|
276
275
|
});
|
|
277
276
|
}
|
package/src/models/class.ts
CHANGED
|
@@ -10,6 +10,10 @@ import type { PrismaClient, WorksheetQuestion } from "@prisma/client";
|
|
|
10
10
|
import { prisma } from "../lib/prisma.js";
|
|
11
11
|
import { v4 as uuidv4 } from 'uuid';
|
|
12
12
|
import { copyFile } from "../lib/googleCloudStorage.js";
|
|
13
|
+
import {
|
|
14
|
+
classMemberOrSchoolAdminWhere,
|
|
15
|
+
classTeacherOrSchoolAdminWhere,
|
|
16
|
+
} from "./access.js";
|
|
13
17
|
|
|
14
18
|
const classIncludeForGetAll = {
|
|
15
19
|
assignments: {
|
|
@@ -55,7 +59,7 @@ const classIncludeForGetAll = {
|
|
|
55
59
|
/** @returns Classes where user is a teacher. */
|
|
56
60
|
export function findTeacherClasses(userId: string) {
|
|
57
61
|
return prisma.class.findMany({
|
|
58
|
-
where:
|
|
62
|
+
where: classTeacherOrSchoolAdminWhere(userId),
|
|
59
63
|
include: classIncludeForGetAll,
|
|
60
64
|
});
|
|
61
65
|
}
|
|
@@ -68,12 +72,12 @@ export function findStudentClasses(userId: string) {
|
|
|
68
72
|
});
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
/** @returns Class if user is a teacher. */
|
|
75
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
72
76
|
export function isTeacherInClass(classId: string, userId: string) {
|
|
73
77
|
return prisma.class.findFirst({
|
|
74
78
|
where: {
|
|
75
79
|
id: classId,
|
|
76
|
-
|
|
80
|
+
...classTeacherOrSchoolAdminWhere(userId),
|
|
77
81
|
},
|
|
78
82
|
});
|
|
79
83
|
}
|
|
@@ -218,12 +222,7 @@ export function findClassWithAssignments(
|
|
|
218
222
|
where: {
|
|
219
223
|
id: classId,
|
|
220
224
|
...(options.userId
|
|
221
|
-
?
|
|
222
|
-
OR: [
|
|
223
|
-
{ students: { some: { id: options.userId } } },
|
|
224
|
-
{ teachers: { some: { id: options.userId } } },
|
|
225
|
-
],
|
|
226
|
-
}
|
|
225
|
+
? classMemberOrSchoolAdminWhere(options.userId)
|
|
227
226
|
: {}),
|
|
228
227
|
},
|
|
229
228
|
include: {
|