@studious-lms/server 1.0.2 → 1.0.3
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/package.json +1 -6
- package/prisma/schema.prisma +228 -0
- package/src/exportType.ts +9 -0
- package/src/index.ts +94 -0
- package/src/lib/fileUpload.ts +163 -0
- package/src/lib/googleCloudStorage.ts +94 -0
- package/src/lib/prisma.ts +16 -0
- package/src/lib/thumbnailGenerator.ts +185 -0
- package/src/logger.ts +163 -0
- package/src/middleware/auth.ts +191 -0
- package/src/middleware/logging.ts +54 -0
- package/src/routers/_app.ts +34 -0
- package/src/routers/agenda.ts +79 -0
- package/src/routers/announcement.ts +134 -0
- package/src/routers/assignment.ts +1614 -0
- package/src/routers/attendance.ts +284 -0
- package/src/routers/auth.ts +286 -0
- package/src/routers/class.ts +753 -0
- package/src/routers/event.ts +509 -0
- package/src/routers/file.ts +96 -0
- package/src/routers/section.ts +138 -0
- package/src/routers/user.ts +82 -0
- package/src/socket/handlers.ts +143 -0
- package/src/trpc.ts +90 -0
- package/src/types/trpc.ts +15 -0
- package/src/utils/email.ts +11 -0
- package/src/utils/generateInviteCode.ts +8 -0
- package/src/utils/logger.ts +156 -0
- package/tsconfig.json +17 -0
- package/generated/prisma/client.d.ts +0 -1
- package/generated/prisma/client.js +0 -4
- package/generated/prisma/default.d.ts +0 -1
- package/generated/prisma/default.js +0 -4
- package/generated/prisma/edge.d.ts +0 -1
- package/generated/prisma/edge.js +0 -389
- package/generated/prisma/index-browser.js +0 -375
- package/generated/prisma/index.d.ts +0 -34865
- package/generated/prisma/index.js +0 -410
- package/generated/prisma/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/generated/prisma/package.json +0 -140
- package/generated/prisma/runtime/edge-esm.js +0 -34
- package/generated/prisma/runtime/edge.js +0 -34
- package/generated/prisma/runtime/index-browser.d.ts +0 -370
- package/generated/prisma/runtime/index-browser.js +0 -16
- package/generated/prisma/runtime/library.d.ts +0 -3647
- package/generated/prisma/runtime/library.js +0 -146
- package/generated/prisma/runtime/react-native.js +0 -83
- package/generated/prisma/runtime/wasm.js +0 -35
- package/generated/prisma/schema.prisma +0 -304
- package/generated/prisma/wasm.d.ts +0 -1
- package/generated/prisma/wasm.js +0 -375
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
|
3
|
+
import { TRPCError } from "@trpc/server";
|
|
4
|
+
import { prisma } from "../lib/prisma";
|
|
5
|
+
|
|
6
|
+
const attendanceSchema = z.object({
|
|
7
|
+
eventId: z.string().optional(),
|
|
8
|
+
present: z.array(z.object({ id: z.string(), username: z.string() })),
|
|
9
|
+
late: z.array(z.object({ id: z.string(), username: z.string() })),
|
|
10
|
+
absent: z.array(z.object({ id: z.string(), username: z.string() })),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const attendanceRouter = createTRPCRouter({
|
|
14
|
+
get: protectedProcedure
|
|
15
|
+
.input(z.object({
|
|
16
|
+
classId: z.string(),
|
|
17
|
+
eventId: z.string().optional(),
|
|
18
|
+
}))
|
|
19
|
+
.query(async ({ ctx, input }) => {
|
|
20
|
+
if (!ctx.user) {
|
|
21
|
+
throw new TRPCError({
|
|
22
|
+
code: "UNAUTHORIZED",
|
|
23
|
+
message: "You must be logged in to view attendance",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if user is a teacher or student of the class
|
|
28
|
+
const classData = await prisma.class.findUnique({
|
|
29
|
+
where: {
|
|
30
|
+
id: input.classId,
|
|
31
|
+
OR: [
|
|
32
|
+
{
|
|
33
|
+
teachers: {
|
|
34
|
+
some: {
|
|
35
|
+
id: ctx.user.id,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
students: {
|
|
41
|
+
some: {
|
|
42
|
+
id: ctx.user.id,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
select: {
|
|
49
|
+
students: {
|
|
50
|
+
select: {
|
|
51
|
+
id: true,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!classData) {
|
|
58
|
+
throw new TRPCError({
|
|
59
|
+
code: "UNAUTHORIZED",
|
|
60
|
+
message: "You are not authorized to view this class's attendance",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// check each event has an attendance, if not create one
|
|
65
|
+
const events = await prisma.event.findMany({
|
|
66
|
+
where: {
|
|
67
|
+
classId: input.classId,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
for (const event of events) {
|
|
72
|
+
const attendance = await prisma.attendance.findFirst({
|
|
73
|
+
where: {
|
|
74
|
+
eventId: event.id,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!attendance) {
|
|
79
|
+
await prisma.attendance.create({
|
|
80
|
+
data: {
|
|
81
|
+
event: {
|
|
82
|
+
connect: {
|
|
83
|
+
id: event.id,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
class: {
|
|
87
|
+
connect: {
|
|
88
|
+
id: input.classId,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
present: {
|
|
92
|
+
connect: classData.students.map(student => ({ id: student.id })),
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const attendance = await prisma.attendance.findMany({
|
|
101
|
+
where: {
|
|
102
|
+
classId: input.classId,
|
|
103
|
+
...(input.eventId ? { eventId: input.eventId } : {}),
|
|
104
|
+
},
|
|
105
|
+
include: {
|
|
106
|
+
event: {
|
|
107
|
+
select: {
|
|
108
|
+
id: true,
|
|
109
|
+
name: true,
|
|
110
|
+
startTime: true,
|
|
111
|
+
endTime: true,
|
|
112
|
+
location: true,
|
|
113
|
+
color: true,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
present: {
|
|
117
|
+
select: {
|
|
118
|
+
id: true,
|
|
119
|
+
username: true,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
late: {
|
|
123
|
+
select: {
|
|
124
|
+
id: true,
|
|
125
|
+
username: true,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
absent: {
|
|
129
|
+
select: {
|
|
130
|
+
id: true,
|
|
131
|
+
username: true,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
orderBy: {
|
|
136
|
+
date: "desc",
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return attendance;
|
|
141
|
+
}),
|
|
142
|
+
|
|
143
|
+
update: protectedProcedure
|
|
144
|
+
.input(z.object({
|
|
145
|
+
classId: z.string(),
|
|
146
|
+
eventId: z.string().optional(),
|
|
147
|
+
attendance: attendanceSchema,
|
|
148
|
+
}))
|
|
149
|
+
.mutation(async ({ ctx, input }) => {
|
|
150
|
+
if (!ctx.user) {
|
|
151
|
+
throw new TRPCError({
|
|
152
|
+
code: "UNAUTHORIZED",
|
|
153
|
+
message: "You must be logged in to update attendance",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Check if user is a teacher of the class
|
|
158
|
+
const classData = await prisma.class.findUnique({
|
|
159
|
+
where: {
|
|
160
|
+
id: input.classId,
|
|
161
|
+
teachers: {
|
|
162
|
+
some: {
|
|
163
|
+
id: ctx.user.id,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (!classData) {
|
|
170
|
+
throw new TRPCError({
|
|
171
|
+
code: "UNAUTHORIZED",
|
|
172
|
+
message: "You are not authorized to update this class's attendance",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check if attendance record exists
|
|
177
|
+
const existingAttendance = await prisma.attendance.findFirst({
|
|
178
|
+
where: {
|
|
179
|
+
classId: input.classId,
|
|
180
|
+
eventId: input.eventId,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (!existingAttendance) {
|
|
185
|
+
// Create new attendance record
|
|
186
|
+
const attendance = await prisma.attendance.create({
|
|
187
|
+
data: {
|
|
188
|
+
classId: input.classId,
|
|
189
|
+
eventId: input.eventId,
|
|
190
|
+
date: new Date(),
|
|
191
|
+
present: {
|
|
192
|
+
connect: input.attendance.present.map(student => ({ id: student.id })),
|
|
193
|
+
},
|
|
194
|
+
late: {
|
|
195
|
+
connect: input.attendance.late.map(student => ({ id: student.id })),
|
|
196
|
+
},
|
|
197
|
+
absent: {
|
|
198
|
+
connect: input.attendance.absent.map(student => ({ id: student.id })),
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
include: {
|
|
202
|
+
event: {
|
|
203
|
+
select: {
|
|
204
|
+
id: true,
|
|
205
|
+
name: true,
|
|
206
|
+
startTime: true,
|
|
207
|
+
endTime: true,
|
|
208
|
+
location: true,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
present: {
|
|
212
|
+
select: {
|
|
213
|
+
id: true,
|
|
214
|
+
username: true,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
late: {
|
|
218
|
+
select: {
|
|
219
|
+
id: true,
|
|
220
|
+
username: true,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
absent: {
|
|
224
|
+
select: {
|
|
225
|
+
id: true,
|
|
226
|
+
username: true,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return attendance;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Update existing attendance record
|
|
236
|
+
const attendance = await prisma.attendance.update({
|
|
237
|
+
where: {
|
|
238
|
+
id: existingAttendance.id,
|
|
239
|
+
},
|
|
240
|
+
data: {
|
|
241
|
+
present: {
|
|
242
|
+
set: input.attendance.present.map(student => ({ id: student.id })),
|
|
243
|
+
},
|
|
244
|
+
late: {
|
|
245
|
+
set: input.attendance.late.map(student => ({ id: student.id })),
|
|
246
|
+
},
|
|
247
|
+
absent: {
|
|
248
|
+
set: input.attendance.absent.map(student => ({ id: student.id })),
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
include: {
|
|
252
|
+
event: {
|
|
253
|
+
select: {
|
|
254
|
+
id: true,
|
|
255
|
+
name: true,
|
|
256
|
+
startTime: true,
|
|
257
|
+
endTime: true,
|
|
258
|
+
location: true,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
present: {
|
|
262
|
+
select: {
|
|
263
|
+
id: true,
|
|
264
|
+
username: true,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
late: {
|
|
268
|
+
select: {
|
|
269
|
+
id: true,
|
|
270
|
+
username: true,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
absent: {
|
|
274
|
+
select: {
|
|
275
|
+
id: true,
|
|
276
|
+
username: true,
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return attendance;
|
|
283
|
+
}),
|
|
284
|
+
});
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
|
3
|
+
import { TRPCError } from "@trpc/server";
|
|
4
|
+
import { prisma } from "../lib/prisma";
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
import { compare, hash } from "bcryptjs";
|
|
7
|
+
import { transport } from "../utils/email";
|
|
8
|
+
|
|
9
|
+
const loginSchema = z.object({
|
|
10
|
+
username: z.string(),
|
|
11
|
+
password: z.string(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const registerSchema = z.object({
|
|
15
|
+
username: z.string().min(3, "Username must be at least 3 characters"),
|
|
16
|
+
email: z.string().email("Invalid email address"),
|
|
17
|
+
password: z.string().min(6, "Password must be at least 6 characters"),
|
|
18
|
+
confirmPassword: z.string(),
|
|
19
|
+
}).refine((data) => data.password === data.confirmPassword, {
|
|
20
|
+
message: "Passwords don't match",
|
|
21
|
+
path: ["confirmPassword"],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const authRouter = createTRPCRouter({
|
|
25
|
+
register: publicProcedure
|
|
26
|
+
.input(registerSchema)
|
|
27
|
+
.mutation(async ({ input }) => {
|
|
28
|
+
const { username, email, password } = input;
|
|
29
|
+
|
|
30
|
+
// Check if username already exists
|
|
31
|
+
const existingUser = await prisma.user.findFirst({
|
|
32
|
+
where: {
|
|
33
|
+
OR: [
|
|
34
|
+
{ username },
|
|
35
|
+
{ email }
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
select: {
|
|
39
|
+
id: true,
|
|
40
|
+
username: true,
|
|
41
|
+
email: true,
|
|
42
|
+
verified: true,
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (existingUser && existingUser.verified) {
|
|
47
|
+
if (existingUser.username === username) {
|
|
48
|
+
throw new TRPCError({
|
|
49
|
+
code: "CONFLICT",
|
|
50
|
+
message: "Username already exists",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (existingUser.email === email) {
|
|
54
|
+
throw new TRPCError({
|
|
55
|
+
code: "CONFLICT",
|
|
56
|
+
message: "Email already exists",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
} else if (existingUser && !existingUser.verified) {
|
|
60
|
+
await prisma.session.deleteMany({
|
|
61
|
+
where: { userId: existingUser.id },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await prisma.user.delete({
|
|
65
|
+
where: { id: existingUser.id },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Create new user
|
|
70
|
+
const user = await prisma.user.create({
|
|
71
|
+
data: {
|
|
72
|
+
username,
|
|
73
|
+
email,
|
|
74
|
+
password: await hash(password, 10),
|
|
75
|
+
profile: {},
|
|
76
|
+
},
|
|
77
|
+
select: {
|
|
78
|
+
id: true,
|
|
79
|
+
username: true,
|
|
80
|
+
email: true,
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const verificationToken = await prisma.session.create({
|
|
85
|
+
data: {
|
|
86
|
+
id: uuidv4(),
|
|
87
|
+
userId: user.id,
|
|
88
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await transport.sendMail({
|
|
93
|
+
from: 'noreply@studious.sh',
|
|
94
|
+
to: user.email,
|
|
95
|
+
subject: 'Verify your email',
|
|
96
|
+
text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
user: {
|
|
101
|
+
id: user.id,
|
|
102
|
+
username: user.username,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}),
|
|
106
|
+
|
|
107
|
+
login: publicProcedure
|
|
108
|
+
.input(loginSchema)
|
|
109
|
+
.mutation(async ({ input }) => {
|
|
110
|
+
const { username, password } = input;
|
|
111
|
+
|
|
112
|
+
const user = await prisma.user.findFirst({
|
|
113
|
+
where: { username },
|
|
114
|
+
select: {
|
|
115
|
+
id: true,
|
|
116
|
+
username: true,
|
|
117
|
+
password: true,
|
|
118
|
+
email: true,
|
|
119
|
+
verified: true,
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (!user) {
|
|
124
|
+
throw new TRPCError({
|
|
125
|
+
code: "UNAUTHORIZED",
|
|
126
|
+
message: "Invalid username or password",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (await compare(password, user.password)) {
|
|
131
|
+
throw new TRPCError({
|
|
132
|
+
code: "UNAUTHORIZED",
|
|
133
|
+
message: "Invalid username or password",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!user.verified) {
|
|
138
|
+
return {
|
|
139
|
+
verified: false,
|
|
140
|
+
user: {
|
|
141
|
+
email: user.email,
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Create a new session
|
|
147
|
+
const session = await prisma.session.create({
|
|
148
|
+
data: {
|
|
149
|
+
id: uuidv4(),
|
|
150
|
+
userId: user.id,
|
|
151
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
token: session.id,
|
|
157
|
+
user: {
|
|
158
|
+
id: user.id,
|
|
159
|
+
username: user.username,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}),
|
|
163
|
+
|
|
164
|
+
logout: publicProcedure
|
|
165
|
+
.mutation(async ({ ctx }) => {
|
|
166
|
+
if (!ctx.user) {
|
|
167
|
+
throw new TRPCError({
|
|
168
|
+
code: "UNAUTHORIZED",
|
|
169
|
+
message: "Not authenticated",
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Delete the current session
|
|
174
|
+
await prisma.session.deleteMany({
|
|
175
|
+
where: { userId: ctx.user.id },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return { success: true };
|
|
179
|
+
}),
|
|
180
|
+
|
|
181
|
+
check: protectedProcedure
|
|
182
|
+
.query(async ({ ctx }) => {
|
|
183
|
+
if (!ctx.user) {
|
|
184
|
+
throw new TRPCError({
|
|
185
|
+
code: "UNAUTHORIZED",
|
|
186
|
+
message: "Not authenticated",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const user = await prisma.user.findUnique({
|
|
191
|
+
where: { id: ctx.user.id },
|
|
192
|
+
select: {
|
|
193
|
+
id: true,
|
|
194
|
+
username: true,
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (!user) {
|
|
199
|
+
throw new TRPCError({
|
|
200
|
+
code: "NOT_FOUND",
|
|
201
|
+
message: "User not found",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {user};
|
|
206
|
+
}),
|
|
207
|
+
resendVerificationEmail: publicProcedure
|
|
208
|
+
.input(z.object({
|
|
209
|
+
email: z.string().email(),
|
|
210
|
+
}))
|
|
211
|
+
.mutation(async ({ input }) => {
|
|
212
|
+
const { email } = input;
|
|
213
|
+
|
|
214
|
+
const user = await prisma.user.findFirst({
|
|
215
|
+
where: {
|
|
216
|
+
email,
|
|
217
|
+
},
|
|
218
|
+
select: {
|
|
219
|
+
id: true,
|
|
220
|
+
email: true,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (!user) {
|
|
225
|
+
throw new TRPCError({
|
|
226
|
+
code: "NOT_FOUND",
|
|
227
|
+
message: "User not found",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
await prisma.session.deleteMany({
|
|
232
|
+
where: { userId: user?.id },
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const verificationToken = await prisma.session.create({
|
|
236
|
+
data: {
|
|
237
|
+
id: uuidv4(),
|
|
238
|
+
userId: user.id,
|
|
239
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await transport.sendMail({
|
|
244
|
+
from: 'noreply@studious.sh',
|
|
245
|
+
to: user.email,
|
|
246
|
+
subject: 'Verify your email',
|
|
247
|
+
text: `Click the link to verify your email: ${process.env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return { success: true };
|
|
251
|
+
}),
|
|
252
|
+
verify: publicProcedure
|
|
253
|
+
.input(z.object({
|
|
254
|
+
token: z.string(),
|
|
255
|
+
}))
|
|
256
|
+
.mutation(async ({ input }) => {
|
|
257
|
+
const { token } = input;
|
|
258
|
+
|
|
259
|
+
const session = await prisma.session.findUnique({
|
|
260
|
+
where: { id: token },
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (!session) {
|
|
264
|
+
throw new TRPCError({
|
|
265
|
+
code: "NOT_FOUND",
|
|
266
|
+
message: "Session not found",
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (session.expiresAt && session.expiresAt < new Date()) {
|
|
271
|
+
throw new TRPCError({
|
|
272
|
+
code: "UNAUTHORIZED",
|
|
273
|
+
message: "Session expired",
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await prisma.user.update({
|
|
278
|
+
where: { id: session.userId! },
|
|
279
|
+
data: {
|
|
280
|
+
verified: true,
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return { success: true };
|
|
285
|
+
}),
|
|
286
|
+
});
|