@studious-lms/server 1.2.53 → 1.3.0
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 +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -8
- package/dist/index.js.map +1 -1
- package/dist/lib/config/env.d.ts +21 -0
- package/dist/lib/config/env.d.ts.map +1 -1
- package/dist/lib/config/env.js +8 -2
- package/dist/lib/config/env.js.map +1 -1
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +2 -2
- package/dist/lib/fileUpload.js.map +1 -1
- package/dist/lib/googleCloudStorage.d.ts +6 -0
- package/dist/lib/googleCloudStorage.d.ts.map +1 -1
- package/dist/lib/googleCloudStorage.js +19 -2
- package/dist/lib/googleCloudStorage.js.map +1 -1
- package/dist/lib/pusher.d.ts +4 -1
- package/dist/lib/pusher.d.ts.map +1 -1
- package/dist/lib/pusher.js +6 -3
- package/dist/lib/pusher.js.map +1 -1
- 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 +157 -160
- package/dist/lib/thumbnailGenerator.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +33 -95
- package/dist/middleware/auth.js.map +1 -1
- 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 +1393 -1267
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/agenda.d.ts +22 -22
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +4 -65
- package/dist/routers/agenda.js.map +1 -1
- package/dist/routers/announcement.d.ts +16 -16
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +37 -446
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +300 -378
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +78 -1868
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/attendance.d.ts +19 -9
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +7 -264
- package/dist/routers/attendance.js.map +1 -1
- package/dist/routers/auth.d.ts +2 -2
- package/dist/routers/auth.d.ts.map +1 -1
- package/dist/routers/auth.js +29 -354
- package/dist/routers/auth.js.map +1 -1
- package/dist/routers/class.d.ts +139 -68
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +82 -1052
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/comment.d.ts +6 -42
- package/dist/routers/comment.d.ts.map +1 -1
- package/dist/routers/comment.js +24 -244
- package/dist/routers/comment.js.map +1 -1
- package/dist/routers/conversation.d.ts +45 -7
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/conversation.js +19 -327
- package/dist/routers/conversation.js.map +1 -1
- package/dist/routers/event.d.ts +36 -36
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/event.js +13 -433
- package/dist/routers/event.js.map +1 -1
- package/dist/routers/file.d.ts +2 -2
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +9 -323
- package/dist/routers/file.js.map +1 -1
- package/dist/routers/folder.d.ts +21 -14
- package/dist/routers/folder.d.ts.map +1 -1
- package/dist/routers/folder.js +34 -745
- package/dist/routers/folder.js.map +1 -1
- package/dist/routers/labChat.d.ts +11 -10
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +19 -570
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/marketing.d.ts +1 -1
- package/dist/routers/marketing.d.ts.map +1 -1
- package/dist/routers/marketing.js +7 -56
- package/dist/routers/marketing.js.map +1 -1
- package/dist/routers/message.d.ts +2 -2
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +27 -522
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/newtonChat.d.ts +1 -1
- package/dist/routers/newtonChat.d.ts.map +1 -1
- package/dist/routers/newtonChat.js +7 -246
- package/dist/routers/newtonChat.js.map +1 -1
- package/dist/routers/notifications.d.ts +4 -4
- package/dist/routers/notifications.d.ts.map +1 -1
- package/dist/routers/notifications.js +18 -83
- package/dist/routers/notifications.js.map +1 -1
- package/dist/routers/section.d.ts +4 -4
- package/dist/routers/section.d.ts.map +1 -1
- package/dist/routers/section.js +14 -286
- package/dist/routers/section.js.map +1 -1
- package/dist/routers/user.d.ts +1 -1
- package/dist/routers/user.d.ts.map +1 -1
- package/dist/routers/user.js +32 -207
- package/dist/routers/user.js.map +1 -1
- package/dist/routers/worksheet.d.ts +51 -38
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +79 -394
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/seedDatabase.d.ts +1 -1
- package/dist/server/pipelines/gradeWorksheet.d.ts +6 -6
- package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.js +12 -5
- package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
- 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/utils/aiUser.d.ts +1 -3
- package/dist/utils/aiUser.d.ts.map +1 -1
- package/dist/utils/aiUser.js +6 -5
- package/dist/utils/aiUser.js.map +1 -1
- package/dist/utils/email.d.ts +3 -0
- package/dist/utils/email.d.ts.map +1 -1
- package/dist/utils/email.js +7 -4
- package/dist/utils/email.js.map +1 -1
- package/dist/utils/generateInviteCode.d.ts +1 -2
- package/dist/utils/generateInviteCode.d.ts.map +1 -1
- package/dist/utils/generateInviteCode.js +3 -4
- package/dist/utils/generateInviteCode.js.map +1 -1
- package/dist/utils/inference.d.ts +3 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +7 -4
- package/dist/utils/inference.js.map +1 -1
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +5 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
- package/dist/utils/prismaErrorHandler.js +5 -2
- package/dist/utils/prismaErrorHandler.js.map +1 -1
- package/dist/utils/prismaWrapper.d.ts +1 -0
- package/dist/utils/prismaWrapper.d.ts.map +1 -1
- package/dist/utils/prismaWrapper.js +6 -2
- package/dist/utils/prismaWrapper.js.map +1 -1
- package/docker-compose.yml +5 -0
- package/package.json +4 -3
- package/src/index.ts +119 -12
- package/src/lib/config/env.ts +6 -0
- package/src/lib/fileUpload.ts +0 -1
- package/src/lib/googleCloudStorage.ts +17 -0
- package/src/lib/pusher.ts +5 -1
- package/src/lib/redis.ts +56 -0
- package/src/lib/thumbnailGenerator.ts +170 -168
- package/src/middleware/auth.ts +80 -137
- 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/{server/pipelines → pipelines}/aiLabChat.ts +11 -7
- package/src/{server/pipelines → pipelines}/aiNewtonChat.ts +9 -5
- package/src/{server/pipelines → pipelines}/gradeWorksheet.ts +25 -14
- package/src/routers/agenda.ts +3 -66
- package/src/routers/announcement.ts +54 -495
- package/src/routers/assignment.ts +126 -2018
- package/src/routers/attendance.ts +15 -276
- package/src/routers/auth.ts +79 -442
- package/src/routers/class.ts +263 -1187
- package/src/routers/comment.ts +61 -288
- package/src/routers/conversation.ts +51 -360
- package/src/routers/event.ts +50 -481
- package/src/routers/file.ts +45 -368
- package/src/routers/folder.ts +107 -836
- package/src/routers/labChat.ts +29 -605
- package/src/routers/marketing.ts +35 -77
- package/src/routers/message.ts +45 -571
- package/src/routers/newtonChat.ts +17 -278
- package/src/routers/notifications.ts +32 -82
- package/src/routers/section.ts +46 -330
- package/src/routers/user.ts +49 -227
- package/src/routers/worksheet.ts +215 -503
- 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/utils/aiUser.ts +4 -3
- package/src/utils/email.ts +5 -3
- package/src/utils/generateInviteCode.ts +1 -3
- package/src/utils/inference.ts +5 -2
- package/src/utils/logger.ts +3 -1
- 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/{attendance.test.ts → routers/attendance.test.ts} +6 -14
- package/tests/routers/auth.test.ts +171 -0
- package/tests/{class.test.ts → routers/class.test.ts} +131 -85
- package/tests/routers/comment.test.ts +126 -0
- package/tests/routers/conversation.test.ts +145 -0
- package/tests/{event.test.ts → routers/event.test.ts} +93 -32
- 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/{section.test.ts → routers/section.test.ts} +5 -13
- package/tests/server/rateLimit.test.ts +73 -0
- package/tests/setup.ts +18 -92
- package/tests/user.test.ts +9 -31
- 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/vitest.config.ts +6 -3
- package/vitest.unit.config.ts +21 -0
- package/TODO.md +0 -2
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -12110
- package/coverage/coverage-final.json +0 -44
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -221
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/server/index.html +0 -116
- package/coverage/server/src/exportType.ts.html +0 -109
- package/coverage/server/src/index.html +0 -161
- package/coverage/server/src/index.ts.html +0 -1702
- package/coverage/server/src/instrument.ts.html +0 -130
- package/coverage/server/src/lib/config/env.ts.html +0 -448
- package/coverage/server/src/lib/config/index.html +0 -116
- package/coverage/server/src/lib/fileUpload.ts.html +0 -1138
- package/coverage/server/src/lib/googleCloudStorage.ts.html +0 -334
- package/coverage/server/src/lib/index.html +0 -206
- package/coverage/server/src/lib/jsonConversion.ts.html +0 -2323
- package/coverage/server/src/lib/jsonStyles.ts.html +0 -193
- package/coverage/server/src/lib/notificationHandler.ts.html +0 -193
- package/coverage/server/src/lib/pusher.ts.html +0 -121
- package/coverage/server/src/lib/thumbnailGenerator.ts.html +0 -592
- package/coverage/server/src/middleware/auth.ts.html +0 -646
- package/coverage/server/src/middleware/index.html +0 -146
- package/coverage/server/src/middleware/logging.ts.html +0 -244
- package/coverage/server/src/middleware/security.ts.html +0 -271
- package/coverage/server/src/routers/_app.ts.html +0 -232
- package/coverage/server/src/routers/agenda.ts.html +0 -319
- package/coverage/server/src/routers/announcement.ts.html +0 -3481
- package/coverage/server/src/routers/assignment.ts.html +0 -7633
- package/coverage/server/src/routers/attendance.ts.html +0 -1030
- package/coverage/server/src/routers/auth.ts.html +0 -1081
- package/coverage/server/src/routers/class.ts.html +0 -3535
- package/coverage/server/src/routers/comment.ts.html +0 -991
- package/coverage/server/src/routers/conversation.ts.html +0 -982
- package/coverage/server/src/routers/event.ts.html +0 -1609
- package/coverage/server/src/routers/file.ts.html +0 -1144
- package/coverage/server/src/routers/folder.ts.html +0 -2797
- package/coverage/server/src/routers/index.html +0 -386
- package/coverage/server/src/routers/labChat.ts.html +0 -3073
- package/coverage/server/src/routers/marketing.ts.html +0 -340
- package/coverage/server/src/routers/message.ts.html +0 -1912
- package/coverage/server/src/routers/notifications.ts.html +0 -364
- package/coverage/server/src/routers/section.ts.html +0 -1120
- package/coverage/server/src/routers/user.ts.html +0 -862
- package/coverage/server/src/routers/worksheet.ts.html +0 -1729
- package/coverage/server/src/trpc.ts.html +0 -397
- package/coverage/server/src/types/index.html +0 -116
- package/coverage/server/src/types/trpc.ts.html +0 -127
- package/coverage/server/src/utils/aiUser.ts.html +0 -280
- package/coverage/server/src/utils/email.ts.html +0 -121
- package/coverage/server/src/utils/generateInviteCode.ts.html +0 -106
- package/coverage/server/src/utils/index.html +0 -206
- package/coverage/server/src/utils/inference.ts.html +0 -709
- package/coverage/server/src/utils/logger.ts.html +0 -664
- package/coverage/server/src/utils/prismaErrorHandler.ts.html +0 -907
- package/coverage/server/src/utils/prismaWrapper.ts.html +0 -355
- package/coverage/server/vitest.config.ts.html +0 -196
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/src/lib/notificationHandler.ts +0 -36
- package/tests/announcement.test.ts +0 -164
- package/tests/assignment.test.ts +0 -296
- package/tests/auth.test.ts +0 -48
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignment.js","sources":["services/assignment.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Assignment service – create, read, update, delete assignments and submissions.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n findAssignmentById,\n findAssignmentWithDetails,\n findAssignmentForDelete,\n findAssignmentsDueToday,\n findSubmissionByAssignmentAndStudent,\n findSubmissionByIdForClassMember,\n findSubmissionForUpdate,\n findSubmissionForTeacherUpdate,\n findSubmissionsByAssignmentForTeacher,\n createSubmission,\n findSectionsByClassId,\n findAssignmentWithClassForTeacher,\n findAvailableEventsForAssignment,\n attachAssignmentToEvent,\n detachAssignmentFromEvent,\n} from \"../models/assignment.js\";\nimport { createDirectUploadFiles, type UploadedFile } from \"../lib/fileUpload.js\";\nimport { deleteFile } from \"../lib/googleCloudStorage.js\";\nimport { gradeWorksheetPipeline } from \"../pipelines/gradeWorksheet.js\";\nimport { sendToMultiple } from \"./notification.js\";\nimport { logger } from \"../utils/logger.js\";\n\nexport async function assignmentExists(id: string) {\n const assignment = await findAssignmentById(id);\n return !!assignment;\n}\n\nexport async function getDueToday() {\n const assignments = await findAssignmentsDueToday();\n return assignments.map((a) => ({\n ...a,\n dueDate: a.dueDate.toISOString(),\n }));\n}\n\nexport async function getAssignment(id: string, _classId: string) {\n const assignment = await findAssignmentWithDetails(id);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n const sections = await findSectionsByClassId(assignment.classId);\n return { ...assignment, sections };\n}\n\nexport async function getSubmission(assignmentId: string, studentId: string) {\n let submission = await findSubmissionByAssignmentAndStudent(assignmentId, studentId);\n if (!submission) {\n const created = await createSubmission(assignmentId, studentId);\n return {\n ...created,\n late: created.assignment.dueDate < new Date(),\n };\n }\n return {\n ...submission,\n late: submission.assignment.dueDate < new Date(),\n };\n}\n\nexport async function getSubmissionById(submissionId: string, classId: string, userId: string) {\n const submission = await findSubmissionByIdForClassMember(submissionId, classId, userId);\n if (!submission) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Submission not found\",\n });\n }\n return {\n ...submission,\n late: submission.assignment.dueDate < new Date(),\n };\n}\n\nexport async function getSubmissions(assignmentId: string, teacherId: string) {\n const submissions = await findSubmissionsByAssignmentForTeacher(assignmentId, teacherId);\n return submissions.map((s) => ({\n ...s,\n late: s.assignment.dueDate < new Date(),\n }));\n}\n\nexport async function createAssignmentRecord(\n userId: string,\n input: {\n classId: string;\n id?: string;\n title: string;\n instructions: string;\n dueDate: string;\n files?: { name: string; type: string; size: number }[];\n existingFileIds?: string[];\n aiPolicyLevel?: number;\n acceptFiles?: boolean;\n acceptExtendedResponse?: boolean;\n acceptWorksheet?: boolean;\n worksheetIds?: string[];\n gradeWithAI?: boolean;\n studentIds?: string[];\n maxGrade?: number;\n graded?: boolean;\n weight?: number;\n sectionId?: string;\n type?: string;\n markSchemeId?: string;\n gradingBoundaryId?: string;\n inProgress?: boolean;\n }\n) {\n const {\n classId,\n id,\n title,\n instructions,\n dueDate,\n files,\n existingFileIds,\n aiPolicyLevel,\n acceptFiles,\n acceptExtendedResponse,\n acceptWorksheet,\n worksheetIds,\n gradeWithAI,\n studentIds,\n maxGrade,\n graded,\n weight,\n sectionId,\n type,\n markSchemeId,\n gradingBoundaryId,\n inProgress,\n } = input;\n\n const [classData, rubricData] = await Promise.all([\n prisma.class.findUnique({\n where: { id: classId },\n include: { students: { select: { id: true } } },\n }),\n markSchemeId\n ? prisma.markScheme.findUnique({\n where: { id: markSchemeId },\n select: { structured: true },\n })\n : null,\n ]);\n\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found\",\n });\n }\n\n let computedMaxGrade = maxGrade;\n if (markSchemeId && rubricData) {\n const parsedRubric = JSON.parse(rubricData.structured || \"{}\");\n computedMaxGrade = parsedRubric.criteria?.reduce(\n (acc: number, criterion: { levels: { points: number }[] }) => {\n const maxPoints = Math.max(...criterion.levels.map((l) => l.points));\n return acc + maxPoints;\n },\n 0\n );\n }\n\n const submissionData =\n studentIds && studentIds.length > 0\n ? studentIds.map((studentId) => ({ student: { connect: { id: studentId } } }))\n : classData.students.map((student) => ({ student: { connect: { id: student.id } } }));\n\n const assignment = await prisma.$transaction(\n async (tx) => {\n const created = await tx.assignment.create({\n data: {\n ...(id && { id }),\n title,\n instructions,\n dueDate: new Date(dueDate),\n maxGrade: markSchemeId ? computedMaxGrade : maxGrade,\n graded,\n weight,\n type: type as any,\n ...(aiPolicyLevel !== undefined && { aiPolicyLevel }),\n acceptFiles,\n acceptExtendedResponse,\n acceptWorksheet,\n ...(worksheetIds?.length && {\n worksheets: { connect: worksheetIds.map((id) => ({ id })) },\n }),\n gradeWithAI,\n ...(studentIds?.length && {\n assignedTo: { connect: studentIds.map((id) => ({ id })) },\n }),\n order: 0,\n inProgress: inProgress || false,\n class: { connect: { id: classId } },\n ...(sectionId && { section: { connect: { id: sectionId } } }),\n ...(markSchemeId && { markScheme: { connect: { id: markSchemeId } } }),\n ...(gradingBoundaryId && {\n gradingBoundary: { connect: { id: gradingBoundaryId } },\n }),\n submissions: { create: submissionData },\n teacher: { connect: { id: userId } },\n },\n select: {\n id: true,\n title: true,\n instructions: true,\n dueDate: true,\n maxGrade: true,\n graded: true,\n weight: true,\n type: true,\n attachments: { select: { id: true, name: true, type: true } },\n section: { select: { id: true, name: true } },\n teacher: { select: { id: true, username: true } },\n class: { select: { id: true, name: true } },\n },\n });\n\n await tx.assignment.updateMany({\n where: { classId, id: { not: created.id } },\n data: { order: { increment: 1 } },\n });\n await tx.section.updateMany({\n where: { classId },\n data: { order: { increment: 1 } },\n });\n await tx.assignment.update({\n where: { id: created.id },\n data: { order: 1 },\n });\n\n return created;\n },\n { maxWait: 10000, timeout: 20000 }\n );\n\n const fileOperations: Promise<unknown>[] = [];\n if (files?.length) {\n fileOperations.push(\n createDirectUploadFiles(files, userId, undefined, assignment.id).then((uploadedFiles) => {\n if (uploadedFiles.length > 0) {\n return prisma.assignment.update({\n where: { id: assignment.id },\n data: {\n attachments: {\n create: uploadedFiles.map((file) => ({\n name: file.name,\n type: file.type,\n size: file.size,\n path: file.path,\n })),\n },\n },\n });\n }\n })\n );\n }\n if (existingFileIds?.length) {\n fileOperations.push(\n prisma.assignment.update({\n where: { id: assignment.id },\n data: {\n attachments: {\n connect: existingFileIds.map((fileId) => ({ id: fileId })),\n },\n },\n })\n );\n }\n await Promise.all(fileOperations);\n\n sendToMultiple({\n receiverIds: classData.students.map((s) => s.id),\n title: `🔔 New assignment for ${classData.name}`,\n content: `The assignment \"${title}\" has been created in ${classData.name}.\\nDue date: ${new Date(dueDate).toLocaleDateString()}.\\n[Link to assignment](/class/${classId}/assignments/${assignment.id})`,\n }).catch((error) => {\n logger.error(\"Failed to send assignment notifications:\", error);\n });\n\n return assignment;\n}\n\nexport async function updateAssignmentRecord(\n userId: string,\n input: {\n id: string;\n classId: string;\n title?: string;\n instructions?: string;\n dueDate?: string;\n files?: { name: string; type: string; size: number }[];\n existingFileIds?: string[];\n removedAttachments?: string[];\n worksheetIds?: string[];\n aiPolicyLevel?: number;\n maxGrade?: number;\n graded?: boolean;\n weight?: number;\n sectionId?: string | null;\n type?: string;\n inProgress?: boolean;\n acceptFiles?: boolean;\n acceptExtendedResponse?: boolean;\n acceptWorksheet?: boolean;\n gradeWithAI?: boolean;\n studentIds?: string[];\n }\n) {\n const [assignment, classData] = await Promise.all([\n prisma.assignment.findFirst({\n where: { id: input.id, teacherId: userId },\n include: {\n attachments: {\n select: {\n id: true,\n name: true,\n type: true,\n path: true,\n size: true,\n uploadStatus: true,\n thumbnail: { select: { path: true } },\n },\n },\n class: { select: { id: true, name: true } },\n markScheme: true,\n },\n }),\n prisma.class.findFirst({\n where: { assignments: { some: { id: input.id } } },\n include: { students: { select: { id: true } } },\n }),\n ]);\n\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n const submissionData =\n input.studentIds?.length\n ? input.studentIds.map((studentId) => ({ student: { connect: { id: studentId } } }))\n : classData?.students.map((student) => ({\n student: { connect: { id: student.id } },\n }));\n\n if (input.removedAttachments?.length) {\n const filesToDelete = assignment.attachments.filter((f) =>\n input.removedAttachments!.includes(f.id)\n );\n await Promise.all(\n filesToDelete.map(async (file) => {\n if (file.uploadStatus === \"COMPLETED\") {\n await deleteFile(file.path).catch(() => {});\n if (file.thumbnail?.path) {\n await deleteFile(file.thumbnail.path).catch(() => {});\n }\n }\n })\n );\n }\n\n let uploadedFiles: UploadedFile[] = [];\n if (input.files?.length) {\n uploadedFiles = await createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n input.id\n );\n }\n\n const updateData: Record<string, unknown> = {\n ...(input.title && { title: input.title }),\n ...(input.instructions && { instructions: input.instructions }),\n ...(input.dueDate && { dueDate: new Date(input.dueDate) }),\n ...(input.maxGrade && { maxGrade: input.maxGrade }),\n ...(input.graded !== undefined && { graded: input.graded }),\n ...(input.weight && { weight: input.weight }),\n ...(input.type && { type: input.type }),\n ...(input.inProgress !== undefined && { inProgress: input.inProgress }),\n ...(input.acceptFiles !== undefined && { acceptFiles: input.acceptFiles }),\n ...(input.acceptExtendedResponse !== undefined && {\n acceptExtendedResponse: input.acceptExtendedResponse,\n }),\n ...(input.acceptWorksheet !== undefined && {\n acceptWorksheet: input.acceptWorksheet,\n }),\n ...(input.gradeWithAI !== undefined && { gradeWithAI: input.gradeWithAI }),\n ...(input.studentIds && {\n assignedTo: {\n set: [],\n connect: input.studentIds.map((id) => ({ id })),\n },\n }),\n ...(submissionData && {\n submissions: {\n deleteMany: {},\n create: submissionData,\n },\n }),\n ...(input.aiPolicyLevel !== undefined && { aiPolicyLevel: input.aiPolicyLevel }),\n ...(input.sectionId !== undefined && {\n section: input.sectionId\n ? { connect: { id: input.sectionId } }\n : { disconnect: true },\n }),\n ...(input.worksheetIds && {\n worksheets: {\n set: [],\n connect: input.worksheetIds.map((id) => ({ id })),\n },\n }),\n ...((uploadedFiles.length > 0 ||\n input.existingFileIds?.length ||\n input.removedAttachments?.length) && {\n attachments: {\n ...(uploadedFiles.length > 0 && {\n create: uploadedFiles.map((file) => ({\n name: file.name,\n type: file.type,\n size: file.size,\n path: file.path,\n ...(file.thumbnailId && {\n thumbnail: { connect: { id: file.thumbnailId } },\n }),\n })),\n }),\n ...(input.existingFileIds?.length && {\n connect: input.existingFileIds.map((fileId) => ({ id: fileId })),\n }),\n ...(input.removedAttachments?.length && {\n deleteMany: { id: { in: input.removedAttachments } },\n }),\n },\n }),\n };\n\n const updatedAssignment = await prisma.$transaction(\n async (tx) => {\n return tx.assignment.update({\n where: { id: input.id },\n data: updateData as any,\n select: {\n id: true,\n title: true,\n instructions: true,\n dueDate: true,\n maxGrade: true,\n graded: true,\n weight: true,\n type: true,\n createdAt: true,\n markSchemeId: true,\n submissions: {\n select: {\n student: { select: { id: true, username: true } },\n },\n },\n attachments: {\n select: {\n id: true,\n name: true,\n type: true,\n thumbnail: true,\n size: true,\n path: true,\n uploadedAt: true,\n thumbnailId: true,\n },\n },\n section: true,\n teacher: true,\n class: true,\n },\n });\n },\n { maxWait: 5000, timeout: 10000 }\n );\n\n if (updatedAssignment.markSchemeId) {\n prisma.markScheme\n .findUnique({\n where: { id: updatedAssignment.markSchemeId },\n select: { structured: true },\n })\n .then((rubric) => {\n if (rubric) {\n const parsed = JSON.parse(rubric.structured || \"{}\");\n const computedMaxGrade =\n parsed.criteria?.reduce(\n (acc: number, c: { levels: { points: number }[] }) =>\n acc + Math.max(...c.levels.map((l) => l.points)),\n 0\n ) || 0;\n return prisma.assignment.update({\n where: { id: input.id },\n data: { maxGrade: computedMaxGrade },\n });\n }\n })\n .catch((error) => logger.error(\"Failed to update max grade from rubric:\", error));\n }\n\n return updatedAssignment;\n}\n\nexport async function deleteAssignmentRecord(userId: string, id: string, _classId: string) {\n const assignment = await findAssignmentForDelete(id, userId);\n\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n const filesToDelete = [\n ...assignment.attachments,\n ...assignment.submissions.flatMap((s) => [\n ...s.attachments,\n ...s.annotations,\n ]),\n ];\n\n await Promise.all(\n filesToDelete.map(async (file) => {\n try {\n if (file.uploadStatus === \"COMPLETED\") {\n await deleteFile(file.path);\n if (file.thumbnail) {\n await deleteFile(file.thumbnail.path);\n }\n }\n } catch {\n // ignore\n }\n })\n );\n\n await prisma.assignment.delete({\n where: { id },\n });\n\n return { id };\n}\n\nexport async function updateSubmissionRecord(\n userId: string,\n input: {\n submissionId: string;\n assignmentId: string;\n classId: string;\n submit?: boolean;\n newAttachments?: { name: string; type: string; size: number }[];\n extendedResponse?: string;\n existingFileIds?: string[];\n removedAttachments?: string[];\n }\n) {\n const { submissionId, submit, newAttachments, extendedResponse, existingFileIds, removedAttachments } =\n input;\n\n const submission = await findSubmissionForUpdate(submissionId, userId);\n\n if (!submission) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Submission not found\",\n });\n }\n\n if (submit !== undefined) {\n if (\n submission.assignment.acceptWorksheet &&\n submission.assignment.gradeWithAI\n ) {\n const worksheetResponses = await prisma.studentWorksheetResponse.findMany({\n where: { submissionId: submission.id },\n });\n for (const wr of worksheetResponses) {\n gradeWorksheetPipeline(wr.id);\n }\n }\n return prisma.submission.update({\n where: { id: submission.id },\n data: {\n submitted: !submission.submitted,\n submittedAt: new Date(),\n },\n include: {\n attachments: true,\n student: { select: { id: true, username: true } },\n assignment: {\n include: {\n class: true,\n markScheme: { select: { id: true, structured: true } },\n gradingBoundary: { select: { id: true, structured: true } },\n },\n },\n },\n });\n }\n\n let uploadedFiles: UploadedFile[] = [];\n if (newAttachments?.length) {\n uploadedFiles = await createDirectUploadFiles(\n newAttachments,\n userId,\n undefined,\n undefined,\n submission.id\n );\n }\n\n if (uploadedFiles.length > 0) {\n await prisma.submission.update({\n where: { id: submission.id },\n data: {\n attachments: {\n create: uploadedFiles.map((file) => ({\n name: file.name,\n type: file.type,\n size: file.size,\n path: file.path,\n ...(file.thumbnailId && {\n thumbnail: { connect: { id: file.thumbnailId } },\n }),\n })),\n },\n },\n });\n }\n\n if (existingFileIds?.length) {\n await prisma.submission.update({\n where: { id: submission.id },\n data: {\n attachments: {\n connect: existingFileIds.map((fileId) => ({ id: fileId })),\n },\n },\n });\n }\n\n if (removedAttachments?.length) {\n const filesToDelete = submission.attachments.filter((f) =>\n removedAttachments.includes(f.id)\n );\n await Promise.all(\n filesToDelete.map(async (file) => {\n try {\n if (file.uploadStatus === \"COMPLETED\") {\n await deleteFile(file.path);\n if (file.thumbnail?.path) {\n await deleteFile(file.thumbnail.path);\n }\n }\n } catch {\n // ignore\n }\n })\n );\n }\n\n return prisma.submission.update({\n where: { id: submission.id },\n data: {\n ...(removedAttachments?.length && {\n attachments: {\n deleteMany: { id: { in: removedAttachments } },\n },\n }),\n ...(extendedResponse !== undefined && { extendedResponse }),\n },\n include: {\n attachments: { include: { thumbnail: true } },\n student: { select: { id: true, username: true } },\n assignment: {\n include: {\n class: true,\n markScheme: { select: { id: true, structured: true } },\n gradingBoundary: { select: { id: true, structured: true } },\n },\n },\n },\n });\n}\n\nconst submissionTeacherInclude = {\n attachments: { include: { thumbnail: true } },\n annotations: { include: { thumbnail: true } },\n student: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n },\n },\n assignment: {\n include: {\n class: true,\n markScheme: { select: { id: true, structured: true } },\n gradingBoundary: { select: { id: true, structured: true } },\n },\n },\n};\n\nexport async function updateSubmissionAsTeacherRecord(\n teacherId: string,\n input: {\n submissionId: string;\n assignmentId: string;\n classId: string;\n return?: boolean;\n gradeReceived?: number | null;\n newAttachments?: { name: string; type: string; size: number }[];\n existingFileIds?: string[];\n removedAttachments?: string[];\n rubricGrades?: { criteriaId: string; selectedLevelId: string; points: number; comments: string }[];\n feedback?: string;\n }\n) {\n const {\n submissionId,\n return: returnSubmission,\n gradeReceived,\n existingFileIds,\n removedAttachments,\n rubricGrades,\n feedback,\n } = input;\n\n const submission = await findSubmissionForTeacherUpdate(submissionId, teacherId);\n\n if (!submission) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Submission not found\",\n });\n }\n\n if (input.newAttachments?.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Direct file upload is deprecated. Use getAnnotationUploadUrls endpoint instead.\",\n });\n }\n\n if (returnSubmission !== undefined) {\n return prisma.submission.update({\n where: { id: submissionId },\n data: { returned: !submission.returned },\n include: submissionTeacherInclude,\n });\n }\n\n if (existingFileIds?.length) {\n await prisma.submission.update({\n where: { id: submission.id },\n data: {\n annotations: {\n connect: existingFileIds.map((fileId) => ({ id: fileId })),\n },\n },\n });\n }\n\n if (removedAttachments?.length) {\n const filesToDelete = submission.annotations.filter((f) =>\n removedAttachments.includes(f.id)\n );\n await Promise.all(\n filesToDelete.map(async (file) => {\n try {\n if (file.uploadStatus === \"COMPLETED\") {\n await deleteFile(file.path);\n if (file.thumbnail?.path) {\n await deleteFile(file.thumbnail.path);\n }\n }\n } catch {\n // ignore\n }\n })\n );\n }\n\n return prisma.submission.update({\n where: { id: submissionId },\n data: {\n ...(gradeReceived !== undefined && { gradeReceived }),\n ...(rubricGrades && { rubricState: JSON.stringify(rubricGrades) }),\n ...(feedback && { teacherComments: feedback }),\n ...(removedAttachments?.length && {\n annotations: {\n deleteMany: { id: { in: removedAttachments } },\n },\n }),\n },\n include: submissionTeacherInclude,\n });\n}\n\nexport async function attachAssignmentToEventRecord(\n teacherId: string,\n assignmentId: string,\n eventId: string\n) {\n const assignment = await findAssignmentWithClassForTeacher(assignmentId, teacherId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found or you are not authorized\",\n });\n }\n\n const event = await prisma.event.findFirst({\n where: {\n id: eventId,\n classId: assignment.classId,\n },\n });\n\n if (!event) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Event not found or does not belong to the same class\",\n });\n }\n\n const updated = await attachAssignmentToEvent(assignmentId, eventId);\n return { assignment: updated };\n}\n\nexport async function detachAssignmentFromEventRecord(teacherId: string, assignmentId: string) {\n const assignment = await findAssignmentWithClassForTeacher(assignmentId, teacherId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found or you are not authorized\",\n });\n }\n\n const updated = await detachAssignmentFromEvent(assignmentId);\n return { assignment: updated };\n}\n\nexport async function getAvailableEventsForAssignment(teacherId: string, assignmentId: string) {\n const assignment = await findAssignmentWithClassForTeacher(assignmentId, teacherId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found or you are not authorized\",\n });\n }\n\n const events = await findAvailableEventsForAssignment(assignment.classId, assignmentId);\n return { events };\n}\n\nexport async function attachMarkSchemeRecord(\n teacherId: string,\n assignmentId: string,\n markSchemeId: string | null\n) {\n const assignment = await findAssignmentById(assignmentId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n if (markSchemeId) {\n const markScheme = await prisma.markScheme.findFirst({\n where: { id: markSchemeId },\n });\n if (!markScheme) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Mark scheme not found\",\n });\n }\n }\n\n return prisma.assignment.update({\n where: { id: assignmentId },\n data: {\n markScheme: markSchemeId\n ? { connect: { id: markSchemeId } }\n : { disconnect: true },\n },\n include: {\n attachments: true,\n section: true,\n teacher: true,\n eventAttached: true,\n markScheme: true,\n },\n });\n}\n\nexport async function detachMarkSchemeRecord(teacherId: string, assignmentId: string) {\n const assignment = await findAssignmentById(assignmentId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n return prisma.assignment.update({\n where: { id: assignmentId },\n data: { markScheme: { disconnect: true } },\n include: {\n attachments: true,\n section: true,\n teacher: true,\n eventAttached: true,\n markScheme: true,\n },\n });\n}\n\nexport async function attachGradingBoundaryRecord(\n teacherId: string,\n assignmentId: string,\n gradingBoundaryId: string | null\n) {\n const assignment = await findAssignmentById(assignmentId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n if (gradingBoundaryId) {\n const gradingBoundary = await prisma.gradingBoundary.findFirst({\n where: { id: gradingBoundaryId },\n });\n if (!gradingBoundary) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Grading boundary not found\",\n });\n }\n }\n\n return prisma.assignment.update({\n where: { id: assignmentId },\n data: {\n gradingBoundary: gradingBoundaryId\n ? { connect: { id: gradingBoundaryId } }\n : { disconnect: true },\n },\n include: {\n attachments: true,\n section: true,\n teacher: true,\n eventAttached: true,\n gradingBoundary: true,\n },\n });\n}\n\nexport async function detachGradingBoundaryRecord(teacherId: string, assignmentId: string) {\n const assignment = await findAssignmentById(assignmentId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n return prisma.assignment.update({\n where: { id: assignmentId },\n data: { gradingBoundary: { disconnect: true } },\n include: {\n attachments: true,\n section: true,\n teacher: true,\n eventAttached: true,\n gradingBoundary: true,\n },\n });\n}\n\n// --- Reorder & Move (unified list of sections + assignments) ---\n\nasync function getUnifiedList(\n tx: { section: { findMany: (args: any) => Promise<any[]> }; assignment: { findMany: (args: any) => Promise<any[]> } },\n classId: string\n) {\n const [sections, assignments] = await Promise.all([\n tx.section.findMany({\n where: { classId },\n select: { id: true, order: true },\n }),\n tx.assignment.findMany({\n where: { classId },\n select: { id: true, order: true },\n }),\n ]);\n\n const unified = [\n ...sections.map((s: { id: string; order: number | null }) => ({ id: s.id, order: s.order, type: \"section\" as const })),\n ...assignments.map((a: { id: string; order: number | null }) => ({ id: a.id, order: a.order, type: \"assignment\" as const })),\n ].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));\n\n return unified;\n}\n\nasync function normalizeUnifiedList(\n tx: { section: { update: (args: any) => Promise<any> }; assignment: { update: (args: any) => Promise<any> } },\n classId: string,\n orderedItems: Array<{ id: string; type: \"section\" | \"assignment\" }>\n) {\n const BATCH_SIZE = 10;\n const sections: Array<{ id: string; order: number }> = [];\n const assignments: Array<{ id: string; order: number }> = [];\n\n orderedItems.forEach((item, index) => {\n const orderData = { id: item.id, order: index + 1 };\n if (item.type === \"section\") {\n sections.push(orderData);\n } else {\n assignments.push(orderData);\n }\n });\n\n const processBatch = async (\n items: Array<{ id: string; order: number }>,\n type: \"section\" | \"assignment\"\n ) => {\n for (let i = 0; i < items.length; i += BATCH_SIZE) {\n const batch = items.slice(i, i + BATCH_SIZE);\n await Promise.all(\n batch.map((item) =>\n type === \"section\"\n ? tx.section.update({ where: { id: item.id }, data: { order: item.order } })\n : tx.assignment.update({ where: { id: item.id }, data: { order: item.order } })\n )\n );\n }\n };\n\n await processBatch(sections, \"section\");\n await processBatch(assignments, \"assignment\");\n}\n\nexport async function reorderAssignmentRecord(\n teacherId: string,\n input: {\n classId: string;\n movedId: string;\n position: \"start\" | \"end\" | \"before\" | \"after\";\n targetId?: string;\n }\n) {\n const assignment = await findAssignmentWithClassForTeacher(input.movedId, teacherId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n if ((input.position === \"before\" || input.position === \"after\") && !input.targetId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"targetId required for before/after\",\n });\n }\n\n return prisma.$transaction(\n async (tx) => {\n const unified = await getUnifiedList(tx, input.classId);\n\n const movedIdx = unified.findIndex(\n (item) => item.id === input.movedId && item.type === \"assignment\"\n );\n if (movedIdx === -1) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found in unified list\",\n });\n }\n\n const withoutMoved = unified.filter(\n (item) => !(item.id === input.movedId && item.type === \"assignment\")\n );\n\n let next: Array<{ id: string; type: \"section\" | \"assignment\" }> = [];\n\n if (input.position === \"start\") {\n next = [\n { id: input.movedId, type: \"assignment\" },\n ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),\n ];\n } else if (input.position === \"end\") {\n next = [\n ...withoutMoved.map((item) => ({ id: item.id, type: item.type })),\n { id: input.movedId, type: \"assignment\" },\n ];\n } else {\n const targetIdx = withoutMoved.findIndex((item) => item.id === input.targetId);\n if (targetIdx === -1) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"targetId not found in unified list\",\n });\n }\n if (input.position === \"before\") {\n next = [\n ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id, type: item.type })),\n { id: input.movedId, type: \"assignment\" },\n ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id, type: item.type })),\n ];\n } else {\n next = [\n ...withoutMoved.slice(0, targetIdx + 1).map((item) => ({ id: item.id, type: item.type })),\n { id: input.movedId, type: \"assignment\" },\n ...withoutMoved.slice(targetIdx + 1).map((item) => ({ id: item.id, type: item.type })),\n ];\n }\n }\n\n await normalizeUnifiedList(tx, input.classId, next);\n\n return tx.assignment.findUnique({ where: { id: input.movedId } });\n },\n { maxWait: 10000, timeout: 30000 }\n );\n}\n\nexport async function moveAssignmentRecord(\n teacherId: string,\n input: {\n id: string;\n classId: string;\n targetSectionId: string | null;\n }\n) {\n const assignment = await findAssignmentWithClassForTeacher(input.id, teacherId);\n if (!assignment) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Assignment not found\",\n });\n }\n\n if (input.targetSectionId) {\n const section = await prisma.section.findFirst({\n where: {\n id: input.targetSectionId,\n classId: input.classId,\n },\n });\n if (!section) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Section not found\",\n });\n }\n }\n\n return prisma.$transaction(\n async (tx) => {\n await tx.assignment.update({\n where: { id: input.id },\n data: { sectionId: input.targetSectionId },\n });\n return tx.assignment.findUnique({ where: { id: input.id } });\n },\n { maxWait: 5000, timeout: 10000 }\n );\n}\n"],"names":[],"mappings":"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","debug_id":"9f048e12-9c8f-592d-82cb-74a5ccf39c12"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Get attendance for a class (optionally filtered by event). Creates missing records. */
|
|
2
|
+
export declare function getAttendance(classId: string, eventId?: string): Promise<({
|
|
3
|
+
event: {
|
|
4
|
+
id: string;
|
|
5
|
+
location: string | null;
|
|
6
|
+
name: string | null;
|
|
7
|
+
color: string | null;
|
|
8
|
+
startTime: Date;
|
|
9
|
+
endTime: Date;
|
|
10
|
+
} | null;
|
|
11
|
+
present: {
|
|
12
|
+
id: string;
|
|
13
|
+
username: string;
|
|
14
|
+
profile: {
|
|
15
|
+
displayName: string | null;
|
|
16
|
+
bio: string | null;
|
|
17
|
+
location: string | null;
|
|
18
|
+
website: string | null;
|
|
19
|
+
profilePicture: string | null;
|
|
20
|
+
profilePictureThumbnail: string | null;
|
|
21
|
+
} | null;
|
|
22
|
+
}[];
|
|
23
|
+
late: {
|
|
24
|
+
id: string;
|
|
25
|
+
username: string;
|
|
26
|
+
profile: {
|
|
27
|
+
displayName: string | null;
|
|
28
|
+
bio: string | null;
|
|
29
|
+
location: string | null;
|
|
30
|
+
website: string | null;
|
|
31
|
+
profilePicture: string | null;
|
|
32
|
+
profilePictureThumbnail: string | null;
|
|
33
|
+
} | null;
|
|
34
|
+
}[];
|
|
35
|
+
absent: {
|
|
36
|
+
id: string;
|
|
37
|
+
username: string;
|
|
38
|
+
profile: {
|
|
39
|
+
displayName: string | null;
|
|
40
|
+
bio: string | null;
|
|
41
|
+
location: string | null;
|
|
42
|
+
website: string | null;
|
|
43
|
+
profilePicture: string | null;
|
|
44
|
+
profilePictureThumbnail: string | null;
|
|
45
|
+
} | null;
|
|
46
|
+
}[];
|
|
47
|
+
} & {
|
|
48
|
+
id: string;
|
|
49
|
+
classId: string;
|
|
50
|
+
eventId: string | null;
|
|
51
|
+
date: Date;
|
|
52
|
+
})[]>;
|
|
53
|
+
/** Update attendance record (present/late/absent). Teacher-only. */
|
|
54
|
+
export declare function updateAttendanceRecord(userId: string, classId: string, eventId: string | null | undefined, attendance: {
|
|
55
|
+
present: {
|
|
56
|
+
id: string;
|
|
57
|
+
username: string;
|
|
58
|
+
}[];
|
|
59
|
+
late: {
|
|
60
|
+
id: string;
|
|
61
|
+
username: string;
|
|
62
|
+
}[];
|
|
63
|
+
absent: {
|
|
64
|
+
id: string;
|
|
65
|
+
username: string;
|
|
66
|
+
}[];
|
|
67
|
+
}): Promise<{
|
|
68
|
+
event: {
|
|
69
|
+
id: string;
|
|
70
|
+
location: string | null;
|
|
71
|
+
name: string | null;
|
|
72
|
+
startTime: Date;
|
|
73
|
+
endTime: Date;
|
|
74
|
+
} | null;
|
|
75
|
+
present: {
|
|
76
|
+
id: string;
|
|
77
|
+
username: string;
|
|
78
|
+
}[];
|
|
79
|
+
late: {
|
|
80
|
+
id: string;
|
|
81
|
+
username: string;
|
|
82
|
+
}[];
|
|
83
|
+
absent: {
|
|
84
|
+
id: string;
|
|
85
|
+
username: string;
|
|
86
|
+
}[];
|
|
87
|
+
} & {
|
|
88
|
+
id: string;
|
|
89
|
+
classId: string;
|
|
90
|
+
eventId: string | null;
|
|
91
|
+
date: Date;
|
|
92
|
+
}>;
|
|
93
|
+
//# sourceMappingURL=attendance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attendance.d.ts","sourceRoot":"/","sources":["services/attendance.ts"],"names":[],"mappings":"AAiBA,0FAA0F;AAC1F,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAwBpE;AAED,oEAAoE;AACpE,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,UAAU,EAAE;IACV,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C;;;;;;;;;;;;;;;;;;;;;;;;;GA+BF"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attendance service – get and update class attendance records.
|
|
3
|
+
* Auto-creates attendance records for events that don't have them yet.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
!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]="55bfbcca-f33b-598a-a729-a6202fde1f7e")}catch(e){}}();
|
|
7
|
+
import { TRPCError } from "@trpc/server";
|
|
8
|
+
import { findClassWithStudents, findClassWithTeacher, findEventsByClassId, findAttendanceByEventId, findAttendanceByClassAndEvent, findManyAttendance, createAttendance, createAttendanceForEvent, updateAttendance, } from "../models/attendance.js";
|
|
9
|
+
/** Get attendance for a class (optionally filtered by event). Creates missing records. */
|
|
10
|
+
export async function getAttendance(classId, eventId) {
|
|
11
|
+
const classData = await findClassWithStudents(classId);
|
|
12
|
+
if (!classData) {
|
|
13
|
+
throw new TRPCError({
|
|
14
|
+
code: "UNAUTHORIZED",
|
|
15
|
+
message: "You are not authorized to view this class's attendance",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const events = await findEventsByClassId(classId);
|
|
19
|
+
const studentIds = classData.students.map((s) => s.id);
|
|
20
|
+
for (const event of events) {
|
|
21
|
+
const existing = await findAttendanceByEventId(event.id);
|
|
22
|
+
if (!existing) {
|
|
23
|
+
await createAttendanceForEvent({
|
|
24
|
+
classId,
|
|
25
|
+
eventId: event.id,
|
|
26
|
+
presentIds: studentIds,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return findManyAttendance(classId, eventId);
|
|
31
|
+
}
|
|
32
|
+
/** Update attendance record (present/late/absent). Teacher-only. */
|
|
33
|
+
export async function updateAttendanceRecord(userId, classId, eventId, attendance) {
|
|
34
|
+
const classData = await findClassWithTeacher(classId, userId);
|
|
35
|
+
if (!classData) {
|
|
36
|
+
throw new TRPCError({
|
|
37
|
+
code: "UNAUTHORIZED",
|
|
38
|
+
message: "You are not authorized to update this class's attendance",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const existing = await findAttendanceByClassAndEvent(classId, eventId ?? null);
|
|
42
|
+
const presentIds = attendance.present.map((s) => s.id);
|
|
43
|
+
const lateIds = attendance.late.map((s) => s.id);
|
|
44
|
+
const absentIds = attendance.absent.map((s) => s.id);
|
|
45
|
+
if (!existing) {
|
|
46
|
+
return createAttendance({
|
|
47
|
+
classId,
|
|
48
|
+
eventId: eventId ?? null,
|
|
49
|
+
presentIds,
|
|
50
|
+
lateIds,
|
|
51
|
+
absentIds,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return updateAttendance(existing.id, {
|
|
55
|
+
presentIds,
|
|
56
|
+
lateIds,
|
|
57
|
+
absentIds,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=attendance.js.map
|
|
61
|
+
//# debugId=55bfbcca-f33b-598a-a729-a6202fde1f7e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attendance.js","sources":["services/attendance.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Attendance service – get and update class attendance records.\n * Auto-creates attendance records for events that don't have them yet.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findClassWithStudents,\n findClassWithTeacher,\n findEventsByClassId,\n findAttendanceByEventId,\n findAttendanceByClassAndEvent,\n findManyAttendance,\n createAttendance,\n createAttendanceForEvent,\n updateAttendance,\n} from \"../models/attendance.js\";\n\n/** Get attendance for a class (optionally filtered by event). Creates missing records. */\nexport async function getAttendance(classId: string, eventId?: string) {\n const classData = await findClassWithStudents(classId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this class's attendance\",\n });\n }\n\n const events = await findEventsByClassId(classId);\n const studentIds = classData.students.map((s) => s.id);\n\n for (const event of events) {\n const existing = await findAttendanceByEventId(event.id);\n if (!existing) {\n await createAttendanceForEvent({\n classId,\n eventId: event.id,\n presentIds: studentIds,\n });\n }\n }\n\n return findManyAttendance(classId, eventId);\n}\n\n/** Update attendance record (present/late/absent). Teacher-only. */\nexport async function updateAttendanceRecord(\n userId: string,\n classId: string,\n eventId: string | null | undefined,\n attendance: {\n present: { id: string; username: string }[];\n late: { id: string; username: string }[];\n absent: { id: string; username: string }[];\n }\n) {\n const classData = await findClassWithTeacher(classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this class's attendance\",\n });\n }\n\n const existing = await findAttendanceByClassAndEvent(classId, eventId ?? null);\n\n const presentIds = attendance.present.map((s) => s.id);\n const lateIds = attendance.late.map((s) => s.id);\n const absentIds = attendance.absent.map((s) => s.id);\n\n if (!existing) {\n return createAttendance({\n classId,\n eventId: eventId ?? null,\n presentIds,\n lateIds,\n absentIds,\n });\n }\n\n return updateAttendance(existing.id, {\n presentIds,\n lateIds,\n absentIds,\n });\n}\n"],"names":[],"mappings":"AAAA;;;GAGG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,6BAA6B,EAC7B,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,OAAgB;IACnE,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,wDAAwD;SAClE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEvD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,wBAAwB,CAAC;gBAC7B,OAAO;gBACP,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,UAAU,EAAE,UAAU;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,OAAe,EACf,OAAkC,EAClC,UAIC;IAED,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,0DAA0D;SACpE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC;IAE/E,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAErD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,gBAAgB,CAAC;YACtB,OAAO;YACP,OAAO,EAAE,OAAO,IAAI,IAAI;YACxB,UAAU;YACV,OAAO;YACP,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,OAAO,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EAAE;QACnC,UAAU;QACV,OAAO;QACP,SAAS;KACV,CAAC,CAAC;AACL,CAAC","debug_id":"55bfbcca-f33b-598a-a729-a6202fde1f7e"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Register a new user, create verification session, and send verification email. */
|
|
2
|
+
export declare function register(input: {
|
|
3
|
+
username: string;
|
|
4
|
+
email: string;
|
|
5
|
+
password: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
user: {
|
|
8
|
+
id: string;
|
|
9
|
+
username: string;
|
|
10
|
+
};
|
|
11
|
+
}>;
|
|
12
|
+
/** Authenticate user by username/password, create session, and cache in Redis. */
|
|
13
|
+
export declare function login(input: {
|
|
14
|
+
username: string;
|
|
15
|
+
password: string;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
verified: boolean;
|
|
18
|
+
user: {
|
|
19
|
+
email: string;
|
|
20
|
+
id?: undefined;
|
|
21
|
+
username?: undefined;
|
|
22
|
+
};
|
|
23
|
+
token?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
token: string;
|
|
26
|
+
user: {
|
|
27
|
+
id: string;
|
|
28
|
+
username: string;
|
|
29
|
+
email?: undefined;
|
|
30
|
+
};
|
|
31
|
+
verified?: undefined;
|
|
32
|
+
}>;
|
|
33
|
+
/** Delete all sessions for a user (logs them out everywhere). */
|
|
34
|
+
export declare function logout(userId: string): Promise<{
|
|
35
|
+
success: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
/** Verify user exists and return profile. Used for session validation. */
|
|
38
|
+
export declare function check(userId: string): Promise<{
|
|
39
|
+
user: {
|
|
40
|
+
id: string;
|
|
41
|
+
username: string;
|
|
42
|
+
profile: {
|
|
43
|
+
displayName: string | null;
|
|
44
|
+
bio: string | null;
|
|
45
|
+
location: string | null;
|
|
46
|
+
website: string | null;
|
|
47
|
+
profilePicture: string | null;
|
|
48
|
+
profilePictureThumbnail: string | null;
|
|
49
|
+
} | null;
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
/** Resend verification email. Replaces existing sessions and sends new link. */
|
|
53
|
+
export declare function resendVerificationEmail(email: string): Promise<{
|
|
54
|
+
success: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
/** Verify email via token. Marks user as verified and deletes the session. */
|
|
57
|
+
export declare function verify(token: string): Promise<{
|
|
58
|
+
success: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
/** Send password reset email. Silently succeeds if email not found (security). */
|
|
61
|
+
export declare function requestPasswordReset(email: string): Promise<{
|
|
62
|
+
success: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
/** Reset password using token from email. Validates token and updates password. */
|
|
65
|
+
export declare function resetPassword(token: string, password: string): Promise<{
|
|
66
|
+
success: boolean;
|
|
67
|
+
}>;
|
|
68
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"/","sources":["services/auth.ts"],"names":[],"mappings":"AA6BA,qFAAqF;AACrF,wBAAsB,QAAQ,CAAC,KAAK,EAAE;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;;;;;GAoEA;AAED,kFAAkF;AAClF,wBAAsB,KAAK,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;GAwCxE;AAED,iEAAiE;AACjE,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM;;GAG1C;AAED,0EAA0E;AAC1E,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;GASzC;AAED,gFAAgF;AAChF,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,MAAM;;GAgC1D;AAED,8EAA8E;AAC9E,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM;;GAmBzC;AAED,kFAAkF;AAClF,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM;;GA8BvD;AAED,mFAAmF;AACnF,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;GAoBlE"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth service – user registration, login, logout, verification, and password reset.
|
|
3
|
+
* Handles session creation, Redis caching, and email notifications.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
!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]="18f3fceb-a95b-5d3c-b80c-c8f5a01da04e")}catch(e){}}();
|
|
7
|
+
import { TRPCError } from "@trpc/server";
|
|
8
|
+
import { v4 as uuidv4 } from "uuid";
|
|
9
|
+
import { compare, hash } from "bcryptjs";
|
|
10
|
+
import { sendMail } from "../utils/email.js";
|
|
11
|
+
import { prismaWrapper } from "../utils/prismaWrapper.js";
|
|
12
|
+
import { env } from "../lib/config/env.js";
|
|
13
|
+
import { logger } from "../utils/logger.js";
|
|
14
|
+
import { findUserByUsername, findUserByUsernameOrEmail, findUserByEmail, findUserById, createUser, createSession, deleteSessionsByUserId, deleteShortLivedSessionsByUserId, deleteUserById, findSessionById, findSessionByIdWithUser, updateUserVerified, updateUserPassword, deleteSessionById, } from "../models/auth.js";
|
|
15
|
+
import { getRedis } from "../lib/redis.js";
|
|
16
|
+
/** Register a new user, create verification session, and send verification email. */
|
|
17
|
+
export async function register(input) {
|
|
18
|
+
const { username, email, password } = input;
|
|
19
|
+
const existingUser = await prismaWrapper.findFirst(() => findUserByUsernameOrEmail(username, email), "checking for existing user during registration");
|
|
20
|
+
if (existingUser && existingUser.verified) {
|
|
21
|
+
if (existingUser.username === username) {
|
|
22
|
+
throw new TRPCError({
|
|
23
|
+
code: "CONFLICT",
|
|
24
|
+
message: "Username already exists",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (existingUser.email === email) {
|
|
28
|
+
throw new TRPCError({
|
|
29
|
+
code: "CONFLICT",
|
|
30
|
+
message: "Email already exists",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (existingUser && !existingUser.verified) {
|
|
35
|
+
await prismaWrapper.deleteMany(() => deleteSessionsByUserId(existingUser.id), "deleting existing sessions for unverified user");
|
|
36
|
+
await prismaWrapper.delete(() => deleteUserById(existingUser.id), "deleting unverified user");
|
|
37
|
+
}
|
|
38
|
+
const user = await prismaWrapper.create(async () => createUser({
|
|
39
|
+
username,
|
|
40
|
+
email,
|
|
41
|
+
password: await hash(password, 10),
|
|
42
|
+
verified: true,
|
|
43
|
+
}), "creating new user during registration");
|
|
44
|
+
const verificationToken = await prismaWrapper.create(() => createSession({
|
|
45
|
+
id: uuidv4(),
|
|
46
|
+
userId: user.id,
|
|
47
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
48
|
+
}), "creating verification token");
|
|
49
|
+
try {
|
|
50
|
+
await sendMail({
|
|
51
|
+
from: "hello@studious.sh",
|
|
52
|
+
to: user.email,
|
|
53
|
+
subject: "Verify your email",
|
|
54
|
+
text: `Click the link to verify your email: ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
logger.error("Failed to send verification email", {
|
|
59
|
+
email: user.email,
|
|
60
|
+
err,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return { user: { id: user.id, username: user.username } };
|
|
64
|
+
}
|
|
65
|
+
/** Authenticate user by username/password, create session, and cache in Redis. */
|
|
66
|
+
export async function login(input) {
|
|
67
|
+
const { username, password } = input;
|
|
68
|
+
const user = await findUserByUsername(username);
|
|
69
|
+
if (!user) {
|
|
70
|
+
throw new TRPCError({
|
|
71
|
+
code: "UNAUTHORIZED",
|
|
72
|
+
message: "Invalid username or password",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (!(await compare(password, user.password))) {
|
|
76
|
+
throw new TRPCError({
|
|
77
|
+
code: "UNAUTHORIZED",
|
|
78
|
+
message: "Invalid username or password",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!user.verified) {
|
|
82
|
+
return {
|
|
83
|
+
verified: false,
|
|
84
|
+
user: { email: user.email },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const session = await createSession({
|
|
88
|
+
id: uuidv4(),
|
|
89
|
+
userId: user.id,
|
|
90
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
91
|
+
});
|
|
92
|
+
const cachePayload = JSON.stringify({ id: user.id, username: user.username });
|
|
93
|
+
const ttlSeconds = 30 * 24 * 60 * 60; // 30 days, match session expiry
|
|
94
|
+
const r = getRedis();
|
|
95
|
+
if (r)
|
|
96
|
+
await r.setex(`session:${session.id}`, ttlSeconds, cachePayload);
|
|
97
|
+
return {
|
|
98
|
+
token: session.id,
|
|
99
|
+
user: { id: user.id, username: user.username },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Delete all sessions for a user (logs them out everywhere). */
|
|
103
|
+
export async function logout(userId) {
|
|
104
|
+
await deleteSessionsByUserId(userId);
|
|
105
|
+
return { success: true };
|
|
106
|
+
}
|
|
107
|
+
/** Verify user exists and return profile. Used for session validation. */
|
|
108
|
+
export async function check(userId) {
|
|
109
|
+
const user = await findUserById(userId);
|
|
110
|
+
if (!user) {
|
|
111
|
+
throw new TRPCError({
|
|
112
|
+
code: "NOT_FOUND",
|
|
113
|
+
message: "User not found",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return { user };
|
|
117
|
+
}
|
|
118
|
+
/** Resend verification email. Replaces existing sessions and sends new link. */
|
|
119
|
+
export async function resendVerificationEmail(email) {
|
|
120
|
+
const user = await findUserByEmail(email);
|
|
121
|
+
if (!user) {
|
|
122
|
+
throw new TRPCError({
|
|
123
|
+
code: "NOT_FOUND",
|
|
124
|
+
message: "User not found",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
await deleteSessionsByUserId(user.id);
|
|
128
|
+
const verificationToken = await createSession({
|
|
129
|
+
id: uuidv4(),
|
|
130
|
+
userId: user.id,
|
|
131
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
|
|
132
|
+
});
|
|
133
|
+
try {
|
|
134
|
+
await sendMail({
|
|
135
|
+
from: "hello@studious.sh",
|
|
136
|
+
to: user.email,
|
|
137
|
+
subject: "Verify your email",
|
|
138
|
+
text: `Click the link to verify your email: ${env.NEXT_PUBLIC_APP_URL}/verify/${verificationToken.id}`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
logger.error("Failed to send verification email", {
|
|
143
|
+
email: user.email,
|
|
144
|
+
err,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return { success: true };
|
|
148
|
+
}
|
|
149
|
+
/** Verify email via token. Marks user as verified and deletes the session. */
|
|
150
|
+
export async function verify(token) {
|
|
151
|
+
const session = await findSessionById(token);
|
|
152
|
+
if (!session) {
|
|
153
|
+
throw new TRPCError({
|
|
154
|
+
code: "NOT_FOUND",
|
|
155
|
+
message: "Session not found",
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (session.expiresAt && session.expiresAt < new Date()) {
|
|
159
|
+
throw new TRPCError({
|
|
160
|
+
code: "UNAUTHORIZED",
|
|
161
|
+
message: "Session expired",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
await updateUserVerified(session.userId, true);
|
|
165
|
+
await deleteSessionById(token);
|
|
166
|
+
return { success: true };
|
|
167
|
+
}
|
|
168
|
+
/** Send password reset email. Silently succeeds if email not found (security). */
|
|
169
|
+
export async function requestPasswordReset(email) {
|
|
170
|
+
const user = await findUserByEmail(email);
|
|
171
|
+
if (!user) {
|
|
172
|
+
return { success: true };
|
|
173
|
+
}
|
|
174
|
+
const twoHoursFromNow = new Date(Date.now() + 1000 * 60 * 60 * 2);
|
|
175
|
+
await deleteShortLivedSessionsByUserId(user.id, twoHoursFromNow);
|
|
176
|
+
const resetToken = await createSession({
|
|
177
|
+
id: uuidv4(),
|
|
178
|
+
userId: user.id,
|
|
179
|
+
expiresAt: new Date(Date.now() + 1000 * 60 * 60),
|
|
180
|
+
});
|
|
181
|
+
try {
|
|
182
|
+
await sendMail({
|
|
183
|
+
from: "hello@studious.sh",
|
|
184
|
+
to: user.email,
|
|
185
|
+
subject: "Reset your password",
|
|
186
|
+
text: `Click the link to reset your password: ${env.NEXT_PUBLIC_APP_URL}/reset-password/${resetToken.id}`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
logger.error("Failed to send password reset email", {
|
|
191
|
+
email: user.email,
|
|
192
|
+
err,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return { success: true };
|
|
196
|
+
}
|
|
197
|
+
/** Reset password using token from email. Validates token and updates password. */
|
|
198
|
+
export async function resetPassword(token, password) {
|
|
199
|
+
const session = await findSessionByIdWithUser(token);
|
|
200
|
+
if (!session || !session.userId) {
|
|
201
|
+
throw new TRPCError({
|
|
202
|
+
code: "NOT_FOUND",
|
|
203
|
+
message: "Invalid or expired reset token",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (session.expiresAt && session.expiresAt < new Date()) {
|
|
207
|
+
await deleteSessionById(token);
|
|
208
|
+
throw new TRPCError({
|
|
209
|
+
code: "UNAUTHORIZED",
|
|
210
|
+
message: "Reset token has expired",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
await updateUserPassword(session.userId, await hash(password, 10));
|
|
214
|
+
await deleteSessionById(token);
|
|
215
|
+
return { success: true };
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=auth.js.map
|
|
218
|
+
//# debugId=18f3fceb-a95b-5d3c-b80c-c8f5a01da04e
|