@studious-lms/server 1.0.4 → 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.
Files changed (101) hide show
  1. package/API_SPECIFICATION.md +1117 -0
  2. package/dist/exportType.js +1 -2
  3. package/dist/index.js +25 -30
  4. package/dist/lib/fileUpload.d.ts.map +1 -1
  5. package/dist/lib/fileUpload.js +31 -29
  6. package/dist/lib/googleCloudStorage.js +9 -14
  7. package/dist/lib/prisma.js +4 -7
  8. package/dist/lib/thumbnailGenerator.js +12 -20
  9. package/dist/middleware/auth.d.ts.map +1 -1
  10. package/dist/middleware/auth.js +17 -22
  11. package/dist/middleware/logging.js +5 -9
  12. package/dist/routers/_app.d.ts +3483 -1801
  13. package/dist/routers/_app.d.ts.map +1 -1
  14. package/dist/routers/_app.js +28 -27
  15. package/dist/routers/agenda.d.ts +13 -8
  16. package/dist/routers/agenda.d.ts.map +1 -1
  17. package/dist/routers/agenda.js +14 -17
  18. package/dist/routers/announcement.d.ts +4 -3
  19. package/dist/routers/announcement.d.ts.map +1 -1
  20. package/dist/routers/announcement.js +28 -31
  21. package/dist/routers/assignment.d.ts +282 -196
  22. package/dist/routers/assignment.d.ts.map +1 -1
  23. package/dist/routers/assignment.js +256 -202
  24. package/dist/routers/attendance.d.ts +5 -4
  25. package/dist/routers/attendance.d.ts.map +1 -1
  26. package/dist/routers/attendance.js +31 -34
  27. package/dist/routers/auth.d.ts +1 -0
  28. package/dist/routers/auth.d.ts.map +1 -1
  29. package/dist/routers/auth.js +80 -75
  30. package/dist/routers/class.d.ts +284 -14
  31. package/dist/routers/class.d.ts.map +1 -1
  32. package/dist/routers/class.js +435 -164
  33. package/dist/routers/event.d.ts +47 -38
  34. package/dist/routers/event.d.ts.map +1 -1
  35. package/dist/routers/event.js +76 -79
  36. package/dist/routers/file.d.ts +71 -1
  37. package/dist/routers/file.d.ts.map +1 -1
  38. package/dist/routers/file.js +267 -32
  39. package/dist/routers/folder.d.ts +296 -0
  40. package/dist/routers/folder.d.ts.map +1 -0
  41. package/dist/routers/folder.js +693 -0
  42. package/dist/routers/notifications.d.ts +103 -0
  43. package/dist/routers/notifications.d.ts.map +1 -0
  44. package/dist/routers/notifications.js +91 -0
  45. package/dist/routers/school.d.ts +208 -0
  46. package/dist/routers/school.d.ts.map +1 -0
  47. package/dist/routers/school.js +481 -0
  48. package/dist/routers/section.d.ts +1 -0
  49. package/dist/routers/section.d.ts.map +1 -1
  50. package/dist/routers/section.js +30 -33
  51. package/dist/routers/user.d.ts +2 -1
  52. package/dist/routers/user.d.ts.map +1 -1
  53. package/dist/routers/user.js +21 -24
  54. package/dist/seedDatabase.d.ts +22 -0
  55. package/dist/seedDatabase.d.ts.map +1 -0
  56. package/dist/seedDatabase.js +57 -0
  57. package/dist/socket/handlers.js +26 -30
  58. package/dist/trpc.d.ts +5 -0
  59. package/dist/trpc.d.ts.map +1 -1
  60. package/dist/trpc.js +35 -26
  61. package/dist/types/trpc.js +1 -2
  62. package/dist/utils/email.js +2 -8
  63. package/dist/utils/generateInviteCode.js +1 -5
  64. package/dist/utils/logger.d.ts.map +1 -1
  65. package/dist/utils/logger.js +13 -9
  66. package/dist/utils/prismaErrorHandler.d.ts +9 -0
  67. package/dist/utils/prismaErrorHandler.d.ts.map +1 -0
  68. package/dist/utils/prismaErrorHandler.js +234 -0
  69. package/dist/utils/prismaWrapper.d.ts +14 -0
  70. package/dist/utils/prismaWrapper.d.ts.map +1 -0
  71. package/dist/utils/prismaWrapper.js +64 -0
  72. package/package.json +17 -4
  73. package/prisma/migrations/20250807062924_init/migration.sql +436 -0
  74. package/prisma/migrations/migration_lock.toml +3 -0
  75. package/prisma/schema.prisma +67 -0
  76. package/src/index.ts +2 -2
  77. package/src/lib/fileUpload.ts +16 -7
  78. package/src/middleware/auth.ts +0 -2
  79. package/src/routers/_app.ts +5 -1
  80. package/src/routers/assignment.ts +82 -22
  81. package/src/routers/auth.ts +80 -54
  82. package/src/routers/class.ts +330 -36
  83. package/src/routers/file.ts +283 -20
  84. package/src/routers/folder.ts +755 -0
  85. package/src/routers/notifications.ts +93 -0
  86. package/src/seedDatabase.ts +66 -0
  87. package/src/socket/handlers.ts +4 -4
  88. package/src/trpc.ts +13 -0
  89. package/src/utils/logger.ts +14 -4
  90. package/src/utils/prismaErrorHandler.ts +275 -0
  91. package/src/utils/prismaWrapper.ts +91 -0
  92. package/tests/auth.test.ts +25 -0
  93. package/tests/class.test.ts +281 -0
  94. package/tests/setup.ts +98 -0
  95. package/tests/startup.test.ts +5 -0
  96. package/tsconfig.json +2 -1
  97. package/vitest.config.ts +11 -0
  98. package/dist/logger.d.ts +0 -26
  99. package/dist/logger.d.ts.map +0 -1
  100. package/dist/logger.js +0 -135
  101. package/src/logger.ts +0 -163
