@studious-lms/server 1.0.6 → 1.0.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/API_SPECIFICATION.md +1117 -0
- package/dist/exportType.js +1 -2
- package/dist/index.js +25 -30
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +31 -29
- package/dist/lib/googleCloudStorage.js +9 -14
- package/dist/lib/prisma.js +4 -7
- package/dist/lib/thumbnailGenerator.js +12 -20
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +17 -22
- package/dist/middleware/logging.js +5 -9
- package/dist/routers/_app.d.ts +3483 -1801
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +28 -27
- package/dist/routers/agenda.d.ts +13 -8
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +14 -17
- package/dist/routers/announcement.d.ts +4 -3
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +28 -31
- package/dist/routers/assignment.d.ts +282 -196
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +256 -202
- package/dist/routers/attendance.d.ts +5 -4
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +31 -34
- package/dist/routers/auth.d.ts +1 -0
- package/dist/routers/auth.d.ts.map +1 -1
- package/dist/routers/auth.js +80 -75
- package/dist/routers/class.d.ts +284 -14
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +435 -164
- package/dist/routers/event.d.ts +47 -38
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/event.js +76 -79
- package/dist/routers/file.d.ts +71 -1
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +267 -32
- package/dist/routers/folder.d.ts +296 -0
- package/dist/routers/folder.d.ts.map +1 -0
- package/dist/routers/folder.js +693 -0
- package/dist/routers/notifications.d.ts +103 -0
- package/dist/routers/notifications.d.ts.map +1 -0
- package/dist/routers/notifications.js +91 -0
- package/dist/routers/school.d.ts +208 -0
- package/dist/routers/school.d.ts.map +1 -0
- package/dist/routers/school.js +481 -0
- package/dist/routers/section.d.ts +1 -0
- package/dist/routers/section.d.ts.map +1 -1
- package/dist/routers/section.js +30 -33
- package/dist/routers/user.d.ts +2 -1
- package/dist/routers/user.d.ts.map +1 -1
- package/dist/routers/user.js +21 -24
- package/dist/seedDatabase.d.ts +22 -0
- package/dist/seedDatabase.d.ts.map +1 -0
- package/dist/seedDatabase.js +57 -0
- package/dist/socket/handlers.js +26 -30
- package/dist/trpc.d.ts +5 -0
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +35 -26
- package/dist/types/trpc.js +1 -2
- package/dist/utils/email.js +2 -8
- package/dist/utils/generateInviteCode.js +1 -5
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +13 -9
- package/dist/utils/prismaErrorHandler.d.ts +9 -0
- package/dist/utils/prismaErrorHandler.d.ts.map +1 -0
- package/dist/utils/prismaErrorHandler.js +234 -0
- package/dist/utils/prismaWrapper.d.ts +14 -0
- package/dist/utils/prismaWrapper.d.ts.map +1 -0
- package/dist/utils/prismaWrapper.js +64 -0
- package/package.json +11 -4
- package/prisma/migrations/20250807062924_init/migration.sql +436 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +67 -0
- package/src/index.ts +2 -2
- package/src/lib/fileUpload.ts +16 -7
- package/src/middleware/auth.ts +0 -2
- package/src/routers/_app.ts +5 -1
- package/src/routers/assignment.ts +82 -22
- package/src/routers/auth.ts +80 -54
- package/src/routers/class.ts +330 -36
- package/src/routers/file.ts +283 -20
- package/src/routers/folder.ts +755 -0
- package/src/routers/notifications.ts +93 -0
- package/src/seedDatabase.ts +66 -0
- package/src/socket/handlers.ts +4 -4
- package/src/trpc.ts +13 -0
- package/src/utils/logger.ts +14 -4
- package/src/utils/prismaErrorHandler.ts +275 -0
- package/src/utils/prismaWrapper.ts +91 -0
- package/tests/auth.test.ts +25 -0
- package/tests/class.test.ts +281 -0
- package/tests/setup.ts +98 -0
- package/tests/startup.test.ts +5 -0
- package/tsconfig.json +2 -1
- package/vitest.config.ts +11 -0
- package/dist/logger.d.ts +0 -26
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -135
- package/src/logger.ts +0 -163
package/src/routers/_app.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { eventRouter } from "./event";
|
|
|
11
11
|
import { authRouter } from "./auth";
|
|
12
12
|
import { agendaRouter } from "./agenda";
|
|
13
13
|
import { fileRouter } from "./file";
|
|
14
|
+
import { folderRouter } from "./folder";
|
|
15
|
+
import { notificationRouter } from "./notifications";
|
|
14
16
|
|
|
15
17
|
export const appRouter = createTRPCRouter({
|
|
16
18
|
class: classRouter,
|
|
@@ -23,7 +25,9 @@ export const appRouter = createTRPCRouter({
|
|
|
23
25
|
auth: authRouter,
|
|
24
26
|
agenda: agendaRouter,
|
|
25
27
|
file: fileRouter,
|
|
26
|
-
|
|
28
|
+
folder: folderRouter,
|
|
29
|
+
notification: notificationRouter,
|
|
30
|
+
});
|
|
27
31
|
|
|
28
32
|
// Export type router type definition
|
|
29
33
|
export type AppRouter = typeof appRouter;
|
|
@@ -18,6 +18,7 @@ const createAssignmentSchema = z.object({
|
|
|
18
18
|
instructions: z.string(),
|
|
19
19
|
dueDate: z.string(),
|
|
20
20
|
files: z.array(fileSchema).optional(),
|
|
21
|
+
existingFileIds: z.array(z.string()).optional(),
|
|
21
22
|
maxGrade: z.number().optional(),
|
|
22
23
|
graded: z.boolean().optional(),
|
|
23
24
|
weight: z.number().optional(),
|
|
@@ -25,6 +26,7 @@ const createAssignmentSchema = z.object({
|
|
|
25
26
|
type: z.enum(['HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'LAB', 'OTHER']).optional(),
|
|
26
27
|
markSchemeId: z.string().optional(),
|
|
27
28
|
gradingBoundaryId: z.string().optional(),
|
|
29
|
+
inProgress: z.boolean().optional(),
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
const updateAssignmentSchema = z.object({
|
|
@@ -34,12 +36,14 @@ const updateAssignmentSchema = z.object({
|
|
|
34
36
|
instructions: z.string().optional(),
|
|
35
37
|
dueDate: z.string().optional(),
|
|
36
38
|
files: z.array(fileSchema).optional(),
|
|
39
|
+
existingFileIds: z.array(z.string()).optional(),
|
|
37
40
|
removedAttachments: z.array(z.string()).optional(),
|
|
38
41
|
maxGrade: z.number().optional(),
|
|
39
42
|
graded: z.boolean().optional(),
|
|
40
43
|
weight: z.number().optional(),
|
|
41
44
|
sectionId: z.string().nullable().optional(),
|
|
42
45
|
type: z.enum(['HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'LAB', 'OTHER']).optional(),
|
|
46
|
+
inProgress: z.boolean().optional(),
|
|
43
47
|
});
|
|
44
48
|
|
|
45
49
|
const deleteAssignmentSchema = z.object({
|
|
@@ -58,6 +62,7 @@ const submissionSchema = z.object({
|
|
|
58
62
|
submissionId: z.string(),
|
|
59
63
|
submit: z.boolean().optional(),
|
|
60
64
|
newAttachments: z.array(fileSchema).optional(),
|
|
65
|
+
existingFileIds: z.array(z.string()).optional(),
|
|
61
66
|
removedAttachments: z.array(z.string()).optional(),
|
|
62
67
|
});
|
|
63
68
|
|
|
@@ -68,6 +73,7 @@ const updateSubmissionSchema = z.object({
|
|
|
68
73
|
return: z.boolean().optional(),
|
|
69
74
|
gradeReceived: z.number().nullable().optional(),
|
|
70
75
|
newAttachments: z.array(fileSchema).optional(),
|
|
76
|
+
existingFileIds: z.array(z.string()).optional(),
|
|
71
77
|
removedAttachments: z.array(z.string()).optional(),
|
|
72
78
|
rubricGrades: z.array(z.object({
|
|
73
79
|
criteriaId: z.string(),
|
|
@@ -81,7 +87,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
81
87
|
create: protectedProcedure
|
|
82
88
|
.input(createAssignmentSchema)
|
|
83
89
|
.mutation(async ({ ctx, input }) => {
|
|
84
|
-
const { classId, title, instructions, dueDate, files, maxGrade, graded, weight, sectionId, type, markSchemeId, gradingBoundaryId } = input;
|
|
90
|
+
const { classId, title, instructions, dueDate, files, existingFileIds, maxGrade, graded, weight, sectionId, type, markSchemeId, gradingBoundaryId, inProgress } = input;
|
|
85
91
|
|
|
86
92
|
if (!ctx.user) {
|
|
87
93
|
throw new TRPCError({
|
|
@@ -107,20 +113,24 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
107
113
|
});
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
let computedMaxGrade = maxGrade;
|
|
117
|
+
if (markSchemeId) {
|
|
118
|
+
const rubric = await prisma.markScheme.findUnique({
|
|
119
|
+
where: { id: markSchemeId },
|
|
120
|
+
select: {
|
|
121
|
+
structured: true,
|
|
122
|
+
}
|
|
123
|
+
});
|
|
116
124
|
|
|
117
|
-
|
|
125
|
+
const parsedRubric = JSON.parse(rubric?.structured || "{}");
|
|
118
126
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
// Calculate max grade from rubric criteria levels
|
|
128
|
+
computedMaxGrade = parsedRubric.criteria.reduce((acc: number, criterion: any) => {
|
|
129
|
+
const maxPoints = Math.max(...criterion.levels.map((level: any) => level.points));
|
|
130
|
+
return acc + maxPoints;
|
|
131
|
+
}, 0);
|
|
132
|
+
}
|
|
133
|
+
console.log(markSchemeId, gradingBoundaryId);
|
|
124
134
|
|
|
125
135
|
// Create assignment with submissions for all students
|
|
126
136
|
const assignment = await prisma.assignment.create({
|
|
@@ -132,6 +142,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
132
142
|
graded,
|
|
133
143
|
weight,
|
|
134
144
|
type,
|
|
145
|
+
inProgress: inProgress || false,
|
|
135
146
|
class: {
|
|
136
147
|
connect: { id: classId }
|
|
137
148
|
},
|
|
@@ -197,11 +208,12 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
197
208
|
}
|
|
198
209
|
}
|
|
199
210
|
});
|
|
211
|
+
|
|
200
212
|
// Upload files if provided
|
|
201
213
|
let uploadedFiles: UploadedFile[] = [];
|
|
202
214
|
if (files && files.length > 0) {
|
|
203
215
|
// Store files in a class and assignment specific directory
|
|
204
|
-
uploadedFiles = await uploadFiles(files, ctx.user.id
|
|
216
|
+
uploadedFiles = await uploadFiles(files, ctx.user.id);
|
|
205
217
|
}
|
|
206
218
|
|
|
207
219
|
// Update assignment with new file attachments
|
|
@@ -226,12 +238,24 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
226
238
|
});
|
|
227
239
|
}
|
|
228
240
|
|
|
241
|
+
// Connect existing files if provided
|
|
242
|
+
if (existingFileIds && existingFileIds.length > 0) {
|
|
243
|
+
await prisma.assignment.update({
|
|
244
|
+
where: { id: assignment.id },
|
|
245
|
+
data: {
|
|
246
|
+
attachments: {
|
|
247
|
+
connect: existingFileIds.map(fileId => ({ id: fileId }))
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
229
253
|
return assignment;
|
|
230
254
|
}),
|
|
231
255
|
update: protectedProcedure
|
|
232
256
|
.input(updateAssignmentSchema)
|
|
233
257
|
.mutation(async ({ ctx, input }) => {
|
|
234
|
-
const { id, title, instructions, dueDate, files, maxGrade, graded, weight, sectionId, type } = input;
|
|
258
|
+
const { id, title, instructions, dueDate, files, existingFileIds, maxGrade, graded, weight, sectionId, type, inProgress } = input;
|
|
235
259
|
|
|
236
260
|
if (!ctx.user) {
|
|
237
261
|
throw new TRPCError({
|
|
@@ -252,6 +276,8 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
252
276
|
id: true,
|
|
253
277
|
name: true,
|
|
254
278
|
type: true,
|
|
279
|
+
path: true,
|
|
280
|
+
size: true,
|
|
255
281
|
thumbnail: {
|
|
256
282
|
select: {
|
|
257
283
|
path: true
|
|
@@ -279,7 +305,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
279
305
|
let uploadedFiles: UploadedFile[] = [];
|
|
280
306
|
if (files && files.length > 0) {
|
|
281
307
|
// Store files in a class and assignment specific directory
|
|
282
|
-
uploadedFiles = await uploadFiles(files, ctx.user.id
|
|
308
|
+
uploadedFiles = await uploadFiles(files, ctx.user.id);
|
|
283
309
|
}
|
|
284
310
|
|
|
285
311
|
// Update assignment
|
|
@@ -293,6 +319,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
293
319
|
...(graded !== undefined && { graded }),
|
|
294
320
|
...(weight && { weight }),
|
|
295
321
|
...(type && { type }),
|
|
322
|
+
...(inProgress !== undefined && { inProgress }),
|
|
296
323
|
...(sectionId !== undefined && {
|
|
297
324
|
section: sectionId ? {
|
|
298
325
|
connect: { id: sectionId }
|
|
@@ -315,6 +342,11 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
315
342
|
}))
|
|
316
343
|
}
|
|
317
344
|
}),
|
|
345
|
+
...(existingFileIds && existingFileIds.length > 0 && {
|
|
346
|
+
attachments: {
|
|
347
|
+
connect: existingFileIds.map(fileId => ({ id: fileId }))
|
|
348
|
+
}
|
|
349
|
+
}),
|
|
318
350
|
...(input.removedAttachments && input.removedAttachments.length > 0 && {
|
|
319
351
|
attachments: {
|
|
320
352
|
deleteMany: {
|
|
@@ -348,7 +380,11 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
348
380
|
id: true,
|
|
349
381
|
name: true,
|
|
350
382
|
type: true,
|
|
351
|
-
thumbnail: true
|
|
383
|
+
thumbnail: true,
|
|
384
|
+
size: true,
|
|
385
|
+
path: true,
|
|
386
|
+
uploadedAt: true,
|
|
387
|
+
thumbnailId: true,
|
|
352
388
|
}
|
|
353
389
|
},
|
|
354
390
|
section: true,
|
|
@@ -497,6 +533,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
497
533
|
type: true,
|
|
498
534
|
size: true,
|
|
499
535
|
path: true,
|
|
536
|
+
uploadedAt: true,
|
|
500
537
|
thumbnailId: true,
|
|
501
538
|
}
|
|
502
539
|
},
|
|
@@ -740,7 +777,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
740
777
|
});
|
|
741
778
|
}
|
|
742
779
|
|
|
743
|
-
const { submissionId, submit, newAttachments, removedAttachments } = input;
|
|
780
|
+
const { submissionId, submit, newAttachments, existingFileIds, removedAttachments } = input;
|
|
744
781
|
|
|
745
782
|
const submission = await prisma.submission.findFirst({
|
|
746
783
|
where: {
|
|
@@ -837,7 +874,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
837
874
|
let uploadedFiles: UploadedFile[] = [];
|
|
838
875
|
if (newAttachments && newAttachments.length > 0) {
|
|
839
876
|
// Store files in a class and assignment specific directory
|
|
840
|
-
uploadedFiles = await uploadFiles(newAttachments, ctx.user.id
|
|
877
|
+
uploadedFiles = await uploadFiles(newAttachments, ctx.user.id);
|
|
841
878
|
}
|
|
842
879
|
|
|
843
880
|
// Update submission with new file attachments
|
|
@@ -862,6 +899,18 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
862
899
|
});
|
|
863
900
|
}
|
|
864
901
|
|
|
902
|
+
// Connect existing files if provided
|
|
903
|
+
if (existingFileIds && existingFileIds.length > 0) {
|
|
904
|
+
await prisma.submission.update({
|
|
905
|
+
where: { id: submission.id },
|
|
906
|
+
data: {
|
|
907
|
+
attachments: {
|
|
908
|
+
connect: existingFileIds.map(fileId => ({ id: fileId }))
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
|
|
865
914
|
// Delete removed attachments if any
|
|
866
915
|
if (removedAttachments && removedAttachments.length > 0) {
|
|
867
916
|
const filesToDelete = submission.attachments.filter((file) =>
|
|
@@ -1003,7 +1052,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1003
1052
|
});
|
|
1004
1053
|
}
|
|
1005
1054
|
|
|
1006
|
-
const { submissionId, return: returnSubmission, gradeReceived, newAttachments, removedAttachments, rubricGrades } = input;
|
|
1055
|
+
const { submissionId, return: returnSubmission, gradeReceived, newAttachments, existingFileIds, removedAttachments, rubricGrades } = input;
|
|
1007
1056
|
|
|
1008
1057
|
const submission = await prisma.submission.findFirst({
|
|
1009
1058
|
where: {
|
|
@@ -1093,7 +1142,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1093
1142
|
let uploadedFiles: UploadedFile[] = [];
|
|
1094
1143
|
if (newAttachments && newAttachments.length > 0) {
|
|
1095
1144
|
// Store files in a class and assignment specific directory
|
|
1096
|
-
uploadedFiles = await uploadFiles(newAttachments, ctx.user.id
|
|
1145
|
+
uploadedFiles = await uploadFiles(newAttachments, ctx.user.id);
|
|
1097
1146
|
}
|
|
1098
1147
|
|
|
1099
1148
|
// Update submission with new file attachments
|
|
@@ -1118,6 +1167,18 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1118
1167
|
});
|
|
1119
1168
|
}
|
|
1120
1169
|
|
|
1170
|
+
// Connect existing files if provided
|
|
1171
|
+
if (existingFileIds && existingFileIds.length > 0) {
|
|
1172
|
+
await prisma.submission.update({
|
|
1173
|
+
where: { id: submission.id },
|
|
1174
|
+
data: {
|
|
1175
|
+
annotations: {
|
|
1176
|
+
connect: existingFileIds.map(fileId => ({ id: fileId }))
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1121
1182
|
// Delete removed attachments if any
|
|
1122
1183
|
if (removedAttachments && removedAttachments.length > 0) {
|
|
1123
1184
|
const filesToDelete = submission.annotations.filter((file) =>
|
|
@@ -1552,7 +1613,6 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1552
1613
|
|
|
1553
1614
|
return updatedAssignment;
|
|
1554
1615
|
}),
|
|
1555
|
-
|
|
1556
1616
|
attachGradingBoundary: protectedTeacherProcedure
|
|
1557
1617
|
.input(z.object({
|
|
1558
1618
|
assignmentId: z.string(),
|
package/src/routers/auth.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { prisma } from "../lib/prisma";
|
|
|
5
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
6
|
import { compare, hash } from "bcryptjs";
|
|
7
7
|
import { transport } from "../utils/email";
|
|
8
|
+
import { prismaWrapper } from "../utils/prismaWrapper";
|
|
8
9
|
|
|
9
10
|
const loginSchema = z.object({
|
|
10
11
|
username: z.string(),
|
|
@@ -28,20 +29,23 @@ export const authRouter = createTRPCRouter({
|
|
|
28
29
|
const { username, email, password } = input;
|
|
29
30
|
|
|
30
31
|
// Check if username already exists
|
|
31
|
-
const existingUser = await
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
const existingUser = await prismaWrapper.findFirst(
|
|
33
|
+
() => prisma.user.findFirst({
|
|
34
|
+
where: {
|
|
35
|
+
OR: [
|
|
36
|
+
{ username },
|
|
37
|
+
{ email }
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
select: {
|
|
41
|
+
id: true,
|
|
42
|
+
username: true,
|
|
43
|
+
email: true,
|
|
44
|
+
verified: true,
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
|
+
'checking for existing user during registration'
|
|
48
|
+
);
|
|
45
49
|
|
|
46
50
|
if (existingUser && existingUser.verified) {
|
|
47
51
|
if (existingUser.username === username) {
|
|
@@ -57,44 +61,59 @@ export const authRouter = createTRPCRouter({
|
|
|
57
61
|
});
|
|
58
62
|
}
|
|
59
63
|
} else if (existingUser && !existingUser.verified) {
|
|
60
|
-
await
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
await prismaWrapper.deleteMany(
|
|
65
|
+
() => prisma.session.deleteMany({
|
|
66
|
+
where: { userId: existingUser.id },
|
|
67
|
+
}),
|
|
68
|
+
'deleting existing sessions for unverified user'
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
await prismaWrapper.delete(
|
|
72
|
+
() => prisma.user.delete({
|
|
73
|
+
where: { id: existingUser.id },
|
|
74
|
+
}),
|
|
75
|
+
'deleting unverified user'
|
|
76
|
+
);
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
// Create new user
|
|
70
|
-
const user = await
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const user = await prismaWrapper.create(
|
|
81
|
+
async () => await prisma.user.create({
|
|
82
|
+
data: {
|
|
83
|
+
username,
|
|
84
|
+
email,
|
|
85
|
+
password: await hash(password, 10),
|
|
86
|
+
profile: {},
|
|
87
|
+
verified: true, // temporary
|
|
88
|
+
},
|
|
89
|
+
select: {
|
|
90
|
+
id: true,
|
|
91
|
+
username: true,
|
|
92
|
+
email: true,
|
|
93
|
+
}
|
|
94
|
+
}),
|
|
95
|
+
'creating new user during registration'
|
|
96
|
+
);
|
|
83
97
|
|
|
84
|
-
const verificationToken = await
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
const verificationToken = await prismaWrapper.create(
|
|
99
|
+
() => prisma.session.create({
|
|
100
|
+
data: {
|
|
101
|
+
id: uuidv4(),
|
|
102
|
+
userId: user.id,
|
|
103
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
104
|
+
},
|
|
105
|
+
}),
|
|
106
|
+
'creating verification token'
|
|
107
|
+
);
|
|
91
108
|
|
|
92
|
-
await transport.sendMail({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
109
|
+
// await transport.sendMail({
|
|
110
|
+
// from: 'noreply@studious.sh',
|
|
111
|
+
// to: user.email,
|
|
112
|
+
// subject: 'Verify your email',
|
|
113
|
+
// text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
|
|
114
|
+
// });
|
|
115
|
+
|
|
116
|
+
console.log(`${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`)
|
|
98
117
|
|
|
99
118
|
return {
|
|
100
119
|
user: {
|
|
@@ -127,7 +146,7 @@ export const authRouter = createTRPCRouter({
|
|
|
127
146
|
});
|
|
128
147
|
}
|
|
129
148
|
|
|
130
|
-
if (await compare(password, user.password)) {
|
|
149
|
+
if (!(await compare(password, user.password))) {
|
|
131
150
|
throw new TRPCError({
|
|
132
151
|
code: "UNAUTHORIZED",
|
|
133
152
|
message: "Invalid username or password",
|
|
@@ -218,6 +237,8 @@ export const authRouter = createTRPCRouter({
|
|
|
218
237
|
select: {
|
|
219
238
|
id: true,
|
|
220
239
|
email: true,
|
|
240
|
+
role: true,
|
|
241
|
+
schoolId: true,
|
|
221
242
|
},
|
|
222
243
|
});
|
|
223
244
|
|
|
@@ -240,12 +261,12 @@ export const authRouter = createTRPCRouter({
|
|
|
240
261
|
},
|
|
241
262
|
});
|
|
242
263
|
|
|
243
|
-
await transport.sendMail({
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
264
|
+
// await transport.sendMail({
|
|
265
|
+
// from: 'noreply@studious.sh',
|
|
266
|
+
// to: user.email,
|
|
267
|
+
// subject: 'Verify your email',
|
|
268
|
+
// text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
|
|
269
|
+
// });
|
|
249
270
|
|
|
250
271
|
return { success: true };
|
|
251
272
|
}),
|
|
@@ -281,6 +302,11 @@ export const authRouter = createTRPCRouter({
|
|
|
281
302
|
},
|
|
282
303
|
});
|
|
283
304
|
|
|
305
|
+
// Clean up the verification token
|
|
306
|
+
await prisma.session.delete({
|
|
307
|
+
where: { id: token },
|
|
308
|
+
});
|
|
309
|
+
|
|
284
310
|
return { success: true };
|
|
285
311
|
}),
|
|
286
312
|
});
|