@studious-lms/server 1.1.24 → 1.2.6
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/.coderabbit.yaml +9 -0
- package/.env.example +53 -0
- package/.env.test.example +37 -0
- package/README.md +34 -7
- package/dist/exportType.d.ts.map +1 -1
- package/dist/exportType.js +4 -0
- package/dist/exportType.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +212 -51
- package/dist/index.js.map +1 -0
- package/dist/instrument.d.ts +2 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +18 -0
- package/dist/instrument.js.map +1 -0
- package/dist/lib/config/env.d.ts +190 -0
- package/dist/lib/config/env.d.ts.map +1 -0
- package/dist/lib/config/env.js +121 -0
- package/dist/lib/config/env.js.map +1 -0
- package/dist/lib/fileUpload.d.ts +2 -2
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +82 -15
- package/dist/lib/fileUpload.js.map +1 -0
- package/dist/lib/googleCloudStorage.d.ts +13 -0
- package/dist/lib/googleCloudStorage.d.ts.map +1 -1
- package/dist/lib/googleCloudStorage.js +45 -6
- package/dist/lib/googleCloudStorage.js.map +1 -0
- package/dist/lib/jsonConversion.d.ts.map +1 -1
- package/dist/lib/jsonConversion.js +16 -14
- package/dist/lib/jsonConversion.js.map +1 -0
- package/dist/lib/jsonStyles.d.ts.map +1 -1
- package/dist/lib/jsonStyles.js +4 -0
- package/dist/lib/jsonStyles.js.map +1 -0
- package/dist/lib/notificationHandler.d.ts +25 -0
- package/dist/lib/notificationHandler.d.ts.map +1 -0
- package/dist/lib/notificationHandler.js +32 -0
- package/dist/lib/notificationHandler.js.map +1 -0
- package/dist/lib/prisma.d.ts +2 -2
- package/dist/lib/prisma.d.ts.map +1 -1
- package/dist/lib/prisma.js +24 -1
- package/dist/lib/prisma.js.map +1 -0
- package/dist/lib/pusher.d.ts +4 -1
- package/dist/lib/pusher.d.ts.map +1 -1
- package/dist/lib/pusher.js +14 -6
- package/dist/lib/pusher.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +53 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/thumbnailGenerator.d.ts +0 -21
- package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
- package/dist/lib/thumbnailGenerator.js +159 -158
- package/dist/lib/thumbnailGenerator.js.map +1 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +41 -93
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/logging.d.ts.map +1 -1
- package/dist/middleware/logging.js +4 -0
- package/dist/middleware/logging.js.map +1 -0
- package/dist/middleware/security.d.ts +5 -0
- package/dist/middleware/security.d.ts.map +1 -0
- package/dist/middleware/security.js +77 -0
- package/dist/middleware/security.js.map +1 -0
- package/dist/models/agenda.d.ts +97 -0
- package/dist/models/agenda.d.ts.map +1 -0
- package/dist/models/agenda.js +40 -0
- package/dist/models/agenda.js.map +1 -0
- package/dist/models/announcement.d.ts +223 -0
- package/dist/models/announcement.d.ts.map +1 -0
- package/dist/models/announcement.js +120 -0
- package/dist/models/announcement.js.map +1 -0
- package/dist/models/assignment.d.ts +1292 -0
- package/dist/models/assignment.d.ts.map +1 -0
- package/dist/models/assignment.js +309 -0
- package/dist/models/assignment.js.map +1 -0
- package/dist/models/attendance.d.ts +180 -0
- package/dist/models/attendance.d.ts.map +1 -0
- package/dist/models/attendance.js +188 -0
- package/dist/models/attendance.js.map +1 -0
- package/dist/models/auth.d.ts +153 -0
- package/dist/models/auth.d.ts.map +1 -0
- package/dist/models/auth.js +217 -0
- package/dist/models/auth.js.map +1 -0
- package/dist/models/class.d.ts +439 -0
- package/dist/models/class.d.ts.map +1 -0
- package/dist/models/class.js +546 -0
- package/dist/models/class.js.map +1 -0
- package/dist/models/comment.d.ts +171 -0
- package/dist/models/comment.d.ts.map +1 -0
- package/dist/models/comment.js +138 -0
- package/dist/models/comment.js.map +1 -0
- package/dist/models/conversation.d.ts +164 -0
- package/dist/models/conversation.d.ts.map +1 -0
- package/dist/models/conversation.js +175 -0
- package/dist/models/conversation.js.map +1 -0
- package/dist/models/event.d.ts +295 -0
- package/dist/models/event.d.ts.map +1 -0
- package/dist/models/event.js +145 -0
- package/dist/models/event.js.map +1 -0
- package/dist/models/file.d.ts +536 -0
- package/dist/models/file.d.ts.map +1 -0
- package/dist/models/file.js +126 -0
- package/dist/models/file.js.map +1 -0
- package/dist/models/folder.d.ts +295 -0
- package/dist/models/folder.d.ts.map +1 -0
- package/dist/models/folder.js +202 -0
- package/dist/models/folder.js.map +1 -0
- package/dist/models/labChat.d.ts +243 -0
- package/dist/models/labChat.d.ts.map +1 -0
- package/dist/models/labChat.js +204 -0
- package/dist/models/labChat.js.map +1 -0
- package/dist/models/marketing.d.ts +72 -0
- package/dist/models/marketing.d.ts.map +1 -0
- package/dist/models/marketing.js +26 -0
- package/dist/models/marketing.js.map +1 -0
- package/dist/models/message.d.ts +100 -0
- package/dist/models/message.d.ts.map +1 -0
- package/dist/models/message.js +131 -0
- package/dist/models/message.js.map +1 -0
- package/dist/models/newtonChat.d.ts +72 -0
- package/dist/models/newtonChat.d.ts.map +1 -0
- package/dist/models/newtonChat.js +61 -0
- package/dist/models/newtonChat.js.map +1 -0
- package/dist/models/notification.d.ts +65 -0
- package/dist/models/notification.d.ts.map +1 -0
- package/dist/models/notification.js +46 -0
- package/dist/models/notification.js.map +1 -0
- package/dist/models/section.d.ts +102 -0
- package/dist/models/section.d.ts.map +1 -0
- package/dist/models/section.js +83 -0
- package/dist/models/section.js.map +1 -0
- package/dist/models/user.d.ts +39 -0
- package/dist/models/user.d.ts.map +1 -0
- package/dist/models/user.js +38 -0
- package/dist/models/user.js.map +1 -0
- package/dist/models/worksheet.d.ts +460 -0
- package/dist/models/worksheet.d.ts.map +1 -0
- package/dist/models/worksheet.js +200 -0
- package/dist/models/worksheet.js.map +1 -0
- package/dist/pipelines/aiLabChat.d.ts +21 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -0
- package/dist/pipelines/aiLabChat.js +460 -0
- package/dist/pipelines/aiLabChat.js.map +1 -0
- package/dist/pipelines/aiNewtonChat.d.ts +30 -0
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -0
- package/dist/pipelines/aiNewtonChat.js +289 -0
- package/dist/pipelines/aiNewtonChat.js.map +1 -0
- package/dist/pipelines/gradeWorksheet.d.ts +30 -0
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -0
- package/dist/pipelines/gradeWorksheet.js +252 -0
- package/dist/pipelines/gradeWorksheet.js.map +1 -0
- package/dist/routers/_app.d.ts +6403 -3741
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +10 -0
- package/dist/routers/_app.js.map +1 -0
- package/dist/routers/agenda.d.ts +58 -6
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +6 -58
- package/dist/routers/agenda.js.map +1 -0
- package/dist/routers/announcement.d.ts +325 -6
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +547 -57
- package/dist/routers/announcement.js.map +1 -0
- package/dist/routers/assignment.d.ts +431 -318
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +104 -1559
- package/dist/routers/assignment.js.map +1 -0
- package/dist/routers/attendance.d.ts +20 -9
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +10 -263
- package/dist/routers/attendance.js.map +1 -0
- package/dist/routers/auth.d.ts +21 -1
- package/dist/routers/auth.d.ts.map +1 -1
- package/dist/routers/auth.js +37 -241
- package/dist/routers/auth.js.map +1 -0
- package/dist/routers/class.d.ts +198 -68
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +88 -909
- package/dist/routers/class.js.map +1 -0
- package/dist/routers/comment.d.ts +153 -0
- package/dist/routers/comment.d.ts.map +1 -0
- package/dist/routers/comment.js +58 -0
- package/dist/routers/comment.js.map +1 -0
- package/dist/routers/conversation.d.ts +73 -3
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/conversation.js +23 -265
- package/dist/routers/conversation.js.map +1 -0
- package/dist/routers/event.d.ts +46 -37
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/event.js +15 -431
- package/dist/routers/event.js.map +1 -0
- package/dist/routers/file.d.ts +4 -2
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +11 -295
- package/dist/routers/file.js.map +1 -0
- package/dist/routers/folder.d.ts +21 -14
- package/dist/routers/folder.d.ts.map +1 -1
- package/dist/routers/folder.js +36 -743
- package/dist/routers/folder.js.map +1 -0
- package/dist/routers/labChat.d.ts +12 -9
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +21 -877
- package/dist/routers/labChat.js.map +1 -0
- package/dist/routers/marketing.d.ts +2 -2
- package/dist/routers/marketing.d.ts.map +1 -1
- package/dist/routers/marketing.js +9 -54
- package/dist/routers/marketing.js.map +1 -0
- package/dist/routers/message.d.ts +2 -1
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +29 -519
- package/dist/routers/message.js.map +1 -0
- package/dist/routers/newtonChat.d.ts +55 -0
- package/dist/routers/newtonChat.d.ts.map +1 -0
- package/dist/routers/newtonChat.js +22 -0
- package/dist/routers/newtonChat.js.map +1 -0
- package/dist/routers/notifications.d.ts +8 -8
- package/dist/routers/notifications.d.ts.map +1 -1
- package/dist/routers/notifications.js +20 -81
- package/dist/routers/notifications.js.map +1 -0
- package/dist/routers/section.d.ts +37 -6
- package/dist/routers/section.d.ts.map +1 -1
- package/dist/routers/section.js +26 -167
- package/dist/routers/section.js.map +1 -0
- package/dist/routers/user.d.ts +1 -1
- package/dist/routers/user.d.ts.map +1 -1
- package/dist/routers/user.js +34 -204
- package/dist/routers/user.js.map +1 -0
- package/dist/routers/worksheet.d.ts +362 -0
- package/dist/routers/worksheet.d.ts.map +1 -0
- package/dist/routers/worksheet.js +153 -0
- package/dist/routers/worksheet.js.map +1 -0
- package/dist/seedDatabase.d.ts +2 -3
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +311 -289
- package/dist/seedDatabase.js.map +1 -0
- package/dist/server/pipelines/aiLabChat.d.ts +21 -0
- package/dist/server/pipelines/aiLabChat.d.ts.map +1 -0
- package/dist/server/pipelines/aiLabChat.js +456 -0
- package/dist/server/pipelines/aiLabChat.js.map +1 -0
- package/dist/server/pipelines/aiNewtonChat.d.ts +30 -0
- package/dist/server/pipelines/aiNewtonChat.d.ts.map +1 -0
- package/dist/server/pipelines/aiNewtonChat.js +285 -0
- package/dist/server/pipelines/aiNewtonChat.js.map +1 -0
- package/dist/server/pipelines/gradeWorksheet.d.ts +30 -0
- package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -0
- package/dist/server/pipelines/gradeWorksheet.js +248 -0
- package/dist/server/pipelines/gradeWorksheet.js.map +1 -0
- package/dist/services/agenda.d.ts +100 -0
- package/dist/services/agenda.d.ts.map +1 -0
- package/dist/services/agenda.js +21 -0
- package/dist/services/agenda.js.map +1 -0
- package/dist/services/announcement.d.ts +135 -0
- package/dist/services/announcement.d.ts.map +1 -0
- package/dist/services/announcement.js +223 -0
- package/dist/services/announcement.js.map +1 -0
- package/dist/services/assignment.d.ts +1462 -0
- package/dist/services/assignment.d.ts.map +1 -0
- package/dist/services/assignment.js +898 -0
- package/dist/services/assignment.js.map +1 -0
- package/dist/services/attendance.d.ts +93 -0
- package/dist/services/attendance.d.ts.map +1 -0
- package/dist/services/attendance.js +61 -0
- package/dist/services/attendance.js.map +1 -0
- package/dist/services/auth.d.ts +68 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +218 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/class.d.ts +621 -0
- package/dist/services/class.d.ts.map +1 -0
- package/dist/services/class.js +474 -0
- package/dist/services/class.js.map +1 -0
- package/dist/services/comment.d.ts +100 -0
- package/dist/services/comment.d.ts.map +1 -0
- package/dist/services/comment.js +83 -0
- package/dist/services/comment.js.map +1 -0
- package/dist/services/conversation.d.ts +159 -0
- package/dist/services/conversation.d.ts.map +1 -0
- package/dist/services/conversation.js +138 -0
- package/dist/services/conversation.js.map +1 -0
- package/dist/services/event.d.ts +216 -0
- package/dist/services/event.d.ts.map +1 -0
- package/dist/services/event.js +168 -0
- package/dist/services/event.js.map +1 -0
- package/dist/services/file.d.ts +74 -0
- package/dist/services/file.d.ts.map +1 -0
- package/dist/services/file.js +133 -0
- package/dist/services/file.js.map +1 -0
- package/dist/services/folder.d.ts +239 -0
- package/dist/services/folder.d.ts.map +1 -0
- package/dist/services/folder.js +248 -0
- package/dist/services/folder.js.map +1 -0
- package/dist/services/labChat.d.ts +165 -0
- package/dist/services/labChat.d.ts.map +1 -0
- package/dist/services/labChat.js +289 -0
- package/dist/services/labChat.js.map +1 -0
- package/dist/services/marketing.d.ts +50 -0
- package/dist/services/marketing.d.ts.map +1 -0
- package/dist/services/marketing.js +32 -0
- package/dist/services/marketing.js.map +1 -0
- package/dist/services/message.d.ts +95 -0
- package/dist/services/message.d.ts.map +1 -0
- package/dist/services/message.js +350 -0
- package/dist/services/message.js.map +1 -0
- package/dist/services/newtonChat.d.ts +22 -0
- package/dist/services/newtonChat.d.ts.map +1 -0
- package/dist/services/newtonChat.js +174 -0
- package/dist/services/newtonChat.js.map +1 -0
- package/dist/services/notification.d.ts +65 -0
- package/dist/services/notification.d.ts.map +1 -0
- package/dist/services/notification.js +33 -0
- package/dist/services/notification.js.map +1 -0
- package/dist/services/section.d.ts +53 -0
- package/dist/services/section.d.ts.map +1 -0
- package/dist/services/section.js +199 -0
- package/dist/services/section.js.map +1 -0
- package/dist/services/user.d.ts +48 -0
- package/dist/services/user.d.ts.map +1 -0
- package/dist/services/user.js +141 -0
- package/dist/services/user.js.map +1 -0
- package/dist/services/worksheet.d.ts +239 -0
- package/dist/services/worksheet.d.ts.map +1 -0
- package/dist/services/worksheet.js +235 -0
- package/dist/services/worksheet.js.map +1 -0
- package/dist/socket/handlers.d.ts.map +1 -1
- package/dist/socket/handlers.js +4 -0
- package/dist/socket/handlers.js.map +1 -0
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +4 -0
- package/dist/trpc.js.map +1 -0
- package/dist/types/trpc.d.ts.map +1 -1
- package/dist/types/trpc.js +4 -0
- package/dist/types/trpc.js.map +1 -0
- package/dist/utils/aiUser.d.ts +1 -3
- package/dist/utils/aiUser.d.ts.map +1 -1
- package/dist/utils/aiUser.js +8 -3
- package/dist/utils/aiUser.js.map +1 -0
- package/dist/utils/email.d.ts +12 -1
- package/dist/utils/email.d.ts.map +1 -1
- package/dist/utils/email.js +26 -4
- package/dist/utils/email.js.map +1 -0
- package/dist/utils/generateInviteCode.d.ts +1 -2
- package/dist/utils/generateInviteCode.d.ts.map +1 -1
- package/dist/utils/generateInviteCode.js +5 -2
- package/dist/utils/generateInviteCode.js.map +1 -0
- package/dist/utils/inference.d.ts +8 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +78 -10
- package/dist/utils/inference.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +35 -3
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
- package/dist/utils/prismaErrorHandler.js +7 -0
- package/dist/utils/prismaErrorHandler.js.map +1 -0
- package/dist/utils/prismaWrapper.d.ts +1 -0
- package/dist/utils/prismaWrapper.d.ts.map +1 -1
- package/dist/utils/prismaWrapper.js +8 -0
- package/dist/utils/prismaWrapper.js.map +1 -0
- package/docker-compose.yml +19 -0
- package/package.json +21 -4
- package/prisma/migrations/20251109122857_annuoncements_comments/migration.sql +30 -0
- package/prisma/migrations/20251109135555_reactions_announcements_comments/migration.sql +35 -0
- package/prisma/schema.prisma +180 -12
- package/scripts/test-pre-push.ts +14 -0
- package/src/index.ts +247 -52
- package/src/instrument.ts +15 -0
- package/src/lib/config/env.ts +132 -0
- package/src/lib/fileUpload.ts +81 -16
- package/src/lib/googleCloudStorage.ts +42 -6
- package/src/lib/jsonConversion.ts +12 -14
- package/src/lib/prisma.ts +23 -2
- package/src/lib/pusher.ts +11 -6
- package/src/lib/redis.ts +56 -0
- package/src/lib/thumbnailGenerator.ts +170 -168
- package/src/middleware/auth.ts +86 -137
- package/src/middleware/security.ts +80 -0
- package/src/models/agenda.ts +46 -0
- package/src/models/announcement.ts +134 -0
- package/src/models/assignment.ts +322 -0
- package/src/models/attendance.ts +208 -0
- package/src/models/auth.ts +247 -0
- package/src/models/class.ts +598 -0
- package/src/models/comment.ts +152 -0
- package/src/models/conversation.ts +200 -0
- package/src/models/event.ts +177 -0
- package/src/models/file.ts +129 -0
- package/src/models/folder.ts +225 -0
- package/src/models/labChat.ts +213 -0
- package/src/models/marketing.ts +45 -0
- package/src/models/message.ts +153 -0
- package/src/models/newtonChat.ts +70 -0
- package/src/models/notification.ts +54 -0
- package/src/models/section.ts +98 -0
- package/src/models/user.ts +47 -0
- package/src/models/worksheet.ts +294 -0
- package/src/pipelines/aiLabChat.ts +511 -0
- package/src/pipelines/aiNewtonChat.ts +347 -0
- package/src/pipelines/gradeWorksheet.ts +286 -0
- package/src/routers/_app.ts +6 -0
- package/src/routers/agenda.ts +3 -61
- package/src/routers/announcement.ts +622 -57
- package/src/routers/assignment.ts +157 -1688
- package/src/routers/attendance.ts +16 -277
- package/src/routers/auth.ts +79 -313
- package/src/routers/class.ts +265 -1038
- package/src/routers/comment.ts +76 -0
- package/src/routers/conversation.ts +53 -284
- package/src/routers/event.ts +50 -481
- package/src/routers/file.ts +45 -341
- package/src/routers/folder.ts +107 -836
- package/src/routers/labChat.ts +29 -960
- package/src/routers/marketing.ts +35 -77
- package/src/routers/message.ts +45 -571
- package/src/routers/newtonChat.ts +36 -0
- package/src/routers/notifications.ts +32 -82
- package/src/routers/section.ts +58 -200
- package/src/routers/user.ts +49 -226
- package/src/routers/worksheet.ts +252 -0
- package/src/seedDatabase.ts +330 -290
- package/src/services/agenda.ts +21 -0
- package/src/services/announcement.ts +290 -0
- package/src/services/assignment.ts +1198 -0
- package/src/services/attendance.ts +85 -0
- package/src/services/auth.ts +277 -0
- package/src/services/class.ts +622 -0
- package/src/services/comment.ts +106 -0
- package/src/services/conversation.ts +213 -0
- package/src/services/event.ts +231 -0
- package/src/services/file.ts +167 -0
- package/src/services/folder.ts +316 -0
- package/src/services/labChat.ts +352 -0
- package/src/services/marketing.ts +57 -0
- package/src/services/message.ts +461 -0
- package/src/services/newtonChat.ts +222 -0
- package/src/services/notification.ts +50 -0
- package/src/services/section.ts +283 -0
- package/src/services/user.ts +172 -0
- package/src/services/worksheet.ts +358 -0
- package/src/trpc.ts +4 -0
- package/src/utils/aiUser.ts +4 -3
- package/src/utils/email.ts +33 -4
- package/src/utils/generateInviteCode.ts +1 -3
- package/src/utils/inference.ts +89 -10
- package/src/utils/logger.ts +33 -3
- package/src/utils/prismaErrorHandler.ts +3 -0
- package/src/utils/prismaWrapper.ts +4 -0
- package/tests/globalSetup.ts +62 -0
- package/tests/helpers.ts +22 -0
- package/tests/middleware/security.test.ts +42 -0
- package/tests/routers/agenda.test.ts +138 -0
- package/tests/routers/announcement.test.ts +490 -0
- package/tests/routers/assignment.test.ts +837 -0
- package/tests/routers/attendance.test.ts +160 -0
- package/tests/routers/auth.test.ts +171 -0
- package/tests/{class.test.ts → routers/class.test.ts} +163 -92
- package/tests/routers/comment.test.ts +126 -0
- package/tests/routers/conversation.test.ts +145 -0
- package/tests/routers/event.test.ts +289 -0
- package/tests/routers/folder.test.ts +178 -0
- package/tests/routers/labChat.test.ts +115 -0
- package/tests/routers/marketing.test.ts +59 -0
- package/tests/routers/message.test.ts +123 -0
- package/tests/routers/notification.test.ts +69 -0
- package/tests/routers/section.test.ts +208 -0
- package/tests/server/rateLimit.test.ts +73 -0
- package/tests/setup.ts +39 -65
- package/tests/user.test.ts +136 -0
- package/tests/utils/aiUser.test.ts +22 -0
- package/tests/utils/generateInviteCode.test.ts +24 -0
- package/tests/utils/logger.test.ts +74 -0
- package/tests/utils/prismaErrorHandler.test.ts +101 -0
- package/tests/utils/prismaWrapper.test.ts +82 -0
- package/tests/worksheet.test.ts +181 -0
- package/tsconfig.json +9 -2
- package/vitest.config.ts +30 -1
- package/vitest.unit.config.ts +21 -0
- package/API_SPECIFICATION.md +0 -1597
- package/BASE64_REMOVAL_SUMMARY.md +0 -164
- package/CHAT_API_SPEC.md +0 -579
- package/LAB_CHAT_API_SPEC.md +0 -518
- package/dist/routers/school.d.ts +0 -208
- package/dist/routers/school.d.ts.map +0 -1
- package/dist/routers/school.js +0 -481
- package/tests/auth.test.ts +0 -25
package/dist/routers/folder.js
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2a91768b-a0eb-5556-9154-f76d73b01c49")}catch(e){}}();
|
|
1
3
|
import { z } from "zod";
|
|
2
|
-
import { createTRPCRouter, protectedProcedure, protectedClassMemberProcedure, protectedTeacherProcedure } from "../trpc.js";
|
|
3
|
-
import {
|
|
4
|
-
import { prisma } from "../lib/prisma.js";
|
|
5
|
-
import { createDirectUploadFiles } from "../lib/fileUpload.js";
|
|
6
|
-
// DEPRECATED: This schema is no longer used - files are uploaded directly to GCS
|
|
7
|
-
// Use directFileSchema instead
|
|
4
|
+
import { createTRPCRouter, protectedProcedure, protectedClassMemberProcedure, protectedTeacherProcedure, } from "../trpc.js";
|
|
5
|
+
import { createFolderRecord, getFolder, getChildFolders, getFolderChildren, getRootFolder, uploadFilesToFolder, deleteFolderRecord, moveFolder, updateFolderRecord, getFolderParents, getFolderUploadUrls, } from "../services/folder.js";
|
|
8
6
|
const createFolderSchema = z.object({
|
|
9
7
|
name: z.string(),
|
|
10
8
|
parentFolderId: z.string().optional(),
|
|
11
9
|
color: z.string().optional(),
|
|
12
10
|
});
|
|
13
|
-
// New schema for direct file uploads (no base64 data)
|
|
14
11
|
const directFileSchema = z.object({
|
|
15
12
|
name: z.string(),
|
|
16
13
|
type: z.string(),
|
|
17
14
|
size: z.number(),
|
|
18
|
-
// No data field - for direct file uploads
|
|
19
15
|
});
|
|
20
16
|
const uploadFilesToFolderSchema = z.object({
|
|
21
17
|
folderId: z.string(),
|
|
22
|
-
files: z.array(directFileSchema),
|
|
18
|
+
files: z.array(directFileSchema),
|
|
23
19
|
});
|
|
24
20
|
const getRootFolderSchema = z.object({
|
|
25
21
|
classId: z.string(),
|
|
@@ -28,488 +24,43 @@ export const folderRouter = createTRPCRouter({
|
|
|
28
24
|
create: protectedTeacherProcedure
|
|
29
25
|
.input(createFolderSchema)
|
|
30
26
|
.mutation(async ({ ctx, input }) => {
|
|
31
|
-
const { classId, name, color } = input;
|
|
32
|
-
let parentFolderId = input.parentFolderId || null;
|
|
33
27
|
if (!ctx.user) {
|
|
34
|
-
throw new
|
|
35
|
-
code: "UNAUTHORIZED",
|
|
36
|
-
message: "You must be logged in to create a folder",
|
|
37
|
-
});
|
|
28
|
+
throw new Error("You must be logged in to create a folder");
|
|
38
29
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
some: {
|
|
45
|
-
id: ctx.user.id,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
},
|
|
30
|
+
return createFolderRecord(ctx.user.id, {
|
|
31
|
+
classId: input.classId,
|
|
32
|
+
name: input.name,
|
|
33
|
+
parentFolderId: input.parentFolderId,
|
|
34
|
+
color: input.color,
|
|
49
35
|
});
|
|
50
|
-
if (!classData) {
|
|
51
|
-
throw new TRPCError({
|
|
52
|
-
code: "NOT_FOUND",
|
|
53
|
-
message: "Class not found or you are not a teacher",
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
// If no parent folder specified, find or create the class parent folder
|
|
57
|
-
if (!parentFolderId) {
|
|
58
|
-
let classParentFolder = await prisma.folder.findFirst({
|
|
59
|
-
where: {
|
|
60
|
-
classId: classId,
|
|
61
|
-
parentFolderId: null,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
if (!classParentFolder) {
|
|
65
|
-
// Create parent folder if it doesn't exist
|
|
66
|
-
classParentFolder = await prisma.folder.create({
|
|
67
|
-
data: {
|
|
68
|
-
name: "Class Files",
|
|
69
|
-
class: {
|
|
70
|
-
connect: { id: classId },
|
|
71
|
-
},
|
|
72
|
-
...(color && {
|
|
73
|
-
color: color,
|
|
74
|
-
}),
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
parentFolderId = classParentFolder.id;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// Check if specified parent folder exists and belongs to the class
|
|
82
|
-
const parentFolder = await prisma.folder.findFirst({
|
|
83
|
-
where: {
|
|
84
|
-
id: parentFolderId,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
if (!parentFolder) {
|
|
88
|
-
throw new TRPCError({
|
|
89
|
-
code: "NOT_FOUND",
|
|
90
|
-
message: "Parent folder not found",
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const folder = await prisma.folder.create({
|
|
95
|
-
data: {
|
|
96
|
-
name,
|
|
97
|
-
...(parentFolderId && {
|
|
98
|
-
parentFolder: {
|
|
99
|
-
connect: { id: parentFolderId },
|
|
100
|
-
},
|
|
101
|
-
}),
|
|
102
|
-
...(color && {
|
|
103
|
-
color: color,
|
|
104
|
-
}),
|
|
105
|
-
},
|
|
106
|
-
include: {
|
|
107
|
-
files: {
|
|
108
|
-
select: {
|
|
109
|
-
id: true,
|
|
110
|
-
name: true,
|
|
111
|
-
type: true,
|
|
112
|
-
size: true,
|
|
113
|
-
uploadedAt: true,
|
|
114
|
-
user: {
|
|
115
|
-
select: {
|
|
116
|
-
id: true,
|
|
117
|
-
username: true,
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
childFolders: {
|
|
123
|
-
select: {
|
|
124
|
-
id: true,
|
|
125
|
-
name: true,
|
|
126
|
-
_count: {
|
|
127
|
-
select: {
|
|
128
|
-
files: true,
|
|
129
|
-
childFolders: true,
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
return folder;
|
|
137
36
|
}),
|
|
138
37
|
get: protectedClassMemberProcedure
|
|
139
|
-
.input(z.object({
|
|
140
|
-
|
|
141
|
-
classId: z.string(),
|
|
142
|
-
}))
|
|
143
|
-
.query(async ({ ctx, input }) => {
|
|
144
|
-
const { classId, folderId } = input;
|
|
145
|
-
// Get specific folder
|
|
146
|
-
const folder = await prisma.folder.findFirst({
|
|
147
|
-
where: {
|
|
148
|
-
id: folderId,
|
|
149
|
-
},
|
|
150
|
-
include: {
|
|
151
|
-
files: {
|
|
152
|
-
select: {
|
|
153
|
-
id: true,
|
|
154
|
-
name: true,
|
|
155
|
-
type: true,
|
|
156
|
-
size: true,
|
|
157
|
-
uploadedAt: true,
|
|
158
|
-
user: {
|
|
159
|
-
select: {
|
|
160
|
-
id: true,
|
|
161
|
-
username: true,
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
orderBy: {
|
|
166
|
-
uploadedAt: 'desc',
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
childFolders: {
|
|
170
|
-
select: {
|
|
171
|
-
id: true,
|
|
172
|
-
name: true,
|
|
173
|
-
color: true,
|
|
174
|
-
_count: {
|
|
175
|
-
select: {
|
|
176
|
-
files: true,
|
|
177
|
-
childFolders: true,
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
orderBy: {
|
|
182
|
-
name: 'asc',
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
parentFolder: {
|
|
186
|
-
select: {
|
|
187
|
-
id: true,
|
|
188
|
-
name: true,
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
if (!folder) {
|
|
194
|
-
throw new TRPCError({
|
|
195
|
-
code: "NOT_FOUND",
|
|
196
|
-
message: "Folder not found",
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
return folder;
|
|
200
|
-
}),
|
|
38
|
+
.input(z.object({ folderId: z.string(), classId: z.string() }))
|
|
39
|
+
.query(({ input }) => getFolder(input.folderId)),
|
|
201
40
|
getChildFolders: protectedClassMemberProcedure
|
|
202
|
-
.input(z.object({
|
|
203
|
-
|
|
204
|
-
}))
|
|
205
|
-
.query(async ({ ctx, input }) => {
|
|
206
|
-
const { classId } = input;
|
|
207
|
-
// Get the parent folder for the class (or create it if it doesn't exist)
|
|
208
|
-
let parentFolder = await prisma.folder.findFirst({
|
|
209
|
-
where: {
|
|
210
|
-
classId: classId,
|
|
211
|
-
parentFolderId: null,
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
if (!parentFolder) {
|
|
215
|
-
// Create parent folder if it doesn't exist
|
|
216
|
-
parentFolder = await prisma.folder.create({
|
|
217
|
-
data: {
|
|
218
|
-
name: "Class Files",
|
|
219
|
-
class: {
|
|
220
|
-
connect: { id: classId },
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
// Get all child folders of the parent
|
|
226
|
-
const childFolders = await prisma.folder.findMany({
|
|
227
|
-
where: {
|
|
228
|
-
parentFolderId: parentFolder.id,
|
|
229
|
-
},
|
|
230
|
-
include: {
|
|
231
|
-
files: {
|
|
232
|
-
select: {
|
|
233
|
-
id: true,
|
|
234
|
-
name: true,
|
|
235
|
-
type: true,
|
|
236
|
-
size: true,
|
|
237
|
-
uploadedAt: true,
|
|
238
|
-
user: {
|
|
239
|
-
select: {
|
|
240
|
-
id: true,
|
|
241
|
-
username: true,
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
orderBy: {
|
|
246
|
-
uploadedAt: 'desc',
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
childFolders: {
|
|
250
|
-
select: {
|
|
251
|
-
id: true,
|
|
252
|
-
name: true,
|
|
253
|
-
_count: {
|
|
254
|
-
select: {
|
|
255
|
-
files: true,
|
|
256
|
-
childFolders: true,
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
orderBy: {
|
|
261
|
-
name: 'asc',
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
orderBy: {
|
|
266
|
-
name: 'asc',
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
return childFolders;
|
|
270
|
-
}),
|
|
41
|
+
.input(z.object({ classId: z.string() }))
|
|
42
|
+
.query(({ input }) => getChildFolders(input.classId)),
|
|
271
43
|
getFolderChildren: protectedClassMemberProcedure
|
|
272
|
-
.input(z.object({
|
|
273
|
-
folderId
|
|
274
|
-
classId: z.string(),
|
|
275
|
-
}))
|
|
276
|
-
.query(async ({ ctx, input }) => {
|
|
277
|
-
const { folderId, classId } = input;
|
|
278
|
-
// Get direct children of the specified folder
|
|
279
|
-
const children = await prisma.folder.findMany({
|
|
280
|
-
where: {
|
|
281
|
-
parentFolderId: folderId,
|
|
282
|
-
classId: classId,
|
|
283
|
-
},
|
|
284
|
-
include: {
|
|
285
|
-
files: {
|
|
286
|
-
select: {
|
|
287
|
-
id: true,
|
|
288
|
-
name: true,
|
|
289
|
-
type: true,
|
|
290
|
-
size: true,
|
|
291
|
-
uploadedAt: true,
|
|
292
|
-
user: {
|
|
293
|
-
select: {
|
|
294
|
-
id: true,
|
|
295
|
-
username: true,
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
orderBy: {
|
|
300
|
-
uploadedAt: 'desc',
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
childFolders: {
|
|
304
|
-
select: {
|
|
305
|
-
id: true,
|
|
306
|
-
name: true,
|
|
307
|
-
color: true,
|
|
308
|
-
_count: {
|
|
309
|
-
select: {
|
|
310
|
-
files: true,
|
|
311
|
-
childFolders: true,
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
orderBy: {
|
|
316
|
-
name: 'asc',
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
orderBy: {
|
|
321
|
-
name: 'asc',
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
return children;
|
|
325
|
-
}),
|
|
44
|
+
.input(z.object({ folderId: z.string(), classId: z.string() }))
|
|
45
|
+
.query(({ input }) => getFolderChildren(input.folderId, input.classId)),
|
|
326
46
|
getRootFolder: protectedClassMemberProcedure
|
|
327
47
|
.input(getRootFolderSchema)
|
|
328
|
-
.query(
|
|
329
|
-
const { classId } = input;
|
|
330
|
-
// Get or create the parent folder for the class
|
|
331
|
-
let parentFolder = await prisma.folder.findFirst({
|
|
332
|
-
where: {
|
|
333
|
-
classId: classId,
|
|
334
|
-
parentFolderId: null,
|
|
335
|
-
},
|
|
336
|
-
});
|
|
337
|
-
if (!parentFolder) {
|
|
338
|
-
// Create parent folder if it doesn't exist
|
|
339
|
-
parentFolder = await prisma.folder.create({
|
|
340
|
-
data: {
|
|
341
|
-
name: "Class Files",
|
|
342
|
-
class: {
|
|
343
|
-
connect: { id: classId },
|
|
344
|
-
},
|
|
345
|
-
},
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
// Get the parent folder with its files and child folders
|
|
349
|
-
const rootFolder = await prisma.folder.findFirst({
|
|
350
|
-
where: {
|
|
351
|
-
id: parentFolder.id,
|
|
352
|
-
classId: classId,
|
|
353
|
-
},
|
|
354
|
-
include: {
|
|
355
|
-
files: {
|
|
356
|
-
select: {
|
|
357
|
-
id: true,
|
|
358
|
-
name: true,
|
|
359
|
-
type: true,
|
|
360
|
-
size: true,
|
|
361
|
-
uploadedAt: true,
|
|
362
|
-
user: {
|
|
363
|
-
select: {
|
|
364
|
-
id: true,
|
|
365
|
-
username: true,
|
|
366
|
-
},
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
orderBy: {
|
|
370
|
-
uploadedAt: 'desc',
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
childFolders: {
|
|
374
|
-
select: {
|
|
375
|
-
id: true,
|
|
376
|
-
name: true,
|
|
377
|
-
color: true,
|
|
378
|
-
files: {
|
|
379
|
-
select: {
|
|
380
|
-
id: true,
|
|
381
|
-
},
|
|
382
|
-
},
|
|
383
|
-
childFolders: {
|
|
384
|
-
select: {
|
|
385
|
-
id: true,
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
},
|
|
389
|
-
orderBy: {
|
|
390
|
-
name: 'asc',
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
});
|
|
395
|
-
return rootFolder;
|
|
396
|
-
}),
|
|
48
|
+
.query(({ input }) => getRootFolder(input.classId)),
|
|
397
49
|
uploadFiles: protectedTeacherProcedure
|
|
398
50
|
.input(uploadFilesToFolderSchema)
|
|
399
51
|
.mutation(async ({ ctx, input }) => {
|
|
400
|
-
const { classId, folderId, files } = input;
|
|
401
52
|
if (!ctx.user) {
|
|
402
|
-
throw new
|
|
403
|
-
code: "UNAUTHORIZED",
|
|
404
|
-
message: "You must be logged in to upload files",
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
// Verify user is a teacher of the class
|
|
408
|
-
const classData = await prisma.class.findFirst({
|
|
409
|
-
where: {
|
|
410
|
-
id: classId,
|
|
411
|
-
teachers: {
|
|
412
|
-
some: {
|
|
413
|
-
id: ctx.user.id,
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
});
|
|
418
|
-
if (!classData) {
|
|
419
|
-
throw new TRPCError({
|
|
420
|
-
code: "NOT_FOUND",
|
|
421
|
-
message: "Class not found or you are not a teacher",
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
// Verify folder exists and belongs to the class
|
|
425
|
-
const folder = await prisma.folder.findFirst({
|
|
426
|
-
where: {
|
|
427
|
-
id: folderId,
|
|
428
|
-
},
|
|
429
|
-
});
|
|
430
|
-
if (!folder) {
|
|
431
|
-
throw new TRPCError({
|
|
432
|
-
code: "NOT_FOUND",
|
|
433
|
-
message: "Folder not found",
|
|
434
|
-
});
|
|
53
|
+
throw new Error("You must be logged in to upload files");
|
|
435
54
|
}
|
|
436
|
-
|
|
437
|
-
const uploadedFiles = await createDirectUploadFiles(files, ctx.user.id, folder.id);
|
|
438
|
-
// Create file records in database
|
|
439
|
-
// const fileRecords = await prisma.file.createMany({
|
|
440
|
-
// data: uploadedFiles.map(file => ({
|
|
441
|
-
// name: file.name,
|
|
442
|
-
// type: file.type,
|
|
443
|
-
// size: file.size,
|
|
444
|
-
// path: file.path,
|
|
445
|
-
// userId: ctx.user!.id,
|
|
446
|
-
// folderId: folderId,
|
|
447
|
-
// ...(file.thumbnailId && {
|
|
448
|
-
// thumbnailId: file.thumbnailId,
|
|
449
|
-
// }),
|
|
450
|
-
// })),
|
|
451
|
-
// });
|
|
452
|
-
return {
|
|
453
|
-
success: true,
|
|
454
|
-
uploadedCount: uploadedFiles.length,
|
|
455
|
-
};
|
|
55
|
+
return uploadFilesToFolder(ctx.user.id, input.classId, input.folderId, input.files);
|
|
456
56
|
}),
|
|
457
57
|
delete: protectedTeacherProcedure
|
|
458
|
-
.input(z.object({
|
|
459
|
-
classId: z.string(),
|
|
460
|
-
folderId: z.string(),
|
|
461
|
-
}))
|
|
58
|
+
.input(z.object({ classId: z.string(), folderId: z.string() }))
|
|
462
59
|
.mutation(async ({ ctx, input }) => {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const classData = await prisma.class.findFirst({
|
|
466
|
-
where: {
|
|
467
|
-
id: classId,
|
|
468
|
-
teachers: {
|
|
469
|
-
some: {
|
|
470
|
-
id: ctx.user.id,
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
});
|
|
475
|
-
if (!classData) {
|
|
476
|
-
throw new TRPCError({
|
|
477
|
-
code: "FORBIDDEN",
|
|
478
|
-
message: "Class not found or you are not a teacher",
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
// Verify folder exists and belongs to the class
|
|
482
|
-
const folder = await prisma.folder.findFirst({
|
|
483
|
-
where: {
|
|
484
|
-
id: folderId,
|
|
485
|
-
classId: classId,
|
|
486
|
-
},
|
|
487
|
-
include: {
|
|
488
|
-
_count: {
|
|
489
|
-
select: {
|
|
490
|
-
files: true,
|
|
491
|
-
childFolders: true,
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
});
|
|
496
|
-
if (!folder) {
|
|
497
|
-
throw new TRPCError({
|
|
498
|
-
code: "NOT_FOUND",
|
|
499
|
-
message: "Folder not found",
|
|
500
|
-
});
|
|
60
|
+
if (!ctx.user) {
|
|
61
|
+
throw new Error("You must be logged in to delete folders");
|
|
501
62
|
}
|
|
502
|
-
|
|
503
|
-
await prisma.folder.delete({
|
|
504
|
-
where: {
|
|
505
|
-
id: folderId,
|
|
506
|
-
},
|
|
507
|
-
});
|
|
508
|
-
return {
|
|
509
|
-
success: true,
|
|
510
|
-
deletedFiles: folder._count.files,
|
|
511
|
-
deletedFolders: folder._count.childFolders + 1, // +1 for the folder itself
|
|
512
|
-
};
|
|
63
|
+
return deleteFolderRecord(ctx.user.id, input.classId, input.folderId);
|
|
513
64
|
}),
|
|
514
65
|
move: protectedTeacherProcedure
|
|
515
66
|
.input(z.object({
|
|
@@ -518,125 +69,10 @@ export const folderRouter = createTRPCRouter({
|
|
|
518
69
|
classId: z.string(),
|
|
519
70
|
}))
|
|
520
71
|
.mutation(async ({ ctx, input }) => {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const classData = await prisma.class.findFirst({
|
|
524
|
-
where: {
|
|
525
|
-
id: classId,
|
|
526
|
-
teachers: {
|
|
527
|
-
some: {
|
|
528
|
-
id: ctx.user.id,
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
},
|
|
532
|
-
});
|
|
533
|
-
if (!classData) {
|
|
534
|
-
throw new TRPCError({
|
|
535
|
-
code: "FORBIDDEN",
|
|
536
|
-
message: "You must be a teacher of this class to move folders",
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
// Get the folder to move
|
|
540
|
-
const folder = await prisma.folder.findFirst({
|
|
541
|
-
where: {
|
|
542
|
-
id: folderId,
|
|
543
|
-
},
|
|
544
|
-
});
|
|
545
|
-
if (!folder) {
|
|
546
|
-
throw new TRPCError({
|
|
547
|
-
code: "NOT_FOUND",
|
|
548
|
-
message: "Folder not found",
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
// Prevent moving the root folder
|
|
552
|
-
if (!folder.parentFolderId) {
|
|
553
|
-
throw new TRPCError({
|
|
554
|
-
code: "BAD_REQUEST",
|
|
555
|
-
message: "Cannot move the root folder",
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
// If target parent folder is specified, verify it exists and belongs to the class
|
|
559
|
-
if (targetParentFolderId) {
|
|
560
|
-
const targetParentFolder = await prisma.folder.findFirst({
|
|
561
|
-
where: {
|
|
562
|
-
id: targetParentFolderId,
|
|
563
|
-
},
|
|
564
|
-
});
|
|
565
|
-
if (!targetParentFolder) {
|
|
566
|
-
throw new TRPCError({
|
|
567
|
-
code: "NOT_FOUND",
|
|
568
|
-
message: "Target parent folder not found",
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
// Prevent moving a folder into itself or its descendants
|
|
572
|
-
if (targetParentFolderId === folderId) {
|
|
573
|
-
throw new TRPCError({
|
|
574
|
-
code: "BAD_REQUEST",
|
|
575
|
-
message: "Cannot move a folder into itself",
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
// Check if target is a descendant of the folder being moved
|
|
579
|
-
let currentParent = targetParentFolder;
|
|
580
|
-
while (currentParent?.parentFolderId) {
|
|
581
|
-
if (currentParent.parentFolderId === folderId) {
|
|
582
|
-
throw new TRPCError({
|
|
583
|
-
code: "BAD_REQUEST",
|
|
584
|
-
message: "Cannot move a folder into its descendant",
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
currentParent = await prisma.folder.findUnique({
|
|
588
|
-
where: { id: currentParent.parentFolderId },
|
|
589
|
-
});
|
|
590
|
-
if (!currentParent)
|
|
591
|
-
break;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
// Moving to root - verify the folder isn't already at root
|
|
596
|
-
if (!folder.parentFolderId) {
|
|
597
|
-
throw new TRPCError({
|
|
598
|
-
code: "BAD_REQUEST",
|
|
599
|
-
message: "Folder is already at root level",
|
|
600
|
-
});
|
|
601
|
-
}
|
|
72
|
+
if (!ctx.user) {
|
|
73
|
+
throw new Error("You must be logged in to move folders");
|
|
602
74
|
}
|
|
603
|
-
|
|
604
|
-
const updatedFolder = await prisma.folder.update({
|
|
605
|
-
where: { id: folderId },
|
|
606
|
-
data: {
|
|
607
|
-
parentFolderId: targetParentFolderId,
|
|
608
|
-
},
|
|
609
|
-
include: {
|
|
610
|
-
files: {
|
|
611
|
-
select: {
|
|
612
|
-
id: true,
|
|
613
|
-
name: true,
|
|
614
|
-
type: true,
|
|
615
|
-
size: true,
|
|
616
|
-
uploadedAt: true,
|
|
617
|
-
user: {
|
|
618
|
-
select: {
|
|
619
|
-
id: true,
|
|
620
|
-
username: true,
|
|
621
|
-
},
|
|
622
|
-
},
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
childFolders: {
|
|
626
|
-
select: {
|
|
627
|
-
id: true,
|
|
628
|
-
name: true,
|
|
629
|
-
_count: {
|
|
630
|
-
select: {
|
|
631
|
-
files: true,
|
|
632
|
-
childFolders: true,
|
|
633
|
-
},
|
|
634
|
-
},
|
|
635
|
-
},
|
|
636
|
-
},
|
|
637
|
-
},
|
|
638
|
-
});
|
|
639
|
-
return updatedFolder;
|
|
75
|
+
return moveFolder(ctx.user.id, input.folderId, input.targetParentFolderId, input.classId);
|
|
640
76
|
}),
|
|
641
77
|
update: protectedTeacherProcedure
|
|
642
78
|
.input(z.object({
|
|
@@ -645,87 +81,10 @@ export const folderRouter = createTRPCRouter({
|
|
|
645
81
|
color: z.string().optional(),
|
|
646
82
|
classId: z.string(),
|
|
647
83
|
}))
|
|
648
|
-
.mutation(
|
|
649
|
-
const { folderId, name, color, classId } = input;
|
|
650
|
-
// Get the folder
|
|
651
|
-
const folder = await prisma.folder.findFirst({
|
|
652
|
-
where: {
|
|
653
|
-
id: folderId,
|
|
654
|
-
},
|
|
655
|
-
});
|
|
656
|
-
if (!folder) {
|
|
657
|
-
throw new TRPCError({
|
|
658
|
-
code: "NOT_FOUND",
|
|
659
|
-
message: "Folder not found",
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
// Validate new name
|
|
663
|
-
if (!name.trim()) {
|
|
664
|
-
throw new TRPCError({
|
|
665
|
-
code: "BAD_REQUEST",
|
|
666
|
-
message: "Folder name cannot be empty",
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
// Rename the folder
|
|
670
|
-
const updatedFolder = await prisma.folder.update({
|
|
671
|
-
where: { id: folderId },
|
|
672
|
-
data: {
|
|
673
|
-
name: name.trim(),
|
|
674
|
-
...(color && {
|
|
675
|
-
color: color,
|
|
676
|
-
}),
|
|
677
|
-
},
|
|
678
|
-
include: {
|
|
679
|
-
files: {
|
|
680
|
-
select: {
|
|
681
|
-
id: true,
|
|
682
|
-
name: true,
|
|
683
|
-
type: true,
|
|
684
|
-
size: true,
|
|
685
|
-
uploadedAt: true,
|
|
686
|
-
user: {
|
|
687
|
-
select: {
|
|
688
|
-
id: true,
|
|
689
|
-
username: true,
|
|
690
|
-
},
|
|
691
|
-
},
|
|
692
|
-
},
|
|
693
|
-
},
|
|
694
|
-
childFolders: {
|
|
695
|
-
select: {
|
|
696
|
-
id: true,
|
|
697
|
-
name: true,
|
|
698
|
-
_count: {
|
|
699
|
-
select: {
|
|
700
|
-
files: true,
|
|
701
|
-
childFolders: true,
|
|
702
|
-
},
|
|
703
|
-
},
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
},
|
|
707
|
-
});
|
|
708
|
-
return updatedFolder;
|
|
709
|
-
}),
|
|
84
|
+
.mutation(({ input }) => updateFolderRecord(input.folderId, input.name, input.color, input.classId)),
|
|
710
85
|
getParents: protectedProcedure
|
|
711
|
-
.input(z.object({
|
|
712
|
-
|
|
713
|
-
}))
|
|
714
|
-
.query(async ({ ctx, input }) => {
|
|
715
|
-
const { folderId } = input;
|
|
716
|
-
let currentParent = folderId;
|
|
717
|
-
const parents = [];
|
|
718
|
-
while (currentParent) {
|
|
719
|
-
const parent = await prisma.folder.findFirst({
|
|
720
|
-
where: {
|
|
721
|
-
id: currentParent,
|
|
722
|
-
},
|
|
723
|
-
});
|
|
724
|
-
currentParent = parent?.parentFolderId;
|
|
725
|
-
parents.push(parent);
|
|
726
|
-
}
|
|
727
|
-
return parents;
|
|
728
|
-
}),
|
|
86
|
+
.input(z.object({ folderId: z.string() }))
|
|
87
|
+
.query(({ input }) => getFolderParents(input.folderId)),
|
|
729
88
|
getFolderUploadUrls: protectedTeacherProcedure
|
|
730
89
|
.input(z.object({
|
|
731
90
|
classId: z.string(),
|
|
@@ -733,72 +92,10 @@ export const folderRouter = createTRPCRouter({
|
|
|
733
92
|
files: z.array(directFileSchema),
|
|
734
93
|
}))
|
|
735
94
|
.mutation(async ({ ctx, input }) => {
|
|
736
|
-
const { classId, folderId, files } = input;
|
|
737
95
|
if (!ctx.user) {
|
|
738
|
-
throw new
|
|
739
|
-
code: "UNAUTHORIZED",
|
|
740
|
-
message: "You must be logged in to upload files",
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
// Verify user is a teacher of the class
|
|
744
|
-
const classData = await prisma.class.findFirst({
|
|
745
|
-
where: {
|
|
746
|
-
id: classId,
|
|
747
|
-
teachers: {
|
|
748
|
-
some: {
|
|
749
|
-
id: ctx.user.id,
|
|
750
|
-
},
|
|
751
|
-
},
|
|
752
|
-
},
|
|
753
|
-
});
|
|
754
|
-
if (!classData) {
|
|
755
|
-
throw new TRPCError({
|
|
756
|
-
code: "NOT_FOUND",
|
|
757
|
-
message: "Class not found or you are not a teacher",
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
// Verify folder exists
|
|
761
|
-
const folder = await prisma.folder.findUnique({
|
|
762
|
-
where: {
|
|
763
|
-
id: folderId,
|
|
764
|
-
},
|
|
765
|
-
});
|
|
766
|
-
if (!folder) {
|
|
767
|
-
throw new TRPCError({
|
|
768
|
-
code: "NOT_FOUND",
|
|
769
|
-
message: "Folder not found",
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
// Verify folder belongs to the class by traversing parent hierarchy
|
|
773
|
-
// Only root folders have classId, child folders use parentFolderId
|
|
774
|
-
let currentFolder = folder;
|
|
775
|
-
let belongsToClass = false;
|
|
776
|
-
while (currentFolder) {
|
|
777
|
-
// Check if we've reached a root folder with the matching classId
|
|
778
|
-
if (currentFolder.classId === classId) {
|
|
779
|
-
belongsToClass = true;
|
|
780
|
-
break;
|
|
781
|
-
}
|
|
782
|
-
// If this folder has a parent, traverse up
|
|
783
|
-
if (currentFolder.parentFolderId) {
|
|
784
|
-
currentFolder = await prisma.folder.findUnique({
|
|
785
|
-
where: { id: currentFolder.parentFolderId },
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
else {
|
|
789
|
-
// Reached a root folder without matching classId
|
|
790
|
-
break;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
if (!belongsToClass) {
|
|
794
|
-
throw new TRPCError({
|
|
795
|
-
code: "FORBIDDEN",
|
|
796
|
-
message: "Folder does not belong to this class",
|
|
797
|
-
});
|
|
96
|
+
throw new Error("You must be logged in to upload files");
|
|
798
97
|
}
|
|
799
|
-
|
|
800
|
-
const uploadFiles = await createDirectUploadFiles(files, ctx.user.id, folder.id);
|
|
801
|
-
return uploadFiles;
|
|
98
|
+
return getFolderUploadUrls(ctx.user.id, input.classId, input.folderId, input.files);
|
|
802
99
|
}),
|
|
803
100
|
confirmFolderUpload: protectedTeacherProcedure
|
|
804
101
|
.input(z.object({
|
|
@@ -806,17 +103,13 @@ export const folderRouter = createTRPCRouter({
|
|
|
806
103
|
uploadSuccess: z.boolean(),
|
|
807
104
|
}))
|
|
808
105
|
.mutation(async ({ ctx, input }) => {
|
|
809
|
-
const { fileId, uploadSuccess } = input;
|
|
810
106
|
if (!ctx.user) {
|
|
811
|
-
throw new
|
|
812
|
-
code: "UNAUTHORIZED",
|
|
813
|
-
message: "You must be logged in to confirm uploads",
|
|
814
|
-
});
|
|
107
|
+
throw new Error("You must be logged in to confirm uploads");
|
|
815
108
|
}
|
|
816
|
-
// Import the confirmDirectUpload function
|
|
817
109
|
const { confirmDirectUpload } = await import("../lib/fileUpload.js");
|
|
818
|
-
|
|
819
|
-
await confirmDirectUpload(fileId, uploadSuccess);
|
|
110
|
+
await confirmDirectUpload(input.fileId, input.uploadSuccess);
|
|
820
111
|
return { success: true };
|
|
821
112
|
}),
|
|
822
113
|
});
|
|
114
|
+
//# sourceMappingURL=folder.js.map
|
|
115
|
+
//# debugId=2a91768b-a0eb-5556-9154-f76d73b01c49
|