@@ -0,0 +1,481 @@
1
+ import { z } from 'zod';
2
+ import { createTRPCRouter, protectedProcedure } from '../trpc';
3
+ import { prisma } from '../lib/prisma';
4
+ import { TRPCError } from '@trpc/server';
5
+ import { UserRole } from '@prisma/client';
6
+ import { transport } from '../utils/email';
7
+ import { generateInviteCode } from '../utils/generateInviteCode';
8
+ import { hash } from 'bcryptjs';
9
+ export const schoolRouter = createTRPCRouter({
10
+ // Create a new school
11
+ createSchool: protectedProcedure
12
+ .input(z.object({
13
+ name: z.string().min(1),
14
+ subdomain: z.string().regex(/^[a-z0-9-]+$/, 'Subdomain can only contain lowercase letters, numbers, and hyphens')
15
+ }))
16
+ .mutation(async ({ input, ctx }) => {
17
+ if (!ctx.user) {
18
+ throw new TRPCError({
19
+ code: 'UNAUTHORIZED',
20
+ message: 'User must be authenticated'
21
+ });
22
+ }
23
+ // Check if subdomain is already taken
24
+ const existingSchool = await prisma.school.findUnique({
25
+ where: { subdomain: input.subdomain }
26
+ });
27
+ if (existingSchool) {
28
+ throw new TRPCError({
29
+ code: 'CONFLICT',
30
+ message: 'This subdomain is already taken'
31
+ });
32
+ }
33
+ // Create a placeholder file for the logo
34
+ const placeholderLogo = await prisma.file.create({
35
+ data: {
36
+ name: 'placeholder-logo',
37
+ path: '/placeholder-logo.png',
38
+ type: 'image/png',
39
+ userId: ctx.user.id
40
+ }
41
+ });
42
+ // Create the school
43
+ const school = await prisma.school.create({
44
+ data: {
45
+ name: input.name,
46
+ subdomain: input.subdomain,
47
+ logoId: placeholderLogo.id,
48
+ users: {
49
+ connect: { id: ctx.user.id }
50
+ }
51
+ }
52
+ });
53
+ // Update the user to be an admin of this school
54
+ await prisma.user.update({
55
+ where: { id: ctx.user.id },
56
+ data: {
57
+ role: UserRole.ADMIN,
58
+ schoolId: school.id
59
+ }
60
+ });
61
+ return school;
62
+ }),
63
+ // Check if user is admin of the school
64
+ checkAdmin: protectedProcedure
65
+ .input(z.object({ schoolId: z.string() }))
66
+ .query(async ({ input, ctx }) => {
67
+ if (!ctx.user) {
68
+ throw new TRPCError({
69
+ code: 'UNAUTHORIZED',
70
+ message: 'User must be authenticated'
71
+ });
72
+ }
73
+ const user = await prisma.user.findFirst({
74
+ where: {
75
+ id: ctx.user.id,
76
+ schoolId: input.schoolId,
77
+ role: UserRole.ADMIN
78
+ }
79
+ });
80
+ if (!user) {
81
+ throw new TRPCError({
82
+ code: 'FORBIDDEN',
83
+ message: 'You must be a school admin to access this'
84
+ });
85
+ }
86
+ return true;
87
+ }),
88
+ // Get all users in a school
89
+ getUsers: protectedProcedure
90
+ .input(z.object({
91
+ schoolId: z.string(),
92
+ role: z.enum(['STUDENT', 'TEACHER', 'ADMIN', 'NONE']).optional()
93
+ }))
94
+ .query(async ({ input, ctx }) => {
95
+ if (!ctx.user) {
96
+ throw new TRPCError({
97
+ code: 'UNAUTHORIZED',
98
+ message: 'User must be authenticated'
99
+ });
100
+ }
101
+ // Check admin permission
102
+ const isAdmin = await prisma.user.findFirst({
103
+ where: {
104
+ id: ctx.user.id,
105
+ schoolId: input.schoolId,
106
+ role: UserRole.ADMIN
107
+ }
108
+ });
109
+ if (!isAdmin) {
110
+ throw new TRPCError({ code: 'FORBIDDEN' });
111
+ }
112
+ return prisma.user.findMany({
113
+ where: {
114
+ schoolId: input.schoolId,
115
+ ...(input.role && { role: input.role })
116
+ },
117
+ select: {
118
+ id: true,
119
+ username: true,
120
+ email: true,
121
+ role: true,
122
+ verified: true,
123
+ profile: true
124
+ },
125
+ orderBy: [
126
+ { role: 'asc' },
127
+ { username: 'asc' }
128
+ ]
129
+ });
130
+ }),
131
+ // Create a new user
132
+ createUser: protectedProcedure
133
+ .input(z.object({
134
+ schoolId: z.string(),
135
+ email: z.string().email(),
136
+ username: z.string().min(3),
137
+ role: z.enum(['STUDENT', 'TEACHER', 'ADMIN', 'NONE']),
138
+ sendInvite: z.boolean().default(true)
139
+ }))
140
+ .mutation(async ({ input, ctx }) => {
141
+ // Check admin permission
142
+ const isAdmin = await prisma.user.findFirst({
143
+ where: {
144
+ id: ctx.user.id,
145
+ schoolId: input.schoolId,
146
+ role: UserRole.ADMIN
147
+ }
148
+ });
149
+ if (!isAdmin) {
150
+ throw new TRPCError({ code: 'FORBIDDEN' });
151
+ }
152
+ // Check if user already exists
153
+ const existingUser = await prisma.user.findFirst({
154
+ where: {
155
+ OR: [
156
+ { email: input.email },
157
+ { username: input.username }
158
+ ]
159
+ }
160
+ });
161
+ if (existingUser) {
162
+ throw new TRPCError({
163
+ code: 'CONFLICT',
164
+ message: 'User with this email or username already exists'
165
+ });
166
+ }
167
+ // Generate temporary password
168
+ const tempPassword = generateInviteCode();
169
+ const hashedPassword = await hash(tempPassword, 10);
170
+ const user = await prisma.user.create({
171
+ data: {
172
+ email: input.email,
173
+ username: input.username,
174
+ password: hashedPassword,
175
+ role: input.role,
176
+ schoolId: input.schoolId,
177
+ verified: false
178
+ }
179
+ });
180
+ // Send invite email
181
+ if (input.sendInvite) {
182
+ await transport.sendMail({
183
+ from: process.env.EMAIL_FROM || 'noreply@studious.app',
184
+ to: input.email,
185
+ subject: 'Welcome to Studious',
186
+ text: `You have been invited to join Studious. Your temporary password is: ${tempPassword}\n\nPlease change your password after logging in.`
187
+ });
188
+ }
189
+ return user;
190
+ }),
191
+ // Bulk create users from JSON
192
+ bulkCreateUsers: protectedProcedure
193
+ .input(z.object({
194
+ schoolId: z.string(),
195
+ users: z.array(z.object({
196
+ email: z.string().email(),
197
+ username: z.string().min(3),
198
+ role: z.enum(['STUDENT', 'TEACHER', 'ADMIN', 'NONE'])
199
+ }))
200
+ }))
201
+ .mutation(async ({ input, ctx }) => {
202
+ if (!ctx.user) {
203
+ throw new TRPCError({
204
+ code: 'UNAUTHORIZED',
205
+ message: 'User must be authenticated'
206
+ });
207
+ }
208
+ // Check admin permission
209
+ const isAdmin = await prisma.user.findFirst({
210
+ where: {
211
+ id: ctx.user.id,
212
+ schoolId: input.schoolId,
213
+ role: UserRole.ADMIN
214
+ }
215
+ });
216
+ if (!isAdmin) {
217
+ throw new TRPCError({ code: 'FORBIDDEN' });
218
+ }
219
+ const results = [];
220
+ const errors = [];
221
+ for (const userData of input.users) {
222
+ try {
223
+ const tempPassword = generateInviteCode();
224
+ const hashedPassword = await hash(tempPassword, 10);
225
+ const user = await prisma.user.create({
226
+ data: {
227
+ email: userData.email,
228
+ username: userData.username,
229
+ password: hashedPassword,
230
+ role: userData.role,
231
+ schoolId: input.schoolId,
232
+ verified: false
233
+ }
234
+ });
235
+ // Send invite email
236
+ await transport.sendMail({
237
+ from: process.env.EMAIL_FROM || 'noreply@studious.app',
238
+ to: userData.email,
239
+ subject: 'Welcome to Studious',
240
+ text: `You have been invited to join Studious. Your temporary password is: ${tempPassword}\n\nPlease change your password after logging in.`
241
+ });
242
+ results.push({ success: true, user });
243
+ }
244
+ catch (error) {
245
+ errors.push({
246
+ email: userData.email,
247
+ error: error instanceof Error ? error.message : 'Unknown error'
248
+ });
249
+ }
250
+ }
251
+ return { results, errors };
252
+ }),
253
+ // Update user
254
+ updateUser: protectedProcedure
255
+ .input(z.object({
256
+ userId: z.string(),
257
+ schoolId: z.string(),
258
+ role: z.enum(['STUDENT', 'TEACHER', 'ADMIN', 'NONE']).optional(),
259
+ verified: z.boolean().optional()
260
+ }))
261
+ .mutation(async ({ input, ctx }) => {
262
+ if (!ctx.user) {
263
+ throw new TRPCError({
264
+ code: 'UNAUTHORIZED',
265
+ message: 'User must be authenticated'
266
+ });
267
+ }
268
+ // Check admin permission
269
+ const isAdmin = await prisma.user.findFirst({
270
+ where: {
271
+ id: ctx.user.id,
272
+ schoolId: input.schoolId,
273
+ role: UserRole.ADMIN
274
+ }
275
+ });
276
+ if (!isAdmin) {
277
+ throw new TRPCError({ code: 'FORBIDDEN' });
278
+ }
279
+ return prisma.user.update({
280
+ where: { id: input.userId },
281
+ data: {
282
+ ...(input.role && { role: input.role }),
283
+ ...(input.verified !== undefined && { verified: input.verified })
284
+ }
285
+ });
286
+ }),
287
+ // Delete user
288
+ deleteUser: protectedProcedure
289
+ .input(z.object({
290
+ userId: z.string(),
291
+ schoolId: z.string()
292
+ }))
293
+ .mutation(async ({ input, ctx }) => {
294
+ if (!ctx.user) {
295
+ throw new TRPCError({
296
+ code: 'UNAUTHORIZED',
297
+ message: 'User must be authenticated'
298
+ });
299
+ }
300
+ // Check admin permission
301
+ const isAdmin = await prisma.user.findFirst({
302
+ where: {
303
+ id: ctx.user.id,
304
+ schoolId: input.schoolId,
305
+ role: UserRole.ADMIN
306
+ }
307
+ });
308
+ if (!isAdmin) {
309
+ throw new TRPCError({ code: 'FORBIDDEN' });
310
+ }
311
+ // Don't allow deleting yourself
312
+ if (input.userId === ctx.user.id) {
313
+ throw new TRPCError({
314
+ code: 'BAD_REQUEST',
315
+ message: 'You cannot delete yourself'
316
+ });
317
+ }
318
+ return prisma.user.delete({
319
+ where: { id: input.userId }
320
+ });
321
+ }),
322
+ // Get school info
323
+ getSchool: protectedProcedure
324
+ .input(z.object({ schoolId: z.string() }))
325
+ .query(async ({ input }) => {
326
+ return prisma.school.findUnique({
327
+ where: { id: input.schoolId },
328
+ include: {
329
+ logo: true,
330
+ _count: {
331
+ select: {
332
+ users: true,
333
+ classes: true
334
+ }
335
+ }
336
+ }
337
+ });
338
+ }),
339
+ // Update school settings
340
+ updateSchool: protectedProcedure
341
+ .input(z.object({
342
+ schoolId: z.string(),
343
+ name: z.string().optional(),
344
+ subdomain: z.string().optional(),
345
+ logoId: z.string().optional()
346
+ }))
347
+ .mutation(async ({ input, ctx }) => {
348
+ if (!ctx.user) {
349
+ throw new TRPCError({
350
+ code: 'UNAUTHORIZED',
351
+ message: 'User must be authenticated'
352
+ });
353
+ }
354
+ // Check admin permission
355
+ const isAdmin = await prisma.user.findFirst({
356
+ where: {
357
+ id: ctx.user.id,
358
+ schoolId: input.schoolId,
359
+ role: UserRole.ADMIN
360
+ }
361
+ });
362
+ if (!isAdmin) {
363
+ throw new TRPCError({ code: 'FORBIDDEN' });
364
+ }
365
+ const updateData = {};
366
+ if (input.name)
367
+ updateData.name = input.name;
368
+ if (input.subdomain)
369
+ updateData.subdomain = input.subdomain;
370
+ if (input.logoId)
371
+ updateData.logoId = input.logoId;
372
+ return prisma.school.update({
373
+ where: { id: input.schoolId },
374
+ data: updateData
375
+ });
376
+ }),
377
+ // Get all classes in school
378
+ getClasses: protectedProcedure
379
+ .input(z.object({ schoolId: z.string() }))
380
+ .query(async ({ input, ctx }) => {
381
+ if (!ctx.user) {
382
+ throw new TRPCError({
383
+ code: 'UNAUTHORIZED',
384
+ message: 'User must be authenticated'
385
+ });
386
+ }
387
+ // Check if user belongs to school
388
+ const user = await prisma.user.findFirst({
389
+ where: {
390
+ id: ctx.user.id,
391
+ schoolId: input.schoolId
392
+ }
393
+ });
394
+ if (!user) {
395
+ throw new TRPCError({ code: 'FORBIDDEN' });
396
+ }
397
+ return prisma.class.findMany({
398
+ where: { schoolId: input.schoolId },
399
+ include: {
400
+ _count: {
401
+ select: {
402
+ students: true,
403
+ teachers: true,
404
+ assignments: true
405
+ }
406
+ }
407
+ },
408
+ orderBy: { name: 'asc' }
409
+ });
410
+ }),
411
+ // Send bulk email
412
+ sendBulkEmail: protectedProcedure
413
+ .input(z.object({
414
+ schoolId: z.string(),
415
+ subject: z.string(),
416
+ content: z.string(),
417
+ recipientRole: z.enum(['ALL', 'STUDENT', 'TEACHER', 'ADMIN']).optional(),
418
+ recipientIds: z.array(z.string()).optional()
419
+ }))
420
+ .mutation(async ({ input, ctx }) => {
421
+ if (!ctx.user) {
422
+ throw new TRPCError({
423
+ code: 'UNAUTHORIZED',
424
+ message: 'User must be authenticated'
425
+ });
426
+ }
427
+ // Check admin permission
428
+ const isAdmin = await prisma.user.findFirst({
429
+ where: {
430
+ id: ctx.user.id,
431
+ schoolId: input.schoolId,
432
+ role: UserRole.ADMIN
433
+ }
434
+ });
435
+ if (!isAdmin) {
436
+ throw new TRPCError({ code: 'FORBIDDEN' });
437
+ }
438
+ // Get recipients
439
+ let recipients = [];
440
+ if (input.recipientIds && input.recipientIds.length > 0) {
441
+ recipients = await prisma.user.findMany({
442
+ where: {
443
+ id: { in: input.recipientIds },
444
+ schoolId: input.schoolId
445
+ },
446
+ select: { email: true }
447
+ });
448
+ }
449
+ else {
450
+ const whereClause = { schoolId: input.schoolId };
451
+ if (input.recipientRole && input.recipientRole !== 'ALL') {
452
+ whereClause.role = input.recipientRole;
453
+ }
454
+ recipients = await prisma.user.findMany({
455
+ where: whereClause,
456
+ select: { email: true }
457
+ });
458
+ }
459
+ // Send emails
460
+ const results = [];
461
+ for (const recipient of recipients) {
462
+ try {
463
+ await transport.sendMail({
464
+ from: process.env.EMAIL_FROM || 'noreply@studious.app',
465
+ to: recipient.email,
466
+ subject: input.subject,
467
+ text: input.content
468
+ });
469
+ results.push({ email: recipient.email, success: true });
470
+ }
471
+ catch (error) {
472
+ results.push({
473
+ email: recipient.email,
474
+ success: false,
475
+ error: error instanceof Error ? error.message : 'Unknown error'
476
+ });
477
+ }
478
+ }
479
+ return results;
480
+ })
481
+ });
@@ -5,6 +5,7 @@ export declare const sectionRouter: import("@trpc/server").TRPCBuiltRouter<{
5
5
  errorShape: {
6
6
  data: {
7
7
  zodError: z.typeToFlattenedError<any, string> | null;
8
+ prismaError: import("../utils/prismaErrorHandler").PrismaErrorInfo | null;
8
9
  code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
9
10
  httpStatus: number;
10
11
  path?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../src/routers/section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAqBxB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoHxB,CAAC"}
1
+ {"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../src/routers/section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAqBxB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoHxB,CAAC"}
@@ -1,35 +1,32 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sectionRouter = void 0;
4
- const zod_1 = require("zod");
5
- const trpc_1 = require("../trpc");
6
- const server_1 = require("@trpc/server");
7
- const prisma_1 = require("../lib/prisma");
8
- const createSectionSchema = zod_1.z.object({
9
- classId: zod_1.z.string(),
10
- name: zod_1.z.string(),
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { prisma } from "../lib/prisma";
5
+ const createSectionSchema = z.object({
6
+ classId: z.string(),
7
+ name: z.string(),
11
8
  });
12
- const updateSectionSchema = zod_1.z.object({
13
- id: zod_1.z.string(),
14
- classId: zod_1.z.string(),
15
- name: zod_1.z.string(),
9
+ const updateSectionSchema = z.object({
10
+ id: z.string(),
11
+ classId: z.string(),
12
+ name: z.string(),
16
13
  });
17
- const deleteSectionSchema = zod_1.z.object({
18
- id: zod_1.z.string(),
19
- classId: zod_1.z.string(),
14
+ const deleteSectionSchema = z.object({
15
+ id: z.string(),
16
+ classId: z.string(),
20
17
  });
21
- exports.sectionRouter = (0, trpc_1.createTRPCRouter)({
22
- create: trpc_1.protectedProcedure
18
+ export const sectionRouter = createTRPCRouter({
19
+ create: protectedProcedure
23
20
  .input(createSectionSchema)
24
21
  .mutation(async ({ ctx, input }) => {
25
22
  if (!ctx.user) {
26
- throw new server_1.TRPCError({
23
+ throw new TRPCError({
27
24
  code: "UNAUTHORIZED",
28
25
  message: "User must be authenticated",
29
26
  });
30
27
  }
31
28
  // Verify user is a teacher of the class
32
- const classData = await prisma_1.prisma.class.findFirst({
29
+ const classData = await prisma.class.findFirst({
33
30
  where: {
34
31
  id: input.classId,
35
32
  teachers: {
@@ -40,12 +37,12 @@ exports.sectionRouter = (0, trpc_1.createTRPCRouter)({
40
37
  },
41
38
  });
42
39
  if (!classData) {
43
- throw new server_1.TRPCError({
40
+ throw new TRPCError({
44
41
  code: "NOT_FOUND",
45
42
  message: "Class not found or you are not a teacher",
46
43
  });
47
44
  }
48
- const section = await prisma_1.prisma.section.create({
45
+ const section = await prisma.section.create({
49
46
  data: {
50
47
  name: input.name,
51
48
  class: {
@@ -55,17 +52,17 @@ exports.sectionRouter = (0, trpc_1.createTRPCRouter)({
55
52
  });
56
53
  return section;
57
54
  }),
58
- update: trpc_1.protectedProcedure
55
+ update: protectedProcedure
59
56
  .input(updateSectionSchema)
60
57
  .mutation(async ({ ctx, input }) => {
61
58
  if (!ctx.user) {
62
- throw new server_1.TRPCError({
59
+ throw new TRPCError({
63
60
  code: "UNAUTHORIZED",
64
61
  message: "User must be authenticated",
65
62
  });
66
63
  }
67
64
  // Verify user is a teacher of the class
68
- const classData = await prisma_1.prisma.class.findFirst({
65
+ const classData = await prisma.class.findFirst({
69
66
  where: {
70
67
  id: input.classId,
71
68
  teachers: {
@@ -76,12 +73,12 @@ exports.sectionRouter = (0, trpc_1.createTRPCRouter)({
76
73
  },
77
74
  });
78
75
  if (!classData) {
79
- throw new server_1.TRPCError({
76
+ throw new TRPCError({
80
77
  code: "NOT_FOUND",
81
78
  message: "Class not found or you are not a teacher",
82
79
  });
83
80
  }
84
- const section = await prisma_1.prisma.section.update({
81
+ const section = await prisma.section.update({
85
82
  where: { id: input.id },
86
83
  data: {
87
84
  name: input.name,
@@ -89,17 +86,17 @@ exports.sectionRouter = (0, trpc_1.createTRPCRouter)({
89
86
  });
90
87
  return section;
91
88
  }),
92
- delete: trpc_1.protectedProcedure
89
+ delete: protectedProcedure
93
90
  .input(deleteSectionSchema)
94
91
  .mutation(async ({ ctx, input }) => {
95
92
  if (!ctx.user) {
96
- throw new server_1.TRPCError({
93
+ throw new TRPCError({
97
94
  code: "UNAUTHORIZED",
98
95
  message: "User must be authenticated",
99
96
  });
100
97
  }
101
98
  // Verify user is a teacher of the class
102
- const classData = await prisma_1.prisma.class.findFirst({
99
+ const classData = await prisma.class.findFirst({
103
100
  where: {
104
101
  id: input.classId,
105
102
  teachers: {
@@ -110,12 +107,12 @@ exports.sectionRouter = (0, trpc_1.createTRPCRouter)({
110
107
  },
111
108
  });
112
109
  if (!classData) {
113
- throw new server_1.TRPCError({
110
+ throw new TRPCError({
114
111
  code: "NOT_FOUND",
115
112
  message: "Class not found or you are not a teacher",
116
113
  });
117
114
  }
118
- await prisma_1.prisma.section.delete({
115
+ await prisma.section.delete({
119
116
  where: { id: input.id },
120
117
  });
121
118
  return { id: input.id };
@@ -5,6 +5,7 @@ export declare const userRouter: import("@trpc/server").TRPCBuiltRouter<{
5
5
  errorShape: {
6
6
  data: {
7
7
  zodError: z.typeToFlattenedError<any, string> | null;
8
+ prismaError: import("../utils/prismaErrorHandler").PrismaErrorInfo | null;
8
9
  code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
9
10
  httpStatus: number;
10
11
  path?: string;
@@ -33,8 +34,8 @@ export declare const userRouter: import("@trpc/server").TRPCBuiltRouter<{
33
34
  profilePicture?: {
34
35
  type: string;
35
36
  name: string;
36
- data: string;
37
37
  size: number;
38
+ data: string;
38
39
  } | undefined;
39
40
  };
40
41
  output: {
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/routers/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DrB,CAAC"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/routers/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DrB,CAAC"}