@studious-lms/server 1.2.52 → 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 +36 -94
- 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 +1399 -1271
- 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 +140 -68
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +82 -1051
- 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/aiNewtonChat.d.ts.map +1 -1
- package/dist/server/pipelines/aiNewtonChat.js +8 -3
- package/dist/server/pipelines/aiNewtonChat.js.map +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 +83 -136
- 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 +15 -6
- 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 -1186
- 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
|
@@ -2,10 +2,26 @@ import { z } from "zod";
|
|
|
2
2
|
import { createTRPCRouter, protectedClassMemberProcedure, protectedTeacherProcedure, protectedProcedure } from "../trpc.js";
|
|
3
3
|
import { prisma } from "../lib/prisma.js";
|
|
4
4
|
import { TRPCError } from "@trpc/server";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getAllAnnouncements,
|
|
7
|
+
getAnnouncement,
|
|
8
|
+
createAnnouncementRecord,
|
|
9
|
+
updateAnnouncementRecord,
|
|
10
|
+
deleteAnnouncementRecord,
|
|
11
|
+
} from "../services/announcement.js";
|
|
12
|
+
import { findAnnouncementByIdAndClass } from "../models/announcement.js";
|
|
13
|
+
import {
|
|
14
|
+
findCommentWithAnnouncement,
|
|
15
|
+
findReactionByUserAndComment,
|
|
16
|
+
upsertReaction,
|
|
17
|
+
deleteReactionById,
|
|
18
|
+
} from "../models/comment.js";
|
|
19
|
+
import { getReactions as getCommentReactions } from "../services/comment.js";
|
|
20
|
+
|
|
6
21
|
import { logger } from "../utils/logger.js";
|
|
7
22
|
import { createDirectUploadFiles, type UploadedFile, type DirectUploadFile, confirmDirectUpload } from "../lib/fileUpload.js";
|
|
8
23
|
import { deleteFile } from "../lib/googleCloudStorage.js";
|
|
24
|
+
import { sendToMultiple } from "../services/notification.js";
|
|
9
25
|
|
|
10
26
|
// Schema for direct file uploads (no base64 data)
|
|
11
27
|
const directFileSchema = z.object({
|
|
@@ -27,97 +43,14 @@ const confirmAnnouncementUploadSchema = z.object({
|
|
|
27
43
|
errorMessage: z.string().optional(),
|
|
28
44
|
});
|
|
29
45
|
|
|
30
|
-
const AnnouncementSelect = {
|
|
31
|
-
id: true,
|
|
32
|
-
teacher: {
|
|
33
|
-
select: {
|
|
34
|
-
id: true,
|
|
35
|
-
username: true,
|
|
36
|
-
profile: {
|
|
37
|
-
select: {
|
|
38
|
-
displayName: true,
|
|
39
|
-
profilePicture: true,
|
|
40
|
-
profilePictureThumbnail: true,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
remarks: true,
|
|
46
|
-
createdAt: true,
|
|
47
|
-
modifiedAt: true,
|
|
48
|
-
attachments: {
|
|
49
|
-
select: {
|
|
50
|
-
id: true,
|
|
51
|
-
name: true,
|
|
52
|
-
type: true,
|
|
53
|
-
size: true,
|
|
54
|
-
path: true,
|
|
55
|
-
uploadedAt: true,
|
|
56
|
-
thumbnailId: true,
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
|
|
61
46
|
export const announcementRouter = createTRPCRouter({
|
|
62
47
|
getAll: protectedClassMemberProcedure
|
|
63
|
-
.input(z.object({
|
|
64
|
-
|
|
65
|
-
}))
|
|
66
|
-
.query(async ({ ctx, input }) => {
|
|
67
|
-
const announcements = await prisma.announcement.findMany({
|
|
68
|
-
where: {
|
|
69
|
-
classId: input.classId,
|
|
70
|
-
},
|
|
71
|
-
select: {
|
|
72
|
-
...AnnouncementSelect,
|
|
73
|
-
_count: {
|
|
74
|
-
select: {
|
|
75
|
-
comments: true,
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
orderBy: {
|
|
80
|
-
createdAt: 'desc',
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Transform to include comment count
|
|
85
|
-
const announcementsWithCounts = announcements.map(announcement => ({
|
|
86
|
-
...announcement,
|
|
87
|
-
commentCount: announcement._count.comments,
|
|
88
|
-
_count: undefined,
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
announcements: announcementsWithCounts,
|
|
93
|
-
};
|
|
94
|
-
}),
|
|
48
|
+
.input(z.object({ classId: z.string() }))
|
|
49
|
+
.query(({ input }) => getAllAnnouncements(input.classId)),
|
|
95
50
|
|
|
96
51
|
get: protectedClassMemberProcedure
|
|
97
|
-
.input(z.object({
|
|
98
|
-
|
|
99
|
-
classId: z.string(),
|
|
100
|
-
}))
|
|
101
|
-
.query(async ({ ctx, input }) => {
|
|
102
|
-
const announcement = await prisma.announcement.findUnique({
|
|
103
|
-
where: {
|
|
104
|
-
id: input.id,
|
|
105
|
-
classId: input.classId,
|
|
106
|
-
},
|
|
107
|
-
select: AnnouncementSelect,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
if (!announcement) {
|
|
111
|
-
throw new TRPCError({
|
|
112
|
-
code: "NOT_FOUND",
|
|
113
|
-
message: "Announcement not found",
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
announcement,
|
|
119
|
-
};
|
|
120
|
-
}),
|
|
52
|
+
.input(z.object({ id: z.string(), classId: z.string() }))
|
|
53
|
+
.query(({ input }) => getAnnouncement(input.id, input.classId)),
|
|
121
54
|
|
|
122
55
|
create: protectedTeacherProcedure
|
|
123
56
|
.input(z.object({
|
|
@@ -126,92 +59,14 @@ export const announcementRouter = createTRPCRouter({
|
|
|
126
59
|
files: z.array(directFileSchema).optional(),
|
|
127
60
|
existingFileIds: z.array(z.string()).optional(),
|
|
128
61
|
}))
|
|
129
|
-
.mutation(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const classData = await prisma.class.findUnique({
|
|
140
|
-
where: { id: classId },
|
|
141
|
-
include: {
|
|
142
|
-
students: {
|
|
143
|
-
select: { id: true }
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
if (!classData) {
|
|
149
|
-
throw new TRPCError({
|
|
150
|
-
code: "NOT_FOUND",
|
|
151
|
-
message: "Class not found",
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const announcement = await prisma.announcement.create({
|
|
156
|
-
data: {
|
|
157
|
-
remarks: remarks,
|
|
158
|
-
teacher: {
|
|
159
|
-
connect: {
|
|
160
|
-
id: ctx.user.id,
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
class: {
|
|
164
|
-
connect: {
|
|
165
|
-
id: classId,
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
select: AnnouncementSelect,
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// Handle file attachments
|
|
173
|
-
// NOTE: Files are now handled via direct upload endpoints
|
|
174
|
-
// The files field in the schema is for metadata only
|
|
175
|
-
// Actual file uploads should use getAnnouncementUploadUrls endpoint
|
|
176
|
-
// However, if files are provided here, we create the file records and return upload URLs
|
|
177
|
-
let directUploadFiles: DirectUploadFile[] = [];
|
|
178
|
-
if (files && files.length > 0) {
|
|
179
|
-
// Create direct upload files - this creates file records with upload URLs
|
|
180
|
-
// Files are automatically connected to the announcement via announcementId
|
|
181
|
-
directUploadFiles = await createDirectUploadFiles(files, ctx.user.id, undefined, undefined, undefined, announcement.id);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Connect existing files if provided
|
|
185
|
-
if (existingFileIds && existingFileIds.length > 0) {
|
|
186
|
-
await prisma.announcement.update({
|
|
187
|
-
where: { id: announcement.id },
|
|
188
|
-
data: {
|
|
189
|
-
attachments: {
|
|
190
|
-
connect: existingFileIds.map(fileId => ({ id: fileId }))
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Fetch announcement with attachments
|
|
197
|
-
const announcementWithAttachments = await prisma.announcement.findUnique({
|
|
198
|
-
where: { id: announcement.id },
|
|
199
|
-
select: AnnouncementSelect,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
sendNotifications(classData.students.map(student => student.id), {
|
|
203
|
-
title: `🔔 Announcement for ${classData.name}`,
|
|
204
|
-
content: remarks
|
|
205
|
-
}).catch(error => {
|
|
206
|
-
logger.error('Failed to send announcement notifications:', error);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
announcement: announcementWithAttachments || announcement,
|
|
211
|
-
// Return upload URLs if files were provided
|
|
212
|
-
uploadFiles: directUploadFiles.length > 0 ? directUploadFiles : undefined,
|
|
213
|
-
};
|
|
214
|
-
}),
|
|
62
|
+
.mutation(({ ctx, input }) =>
|
|
63
|
+
createAnnouncementRecord(ctx.user!.id, {
|
|
64
|
+
classId: input.classId,
|
|
65
|
+
remarks: input.remarks,
|
|
66
|
+
files: input.files,
|
|
67
|
+
existingFileIds: input.existingFileIds,
|
|
68
|
+
})
|
|
69
|
+
),
|
|
215
70
|
|
|
216
71
|
update: protectedTeacherProcedure
|
|
217
72
|
.input(z.object({
|
|
@@ -224,180 +79,19 @@ export const announcementRouter = createTRPCRouter({
|
|
|
224
79
|
removedAttachments: z.array(z.string()).optional(),
|
|
225
80
|
}),
|
|
226
81
|
}))
|
|
227
|
-
.mutation(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const announcement = await prisma.announcement.findUnique({
|
|
236
|
-
where: { id: input.id },
|
|
237
|
-
include: {
|
|
238
|
-
class: {
|
|
239
|
-
include: {
|
|
240
|
-
teachers: true,
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
attachments: {
|
|
244
|
-
select: {
|
|
245
|
-
id: true,
|
|
246
|
-
name: true,
|
|
247
|
-
type: true,
|
|
248
|
-
path: true,
|
|
249
|
-
size: true,
|
|
250
|
-
uploadStatus: true,
|
|
251
|
-
thumbnail: {
|
|
252
|
-
select: {
|
|
253
|
-
path: true
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
if (!announcement) {
|
|
262
|
-
throw new TRPCError({
|
|
263
|
-
code: "NOT_FOUND",
|
|
264
|
-
message: "Announcement not found",
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Authorization check: user must be the creator OR a teacher in the class
|
|
269
|
-
const userId = ctx.user.id;
|
|
270
|
-
const isCreator = announcement.teacherId === userId;
|
|
271
|
-
const isClassTeacher = announcement.class.teachers.some(
|
|
272
|
-
(teacher) => teacher.id === userId
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
if (!isCreator && !isClassTeacher) {
|
|
276
|
-
throw new TRPCError({
|
|
277
|
-
code: "FORBIDDEN",
|
|
278
|
-
message: "Only the announcement creator or class teachers can update announcements",
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Handle file attachments
|
|
283
|
-
// NOTE: Files are now handled via direct upload endpoints
|
|
284
|
-
let directUploadFiles: DirectUploadFile[] = [];
|
|
285
|
-
if (input.data.files && input.data.files.length > 0) {
|
|
286
|
-
// Create direct upload files - this creates file records with upload URLs
|
|
287
|
-
// Files are automatically connected to the announcement via announcementId
|
|
288
|
-
directUploadFiles = await createDirectUploadFiles(input.data.files, userId, undefined, undefined, undefined, input.id);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Delete removed attachments from storage before updating database
|
|
292
|
-
if (input.data.removedAttachments && input.data.removedAttachments.length > 0) {
|
|
293
|
-
const filesToDelete = announcement.attachments.filter((file) =>
|
|
294
|
-
input.data.removedAttachments!.includes(file.id)
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
// Delete files from storage (only if they were actually uploaded)
|
|
298
|
-
await Promise.all(filesToDelete.map(async (file) => {
|
|
299
|
-
try {
|
|
300
|
-
// Only delete from GCS if the file was successfully uploaded
|
|
301
|
-
if (file.uploadStatus === 'COMPLETED') {
|
|
302
|
-
// Delete the main file
|
|
303
|
-
await deleteFile(file.path);
|
|
304
|
-
|
|
305
|
-
// Delete thumbnail if it exists
|
|
306
|
-
if (file.thumbnail?.path) {
|
|
307
|
-
await deleteFile(file.thumbnail.path);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
} catch (error) {
|
|
311
|
-
logger.warn(`Failed to delete file ${file.path}:`, {
|
|
312
|
-
error: error instanceof Error ? {
|
|
313
|
-
name: error.name,
|
|
314
|
-
message: error.message,
|
|
315
|
-
stack: error.stack,
|
|
316
|
-
} : error
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}));
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const updatedAnnouncement = await prisma.announcement.update({
|
|
323
|
-
where: { id: input.id },
|
|
324
|
-
data: {
|
|
325
|
-
...(input.data.remarks && { remarks: input.data.remarks }),
|
|
326
|
-
// Note: directUploadFiles are already connected via createDirectUploadFiles
|
|
327
|
-
...(input.data.existingFileIds && input.data.existingFileIds.length > 0 && {
|
|
328
|
-
attachments: {
|
|
329
|
-
connect: input.data.existingFileIds.map(fileId => ({ id: fileId }))
|
|
330
|
-
}
|
|
331
|
-
}),
|
|
332
|
-
...(input.data.removedAttachments && input.data.removedAttachments.length > 0 && {
|
|
333
|
-
attachments: {
|
|
334
|
-
deleteMany: {
|
|
335
|
-
id: { in: input.data.removedAttachments }
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}),
|
|
339
|
-
},
|
|
340
|
-
select: AnnouncementSelect,
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
return {
|
|
344
|
-
announcement: updatedAnnouncement,
|
|
345
|
-
// Return upload URLs if new files were provided
|
|
346
|
-
uploadFiles: directUploadFiles.length > 0 ? directUploadFiles : undefined,
|
|
347
|
-
};
|
|
348
|
-
}),
|
|
82
|
+
.mutation(({ ctx, input }) =>
|
|
83
|
+
updateAnnouncementRecord(ctx.user!.id, {
|
|
84
|
+
id: input.id,
|
|
85
|
+
classId: input.classId,
|
|
86
|
+
data: input.data,
|
|
87
|
+
})
|
|
88
|
+
),
|
|
349
89
|
|
|
350
90
|
delete: protectedTeacherProcedure
|
|
351
|
-
.input(z.object({
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
.mutation(async ({ ctx, input }) => {
|
|
356
|
-
if (!ctx.user) {
|
|
357
|
-
throw new TRPCError({
|
|
358
|
-
code: "UNAUTHORIZED",
|
|
359
|
-
message: "User must be authenticated",
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const announcement = await prisma.announcement.findUnique({
|
|
364
|
-
where: { id: input.id },
|
|
365
|
-
include: {
|
|
366
|
-
class: {
|
|
367
|
-
include: {
|
|
368
|
-
teachers: true,
|
|
369
|
-
},
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
if (!announcement) {
|
|
375
|
-
throw new TRPCError({
|
|
376
|
-
code: "NOT_FOUND",
|
|
377
|
-
message: "Announcement not found",
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Authorization check: user must be the creator OR a teacher in the class
|
|
382
|
-
const userId = ctx.user.id;
|
|
383
|
-
const isCreator = announcement.teacherId === userId;
|
|
384
|
-
const isClassTeacher = announcement.class.teachers.some(
|
|
385
|
-
(teacher) => teacher.id === userId
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
if (!isCreator && !isClassTeacher) {
|
|
389
|
-
throw new TRPCError({
|
|
390
|
-
code: "FORBIDDEN",
|
|
391
|
-
message: "Only the announcement creator or class teachers can delete announcements",
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
await prisma.announcement.delete({
|
|
396
|
-
where: { id: input.id },
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
return { success: true };
|
|
400
|
-
}),
|
|
91
|
+
.input(z.object({ id: z.string(), classId: z.string() }))
|
|
92
|
+
.mutation(({ ctx, input }) =>
|
|
93
|
+
deleteAnnouncementRecord(ctx.user!.id, input.id, input.classId)
|
|
94
|
+
),
|
|
401
95
|
|
|
402
96
|
getAnnouncementUploadUrls: protectedTeacherProcedure
|
|
403
97
|
.input(getAnnouncementUploadUrlsSchema)
|
|
@@ -430,14 +124,7 @@ export const announcementRouter = createTRPCRouter({
|
|
|
430
124
|
});
|
|
431
125
|
}
|
|
432
126
|
|
|
433
|
-
|
|
434
|
-
const announcement = await prisma.announcement.findFirst({
|
|
435
|
-
where: {
|
|
436
|
-
id: announcementId,
|
|
437
|
-
classId: classId,
|
|
438
|
-
},
|
|
439
|
-
});
|
|
440
|
-
|
|
127
|
+
const announcement = await findAnnouncementByIdAndClass(announcementId, classId);
|
|
441
128
|
if (!announcement) {
|
|
442
129
|
throw new TRPCError({
|
|
443
130
|
code: "NOT_FOUND",
|
|
@@ -515,14 +202,7 @@ export const announcementRouter = createTRPCRouter({
|
|
|
515
202
|
});
|
|
516
203
|
}
|
|
517
204
|
|
|
518
|
-
|
|
519
|
-
const announcement = await prisma.announcement.findFirst({
|
|
520
|
-
where: {
|
|
521
|
-
id: input.announcementId,
|
|
522
|
-
classId: input.classId,
|
|
523
|
-
},
|
|
524
|
-
});
|
|
525
|
-
|
|
205
|
+
const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
|
|
526
206
|
if (!announcement) {
|
|
527
207
|
throw new TRPCError({
|
|
528
208
|
code: "NOT_FOUND",
|
|
@@ -700,14 +380,7 @@ export const announcementRouter = createTRPCRouter({
|
|
|
700
380
|
classId: z.string(),
|
|
701
381
|
}))
|
|
702
382
|
.query(async ({ ctx, input }) => {
|
|
703
|
-
|
|
704
|
-
const announcement = await prisma.announcement.findFirst({
|
|
705
|
-
where: {
|
|
706
|
-
id: input.announcementId,
|
|
707
|
-
classId: input.classId,
|
|
708
|
-
},
|
|
709
|
-
});
|
|
710
|
-
|
|
383
|
+
const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
|
|
711
384
|
if (!announcement) {
|
|
712
385
|
throw new TRPCError({
|
|
713
386
|
code: "NOT_FOUND",
|
|
@@ -797,15 +470,8 @@ export const announcementRouter = createTRPCRouter({
|
|
|
797
470
|
|
|
798
471
|
const userId = ctx.user.id;
|
|
799
472
|
|
|
800
|
-
// Verify the announcement or comment exists and belongs to the class
|
|
801
473
|
if (input.announcementId) {
|
|
802
|
-
const announcement = await
|
|
803
|
-
where: {
|
|
804
|
-
id: input.announcementId,
|
|
805
|
-
classId: input.classId,
|
|
806
|
-
},
|
|
807
|
-
});
|
|
808
|
-
|
|
474
|
+
const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
|
|
809
475
|
if (!announcement) {
|
|
810
476
|
throw new TRPCError({
|
|
811
477
|
code: "NOT_FOUND",
|
|
@@ -848,25 +514,13 @@ export const announcementRouter = createTRPCRouter({
|
|
|
848
514
|
|
|
849
515
|
return { reaction };
|
|
850
516
|
} else if (input.commentId) {
|
|
851
|
-
|
|
852
|
-
const comment = await prisma.comment.findUnique({
|
|
853
|
-
where: { id: input.commentId },
|
|
854
|
-
include: {
|
|
855
|
-
announcement: {
|
|
856
|
-
select: {
|
|
857
|
-
classId: true,
|
|
858
|
-
},
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
});
|
|
862
|
-
|
|
517
|
+
const comment = await findCommentWithAnnouncement(input.commentId);
|
|
863
518
|
if (!comment) {
|
|
864
519
|
throw new TRPCError({
|
|
865
520
|
code: "NOT_FOUND",
|
|
866
521
|
message: "Comment not found",
|
|
867
522
|
});
|
|
868
523
|
}
|
|
869
|
-
|
|
870
524
|
if (comment.announcement!.classId !== input.classId) {
|
|
871
525
|
throw new TRPCError({
|
|
872
526
|
code: "FORBIDDEN",
|
|
@@ -874,39 +528,11 @@ export const announcementRouter = createTRPCRouter({
|
|
|
874
528
|
});
|
|
875
529
|
}
|
|
876
530
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
userId,
|
|
882
|
-
commentId: input.commentId,
|
|
883
|
-
},
|
|
884
|
-
},
|
|
885
|
-
update: {
|
|
886
|
-
type: input.type,
|
|
887
|
-
},
|
|
888
|
-
create: {
|
|
889
|
-
type: input.type,
|
|
890
|
-
userId,
|
|
891
|
-
commentId: input.commentId,
|
|
892
|
-
},
|
|
893
|
-
include: {
|
|
894
|
-
user: {
|
|
895
|
-
select: {
|
|
896
|
-
id: true,
|
|
897
|
-
username: true,
|
|
898
|
-
profile: {
|
|
899
|
-
select: {
|
|
900
|
-
displayName: true,
|
|
901
|
-
profilePicture: true,
|
|
902
|
-
profilePictureThumbnail: true,
|
|
903
|
-
},
|
|
904
|
-
},
|
|
905
|
-
},
|
|
906
|
-
},
|
|
907
|
-
},
|
|
531
|
+
const reaction = await upsertReaction({
|
|
532
|
+
userId,
|
|
533
|
+
commentId: input.commentId,
|
|
534
|
+
type: input.type,
|
|
908
535
|
});
|
|
909
|
-
|
|
910
536
|
return { reaction };
|
|
911
537
|
}
|
|
912
538
|
|
|
@@ -962,26 +588,14 @@ export const announcementRouter = createTRPCRouter({
|
|
|
962
588
|
|
|
963
589
|
return { success: true };
|
|
964
590
|
} else if (input.commentId) {
|
|
965
|
-
const reaction = await
|
|
966
|
-
where: {
|
|
967
|
-
userId_commentId: {
|
|
968
|
-
userId,
|
|
969
|
-
commentId: input.commentId,
|
|
970
|
-
},
|
|
971
|
-
},
|
|
972
|
-
});
|
|
973
|
-
|
|
591
|
+
const reaction = await findReactionByUserAndComment(userId, input.commentId);
|
|
974
592
|
if (!reaction) {
|
|
975
593
|
throw new TRPCError({
|
|
976
594
|
code: "NOT_FOUND",
|
|
977
595
|
message: "Reaction not found",
|
|
978
596
|
});
|
|
979
597
|
}
|
|
980
|
-
|
|
981
|
-
await prisma.reaction.delete({
|
|
982
|
-
where: { id: reaction.id },
|
|
983
|
-
});
|
|
984
|
-
|
|
598
|
+
await deleteReactionById(reaction.id);
|
|
985
599
|
return { success: true };
|
|
986
600
|
}
|
|
987
601
|
|
|
@@ -1016,14 +630,7 @@ export const announcementRouter = createTRPCRouter({
|
|
|
1016
630
|
const userId = ctx.user.id;
|
|
1017
631
|
|
|
1018
632
|
if (input.announcementId) {
|
|
1019
|
-
|
|
1020
|
-
const announcement = await prisma.announcement.findFirst({
|
|
1021
|
-
where: {
|
|
1022
|
-
id: input.announcementId,
|
|
1023
|
-
classId: input.classId,
|
|
1024
|
-
},
|
|
1025
|
-
});
|
|
1026
|
-
|
|
633
|
+
const announcement = await findAnnouncementByIdAndClass(input.announcementId, input.classId);
|
|
1027
634
|
if (!announcement) {
|
|
1028
635
|
throw new TRPCError({
|
|
1029
636
|
code: "NOT_FOUND",
|
|
@@ -1068,68 +675,20 @@ export const announcementRouter = createTRPCRouter({
|
|
|
1068
675
|
total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),
|
|
1069
676
|
};
|
|
1070
677
|
} else if (input.commentId) {
|
|
1071
|
-
|
|
1072
|
-
const comment = await prisma.comment.findUnique({
|
|
1073
|
-
where: { id: input.commentId },
|
|
1074
|
-
include: {
|
|
1075
|
-
announcement: {
|
|
1076
|
-
select: {
|
|
1077
|
-
classId: true,
|
|
1078
|
-
},
|
|
1079
|
-
},
|
|
1080
|
-
},
|
|
1081
|
-
});
|
|
1082
|
-
|
|
678
|
+
const comment = await findCommentWithAnnouncement(input.commentId);
|
|
1083
679
|
if (!comment) {
|
|
1084
680
|
throw new TRPCError({
|
|
1085
681
|
code: "NOT_FOUND",
|
|
1086
682
|
message: "Comment not found",
|
|
1087
683
|
});
|
|
1088
684
|
}
|
|
1089
|
-
|
|
1090
685
|
if (comment.announcement!.classId !== input.classId) {
|
|
1091
686
|
throw new TRPCError({
|
|
1092
687
|
code: "FORBIDDEN",
|
|
1093
688
|
message: "Comment does not belong to this class",
|
|
1094
689
|
});
|
|
1095
690
|
}
|
|
1096
|
-
|
|
1097
|
-
// Get reaction counts by type
|
|
1098
|
-
const reactionCounts = await prisma.reaction.groupBy({
|
|
1099
|
-
by: ['type'],
|
|
1100
|
-
where: { commentId: input.commentId },
|
|
1101
|
-
_count: { type: true },
|
|
1102
|
-
});
|
|
1103
|
-
|
|
1104
|
-
// Get current user's reaction
|
|
1105
|
-
const userReaction = await prisma.reaction.findUnique({
|
|
1106
|
-
where: {
|
|
1107
|
-
userId_commentId: {
|
|
1108
|
-
userId,
|
|
1109
|
-
commentId: input.commentId,
|
|
1110
|
-
},
|
|
1111
|
-
},
|
|
1112
|
-
});
|
|
1113
|
-
|
|
1114
|
-
// Format counts
|
|
1115
|
-
const counts = {
|
|
1116
|
-
THUMBSUP: 0,
|
|
1117
|
-
CELEBRATE: 0,
|
|
1118
|
-
CARE: 0,
|
|
1119
|
-
HEART: 0,
|
|
1120
|
-
IDEA: 0,
|
|
1121
|
-
HAPPY: 0,
|
|
1122
|
-
};
|
|
1123
|
-
|
|
1124
|
-
reactionCounts.forEach((item) => {
|
|
1125
|
-
counts[item.type as keyof typeof counts] = item._count.type;
|
|
1126
|
-
});
|
|
1127
|
-
|
|
1128
|
-
return {
|
|
1129
|
-
counts,
|
|
1130
|
-
userReaction: userReaction?.type || null,
|
|
1131
|
-
total: reactionCounts.reduce((sum, item) => sum + item._count.type, 0),
|
|
1132
|
-
};
|
|
691
|
+
return getCommentReactions(userId, input.commentId);
|
|
1133
692
|
}
|
|
1134
693
|
|
|
1135
694
|
throw new TRPCError({
|