@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,755 @@
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure, protectedClassMemberProcedure, protectedTeacherProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { prisma } from "../lib/prisma";
5
+ import { uploadFiles, type UploadedFile } from "../lib/fileUpload";
6
+
7
+ const fileSchema = z.object({
8
+ name: z.string(),
9
+ type: z.string(),
10
+ size: z.number(),
11
+ data: z.string(), // base64 encoded file data
12
+ });
13
+
14
+ const createFolderSchema = z.object({
15
+ name: z.string(),
16
+ parentFolderId: z.string().optional(),
17
+ });
18
+
19
+ const uploadFilesToFolderSchema = z.object({
20
+ folderId: z.string(),
21
+ files: z.array(fileSchema),
22
+ });
23
+
24
+ const getRootFolderSchema = z.object({
25
+ classId: z.string(),
26
+ });
27
+
28
+ export const folderRouter = createTRPCRouter({
29
+ create: protectedTeacherProcedure
30
+ .input(createFolderSchema)
31
+ .mutation(async ({ ctx, input }) => {
32
+ const { classId, name } = input;
33
+ let parentFolderId = input.parentFolderId || null;
34
+
35
+ if (!ctx.user) {
36
+ throw new TRPCError({
37
+ code: "UNAUTHORIZED",
38
+ message: "You must be logged in to create a folder",
39
+ });
40
+ }
41
+
42
+ // Verify user is a teacher of the class
43
+ const classData = await prisma.class.findFirst({
44
+ where: {
45
+ id: classId,
46
+ teachers: {
47
+ some: {
48
+ id: ctx.user.id,
49
+ },
50
+ },
51
+ },
52
+ });
53
+
54
+ if (!classData) {
55
+ throw new TRPCError({
56
+ code: "NOT_FOUND",
57
+ message: "Class not found or you are not a teacher",
58
+ });
59
+ }
60
+
61
+ // If no parent folder specified, find or create the class parent folder
62
+ if (!parentFolderId) {
63
+ let classParentFolder = await prisma.folder.findFirst({
64
+ where: {
65
+ classId: classId,
66
+ parentFolderId: null,
67
+ },
68
+ });
69
+
70
+ if (!classParentFolder) {
71
+ // Create parent folder if it doesn't exist
72
+ classParentFolder = await prisma.folder.create({
73
+ data: {
74
+ name: "Class Files",
75
+ class: {
76
+ connect: { id: classId },
77
+ },
78
+ },
79
+ });
80
+ }
81
+
82
+ parentFolderId = classParentFolder.id;
83
+ } else {
84
+ // Check if specified parent folder exists and belongs to the class
85
+ const parentFolder = await prisma.folder.findFirst({
86
+ where: {
87
+ id: parentFolderId,
88
+ },
89
+ });
90
+
91
+ if (!parentFolder) {
92
+ throw new TRPCError({
93
+ code: "NOT_FOUND",
94
+ message: "Parent folder not found",
95
+ });
96
+ }
97
+ }
98
+
99
+ const folder = await prisma.folder.create({
100
+ data: {
101
+ name,
102
+ ...(parentFolderId && {
103
+ parentFolder: {
104
+ connect: { id: parentFolderId },
105
+ },
106
+ }),
107
+ },
108
+ include: {
109
+ files: {
110
+ select: {
111
+ id: true,
112
+ name: true,
113
+ type: true,
114
+ size: true,
115
+ uploadedAt: true,
116
+ user: {
117
+ select: {
118
+ id: true,
119
+ username: true,
120
+ },
121
+ },
122
+ },
123
+ },
124
+ childFolders: {
125
+ select: {
126
+ id: true,
127
+ name: true,
128
+ _count: {
129
+ select: {
130
+ files: true,
131
+ childFolders: true,
132
+ },
133
+ },
134
+ },
135
+ },
136
+ },
137
+ });
138
+
139
+ return folder;
140
+ }),
141
+
142
+ get: protectedClassMemberProcedure
143
+ .input(z.object({
144
+ folderId: z.string(),
145
+ classId: z.string(),
146
+ }))
147
+ .query(async ({ ctx, input }) => {
148
+ const { classId, folderId } = input;
149
+
150
+ // Get specific folder
151
+ const folder = await prisma.folder.findFirst({
152
+ where: {
153
+ id: folderId,
154
+ },
155
+ include: {
156
+ files: {
157
+ select: {
158
+ id: true,
159
+ name: true,
160
+ type: true,
161
+ size: true,
162
+ uploadedAt: true,
163
+ user: {
164
+ select: {
165
+ id: true,
166
+ username: true,
167
+ },
168
+ },
169
+ },
170
+ orderBy: {
171
+ uploadedAt: 'desc',
172
+ },
173
+ },
174
+ childFolders: {
175
+ select: {
176
+ id: true,
177
+ name: true,
178
+ _count: {
179
+ select: {
180
+ files: true,
181
+ childFolders: true,
182
+ },
183
+ },
184
+ },
185
+ orderBy: {
186
+ name: 'asc',
187
+ },
188
+ },
189
+ parentFolder: {
190
+ select: {
191
+ id: true,
192
+ name: true,
193
+ },
194
+ },
195
+ },
196
+ });
197
+
198
+ if (!folder) {
199
+ throw new TRPCError({
200
+ code: "NOT_FOUND",
201
+ message: "Folder not found",
202
+ });
203
+ }
204
+
205
+ return folder;
206
+ }),
207
+
208
+ getChildFolders: protectedClassMemberProcedure
209
+ .input(z.object({
210
+ classId: z.string(),
211
+ }))
212
+ .query(async ({ ctx, input }) => {
213
+ const { classId } = input;
214
+
215
+ // Get the parent folder for the class (or create it if it doesn't exist)
216
+ let parentFolder = await prisma.folder.findFirst({
217
+ where: {
218
+ classId: classId,
219
+ parentFolderId: null,
220
+ },
221
+ });
222
+
223
+ if (!parentFolder) {
224
+ // Create parent folder if it doesn't exist
225
+ parentFolder = await prisma.folder.create({
226
+ data: {
227
+ name: "Class Files",
228
+ class: {
229
+ connect: { id: classId },
230
+ },
231
+ },
232
+ });
233
+ }
234
+
235
+ // Get all child folders of the parent
236
+ const childFolders = await prisma.folder.findMany({
237
+ where: {
238
+ parentFolderId: parentFolder.id,
239
+ },
240
+ include: {
241
+ files: {
242
+ select: {
243
+ id: true,
244
+ name: true,
245
+ type: true,
246
+ size: true,
247
+ uploadedAt: true,
248
+ user: {
249
+ select: {
250
+ id: true,
251
+ username: true,
252
+ },
253
+ },
254
+ },
255
+ orderBy: {
256
+ uploadedAt: 'desc',
257
+ },
258
+ },
259
+ childFolders: {
260
+ select: {
261
+ id: true,
262
+ name: true,
263
+ _count: {
264
+ select: {
265
+ files: true,
266
+ childFolders: true,
267
+ },
268
+ },
269
+ },
270
+ orderBy: {
271
+ name: 'asc',
272
+ },
273
+ },
274
+ },
275
+ orderBy: {
276
+ name: 'asc',
277
+ },
278
+ });
279
+
280
+ return childFolders;
281
+ }),
282
+
283
+ getFolderChildren: protectedClassMemberProcedure
284
+ .input(z.object({
285
+ folderId: z.string(),
286
+ classId: z.string(),
287
+ }))
288
+ .query(async ({ ctx, input }) => {
289
+ const { folderId, classId } = input;
290
+
291
+ // Get direct children of the specified folder
292
+ const children = await prisma.folder.findMany({
293
+ where: {
294
+ parentFolderId: folderId,
295
+ classId: classId,
296
+ },
297
+ include: {
298
+ files: {
299
+ select: {
300
+ id: true,
301
+ name: true,
302
+ type: true,
303
+ size: true,
304
+ uploadedAt: true,
305
+ user: {
306
+ select: {
307
+ id: true,
308
+ username: true,
309
+ },
310
+ },
311
+ },
312
+ orderBy: {
313
+ uploadedAt: 'desc',
314
+ },
315
+ },
316
+ childFolders: {
317
+ select: {
318
+ id: true,
319
+ name: true,
320
+ _count: {
321
+ select: {
322
+ files: true,
323
+ childFolders: true,
324
+ },
325
+ },
326
+ },
327
+ orderBy: {
328
+ name: 'asc',
329
+ },
330
+ },
331
+ },
332
+ orderBy: {
333
+ name: 'asc',
334
+ },
335
+ });
336
+
337
+ return children;
338
+ }),
339
+
340
+ getRootFolder: protectedClassMemberProcedure
341
+ .input(getRootFolderSchema)
342
+ .query(async ({ ctx, input }) => {
343
+ const { classId } = input;
344
+
345
+ // Get or create the parent folder for the class
346
+ let parentFolder = await prisma.folder.findFirst({
347
+ where: {
348
+ classId: classId,
349
+ parentFolderId: null,
350
+ },
351
+ });
352
+
353
+ if (!parentFolder) {
354
+ // Create parent folder if it doesn't exist
355
+ parentFolder = await prisma.folder.create({
356
+ data: {
357
+ name: "Class Files",
358
+ class: {
359
+ connect: { id: classId },
360
+ },
361
+ },
362
+ });
363
+ }
364
+
365
+ // Get the parent folder with its files and child folders
366
+ const rootFolder = await prisma.folder.findFirst({
367
+ where: {
368
+ id: parentFolder.id,
369
+ classId: classId,
370
+ },
371
+ include: {
372
+ files: {
373
+ select: {
374
+ id: true,
375
+ name: true,
376
+ type: true,
377
+ size: true,
378
+ uploadedAt: true,
379
+ user: {
380
+ select: {
381
+ id: true,
382
+ username: true,
383
+ },
384
+ },
385
+ },
386
+ orderBy: {
387
+ uploadedAt: 'desc',
388
+ },
389
+ },
390
+ childFolders: {
391
+ select: {
392
+ id: true,
393
+ name: true,
394
+ files: {
395
+ select: {
396
+ id: true,
397
+ },
398
+ },
399
+ childFolders: {
400
+ select: {
401
+ id: true,
402
+ },
403
+ },
404
+ },
405
+ orderBy: {
406
+ name: 'asc',
407
+ },
408
+ },
409
+ },
410
+ });
411
+
412
+ return rootFolder;
413
+ }),
414
+
415
+ uploadFiles: protectedTeacherProcedure
416
+ .input(uploadFilesToFolderSchema)
417
+ .mutation(async ({ ctx, input }) => {
418
+ const { classId, folderId, files } = input;
419
+
420
+ if (!ctx.user) {
421
+ throw new TRPCError({
422
+ code: "UNAUTHORIZED",
423
+ message: "You must be logged in to upload files",
424
+ });
425
+ }
426
+
427
+ // Verify user is a teacher of the class
428
+ const classData = await prisma.class.findFirst({
429
+ where: {
430
+ id: classId,
431
+ teachers: {
432
+ some: {
433
+ id: ctx.user.id,
434
+ },
435
+ },
436
+ },
437
+ });
438
+
439
+ if (!classData) {
440
+ throw new TRPCError({
441
+ code: "NOT_FOUND",
442
+ message: "Class not found or you are not a teacher",
443
+ });
444
+ }
445
+
446
+ // Verify folder exists and belongs to the class
447
+ const folder = await prisma.folder.findFirst({
448
+ where: {
449
+ id: folderId,
450
+ },
451
+ });
452
+
453
+ if (!folder) {
454
+ throw new TRPCError({
455
+ code: "NOT_FOUND",
456
+ message: "Folder not found",
457
+ });
458
+ }
459
+
460
+ // Upload files
461
+ const uploadedFiles = await uploadFiles(files, ctx.user.id, folder.id);
462
+
463
+ // Create file records in database
464
+ // const fileRecords = await prisma.file.createMany({
465
+ // data: uploadedFiles.map(file => ({
466
+ // name: file.name,
467
+ // type: file.type,
468
+ // size: file.size,
469
+ // path: file.path,
470
+ // userId: ctx.user!.id,
471
+ // folderId: folderId,
472
+ // ...(file.thumbnailId && {
473
+ // thumbnailId: file.thumbnailId,
474
+ // }),
475
+ // })),
476
+ // });
477
+
478
+ return {
479
+ success: true,
480
+ uploadedCount: uploadedFiles.length,
481
+ };
482
+ }),
483
+
484
+ delete: protectedTeacherProcedure
485
+ .input(z.object({
486
+ classId: z.string(),
487
+ folderId: z.string(),
488
+ }))
489
+ .mutation(async ({ ctx, input }) => {
490
+ const { classId, folderId } = input;
491
+
492
+ // Verify user is a teacher of the class
493
+ const classData = await prisma.class.findFirst({
494
+ where: {
495
+ id: classId,
496
+ teachers: {
497
+ some: {
498
+ id: ctx.user!.id,
499
+ },
500
+ },
501
+ },
502
+ });
503
+
504
+ if (!classData) {
505
+ throw new TRPCError({
506
+ code: "FORBIDDEN",
507
+ message: "Class not found or you are not a teacher",
508
+ });
509
+ }
510
+
511
+ // Verify folder exists and belongs to the class
512
+ const folder = await prisma.folder.findFirst({
513
+ where: {
514
+ id: folderId,
515
+ classId: classId,
516
+ },
517
+ include: {
518
+ _count: {
519
+ select: {
520
+ files: true,
521
+ childFolders: true,
522
+ },
523
+ },
524
+ },
525
+ });
526
+
527
+ if (!folder) {
528
+ throw new TRPCError({
529
+ code: "NOT_FOUND",
530
+ message: "Folder not found",
531
+ });
532
+ }
533
+
534
+ // Delete folder and all its contents (cascade)
535
+ await prisma.folder.delete({
536
+ where: {
537
+ id: folderId,
538
+ },
539
+ });
540
+
541
+ return {
542
+ success: true,
543
+ deletedFiles: folder._count.files,
544
+ deletedFolders: folder._count.childFolders + 1, // +1 for the folder itself
545
+ };
546
+ }),
547
+
548
+ move: protectedTeacherProcedure
549
+ .input(z.object({
550
+ folderId: z.string(),
551
+ targetParentFolderId: z.string().optional(),
552
+ classId: z.string(),
553
+ }))
554
+ .mutation(async ({ ctx, input }) => {
555
+ const { folderId, targetParentFolderId, classId } = input;
556
+
557
+ // Verify user is a teacher of the class
558
+ const classData = await prisma.class.findFirst({
559
+ where: {
560
+ id: classId,
561
+ teachers: {
562
+ some: {
563
+ id: ctx.user!.id,
564
+ },
565
+ },
566
+ },
567
+ });
568
+
569
+ if (!classData) {
570
+ throw new TRPCError({
571
+ code: "FORBIDDEN",
572
+ message: "You must be a teacher of this class to move folders",
573
+ });
574
+ }
575
+
576
+ // Get the folder to move
577
+ const folder = await prisma.folder.findFirst({
578
+ where: {
579
+ id: folderId,
580
+ },
581
+ });
582
+
583
+ if (!folder) {
584
+ throw new TRPCError({
585
+ code: "NOT_FOUND",
586
+ message: "Folder not found",
587
+ });
588
+ }
589
+
590
+ // Prevent moving the root folder
591
+ if (!folder.parentFolderId) {
592
+ throw new TRPCError({
593
+ code: "BAD_REQUEST",
594
+ message: "Cannot move the root folder",
595
+ });
596
+ }
597
+
598
+ // If target parent folder is specified, verify it exists and belongs to the class
599
+ if (targetParentFolderId) {
600
+ const targetParentFolder = await prisma.folder.findFirst({
601
+ where: {
602
+ id: targetParentFolderId,
603
+ },
604
+ });
605
+
606
+ if (!targetParentFolder) {
607
+ throw new TRPCError({
608
+ code: "NOT_FOUND",
609
+ message: "Target parent folder not found",
610
+ });
611
+ }
612
+
613
+ // Prevent moving a folder into itself or its descendants
614
+ if (targetParentFolderId === folderId) {
615
+ throw new TRPCError({
616
+ code: "BAD_REQUEST",
617
+ message: "Cannot move a folder into itself",
618
+ });
619
+ }
620
+
621
+ // Check if target is a descendant of the folder being moved
622
+ let currentParent: any = targetParentFolder;
623
+ while (currentParent?.parentFolderId) {
624
+ if (currentParent.parentFolderId === folderId) {
625
+ throw new TRPCError({
626
+ code: "BAD_REQUEST",
627
+ message: "Cannot move a folder into its descendant",
628
+ });
629
+ }
630
+ currentParent = await prisma.folder.findUnique({
631
+ where: { id: currentParent.parentFolderId },
632
+ });
633
+ if (!currentParent) break;
634
+ }
635
+ } else {
636
+ // Moving to root - verify the folder isn't already at root
637
+ if (!folder.parentFolderId) {
638
+ throw new TRPCError({
639
+ code: "BAD_REQUEST",
640
+ message: "Folder is already at root level",
641
+ });
642
+ }
643
+ }
644
+
645
+ // Move the folder
646
+ const updatedFolder = await prisma.folder.update({
647
+ where: { id: folderId },
648
+ data: {
649
+ parentFolderId: targetParentFolderId || null,
650
+ },
651
+ include: {
652
+ files: {
653
+ select: {
654
+ id: true,
655
+ name: true,
656
+ type: true,
657
+ size: true,
658
+ uploadedAt: true,
659
+ user: {
660
+ select: {
661
+ id: true,
662
+ username: true,
663
+ },
664
+ },
665
+ },
666
+ },
667
+ childFolders: {
668
+ select: {
669
+ id: true,
670
+ name: true,
671
+ _count: {
672
+ select: {
673
+ files: true,
674
+ childFolders: true,
675
+ },
676
+ },
677
+ },
678
+ },
679
+ },
680
+ });
681
+
682
+ return updatedFolder;
683
+ }),
684
+
685
+ rename: protectedTeacherProcedure
686
+ .input(z.object({
687
+ folderId: z.string(),
688
+ newName: z.string(),
689
+ classId: z.string(),
690
+ }))
691
+ .mutation(async ({ ctx, input }) => {
692
+ const { folderId, newName, classId } = input;
693
+
694
+ // Get the folder
695
+ const folder = await prisma.folder.findFirst({
696
+ where: {
697
+ id: folderId,
698
+ },
699
+ });
700
+
701
+ if (!folder) {
702
+ throw new TRPCError({
703
+ code: "NOT_FOUND",
704
+ message: "Folder not found",
705
+ });
706
+ }
707
+
708
+ // Validate new name
709
+ if (!newName.trim()) {
710
+ throw new TRPCError({
711
+ code: "BAD_REQUEST",
712
+ message: "Folder name cannot be empty",
713
+ });
714
+ }
715
+
716
+ // Rename the folder
717
+ const updatedFolder = await prisma.folder.update({
718
+ where: { id: folderId },
719
+ data: {
720
+ name: newName.trim(),
721
+ },
722
+ include: {
723
+ files: {
724
+ select: {
725
+ id: true,
726
+ name: true,
727
+ type: true,
728
+ size: true,
729
+ uploadedAt: true,
730
+ user: {
731
+ select: {
732
+ id: true,
733
+ username: true,
734
+ },
735
+ },
736
+ },
737
+ },
738
+ childFolders: {
739
+ select: {
740
+ id: true,
741
+ name: true,
742
+ _count: {
743
+ select: {
744
+ files: true,
745
+ childFolders: true,
746
+ },
747
+ },
748
+ },
749
+ },
750
+ },
751
+ });
752
+
753
+ return updatedFolder;
754
+ }),
755
+ });