@studious-lms/server 1.2.53 → 1.4.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 +461 -0
- package/dist/models/class.d.ts.map +1 -0
- package/dist/models/class.js +645 -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 +76 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -0
- package/dist/pipelines/aiLabChat.js +599 -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 +1523 -1315
- 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 +160 -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 +21 -11
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +22 -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 +13 -2
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +32 -520
- 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 +68 -55
- 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 +643 -0
- package/dist/services/class.d.ts.map +1 -0
- package/dist/services/class.js +486 -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 +169 -0
- package/dist/services/labChat.d.ts.map +1 -0
- package/dist/services/labChat.js +381 -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 +103 -0
- package/dist/services/message.d.ts.map +1 -0
- package/dist/services/message.js +422 -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/prisma/schema.prisma +1 -1
- 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 +703 -0
- package/src/models/comment.ts +152 -0
- package/src/models/conversation.ts +200 -0
- package/src/models/event.ts +177 -0
- package/src/models/file.ts +129 -0
- package/src/models/folder.ts +225 -0
- package/src/models/labChat.ts +213 -0
- package/src/models/marketing.ts +45 -0
- package/src/models/message.ts +153 -0
- package/src/models/newtonChat.ts +70 -0
- package/src/models/notification.ts +54 -0
- package/src/models/section.ts +98 -0
- package/src/models/user.ts +47 -0
- package/src/models/worksheet.ts +294 -0
- package/src/pipelines/aiLabChat.ts +684 -0
- 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 +35 -604
- package/src/routers/marketing.ts +35 -77
- package/src/routers/message.ts +54 -567
- 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 +629 -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 +458 -0
- package/src/services/marketing.ts +57 -0
- package/src/services/message.ts +554 -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/src/server/pipelines/aiLabChat.ts +0 -507
- package/tests/announcement.test.ts +0 -164
- package/tests/assignment.test.ts +0 -296
- package/tests/auth.test.ts +0 -48
package/src/index.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { logger } from './utils/logger.js';
|
|
|
11
11
|
import { setupSocketHandlers } from './socket/handlers.js';
|
|
12
12
|
import { bucket } from './lib/googleCloudStorage.js';
|
|
13
13
|
import { prisma } from './lib/prisma.js';
|
|
14
|
+
import { pusher } from './lib/pusher.js';
|
|
15
|
+
import { connectRedis, disconnectRedis } from './lib/redis.js';
|
|
14
16
|
|
|
15
17
|
import { authLimiter, generalLimiter, helmetConfig, uploadLimiter } from './middleware/security.js';
|
|
16
18
|
|
|
@@ -27,6 +29,8 @@ const app = express();
|
|
|
27
29
|
|
|
28
30
|
app.use(helmetConfig);
|
|
29
31
|
app.use(compression());
|
|
32
|
+
app.use(express.json());
|
|
33
|
+
app.use(express.urlencoded({ extended: true }));
|
|
30
34
|
|
|
31
35
|
app.use((req, res, next) => {
|
|
32
36
|
const requestId = uuidv4();
|
|
@@ -38,6 +42,8 @@ const allowedOrigins = env.NODE_ENV === 'production'
|
|
|
38
42
|
? [
|
|
39
43
|
'https://www.studious.sh',
|
|
40
44
|
'https://studious.sh',
|
|
45
|
+
'https://dev.studious.sh',
|
|
46
|
+
'https://www.dev.studious.sh',
|
|
41
47
|
env.NEXT_PUBLIC_APP_URL,
|
|
42
48
|
'http://localhost:3000',
|
|
43
49
|
|
|
@@ -137,6 +143,103 @@ app.get('/health', async (req, res) => {
|
|
|
137
143
|
}
|
|
138
144
|
});
|
|
139
145
|
|
|
146
|
+
// Pusher channel auth (for private-* and presence-* channels)
|
|
147
|
+
// Token from: x-user header, or cookie (same-origin requests send cookies automatically)
|
|
148
|
+
app.post('/api/pusher/auth', async (req, res) => {
|
|
149
|
+
try {
|
|
150
|
+
let token = req.headers['x-user'] as string | undefined;
|
|
151
|
+
if (!token && req.headers.cookie) {
|
|
152
|
+
const cookieName = env.PUSHER_AUTH_COOKIE_NAME || 'token';
|
|
153
|
+
const match = req.headers.cookie.match(new RegExp(`${cookieName}=([^;]+)`));
|
|
154
|
+
token = match?.[1]?.trim();
|
|
155
|
+
}
|
|
156
|
+
const { socket_id, channel_name } = req.body as { socket_id?: string; channel_name?: string };
|
|
157
|
+
|
|
158
|
+
if (!socket_id || !channel_name) {
|
|
159
|
+
return res.status(400).json({ error: 'socket_id and channel_name required' });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!token) {
|
|
163
|
+
return res.status(401).json({ error: 'Authentication required' });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const user = await prisma.user.findFirst({
|
|
167
|
+
where: { sessions: { some: { id: token } } },
|
|
168
|
+
select: { id: true, username: true },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (!user) {
|
|
172
|
+
return res.status(401).json({ error: 'Invalid or expired session' });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Verify channel access for private-conversation-* channels
|
|
176
|
+
if (channel_name.startsWith('private-conversation-')) {
|
|
177
|
+
const conversationId = channel_name.replace('private-conversation-', '');
|
|
178
|
+
const member = await prisma.conversationMember.findFirst({
|
|
179
|
+
where: { conversationId, userId: user.id },
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (!member) {
|
|
183
|
+
return res.status(403).json({ error: 'Not a member of this conversation' });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (channel_name.startsWith('private-worksheet-')) {
|
|
188
|
+
const worksheetId = channel_name.replace('private-worksheet-', '');
|
|
189
|
+
const worksheet = await prisma.studentWorksheetResponse.findFirst({
|
|
190
|
+
where: { id: worksheetId, OR: [
|
|
191
|
+
{ studentId: user.id },
|
|
192
|
+
{ submission: { assignment: { class: { teachers: { some: { id: user.id } } } } } },
|
|
193
|
+
] },
|
|
194
|
+
});
|
|
195
|
+
if (!worksheet) {
|
|
196
|
+
return res.status(403).json({ error: 'No access to this worksheet' });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (channel_name.startsWith('private-teacher-')) {
|
|
201
|
+
const classId = channel_name.replace('private-teacher-', '');
|
|
202
|
+
const isTeacher = await prisma.class.findFirst({
|
|
203
|
+
where: { id: classId, teachers: { some: { id: user.id } } },
|
|
204
|
+
});
|
|
205
|
+
if (!isTeacher) {
|
|
206
|
+
return res.status(403).json({ error: 'Not a teacher of this class' });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Verify channel access for private-class-* channels
|
|
211
|
+
// if (channel_name.startsWith('private-class-')) {
|
|
212
|
+
// const classId = channel_name.replace('private-class-', '');
|
|
213
|
+
// const isMember = await prisma.class.findFirst({
|
|
214
|
+
// where: {
|
|
215
|
+
// id: classId,
|
|
216
|
+
// OR: [
|
|
217
|
+
// { students: { some: { id: user.id } } },
|
|
218
|
+
// { teachers: { some: { id: user.id } } },
|
|
219
|
+
// ],
|
|
220
|
+
// },
|
|
221
|
+
// });
|
|
222
|
+
// if (!isMember) {
|
|
223
|
+
// return res.status(403).json({ error: 'Not a member of this class' });
|
|
224
|
+
// }
|
|
225
|
+
// }
|
|
226
|
+
|
|
227
|
+
if (channel_name.startsWith('presence-')) {
|
|
228
|
+
const authResponse = pusher.authorizeChannel(socket_id, channel_name, {
|
|
229
|
+
user_id: user.id,
|
|
230
|
+
user_info: { username: user.username },
|
|
231
|
+
});
|
|
232
|
+
return res.json(authResponse);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const authResponse = pusher.authorizeChannel(socket_id, channel_name);
|
|
236
|
+
return res.json(authResponse);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
logger.error('Pusher auth error', { error });
|
|
239
|
+
return res.status(500).json({ error: 'Authentication failed' });
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
140
243
|
// Setup Socket.IO
|
|
141
244
|
const io = new Server(httpServer, {
|
|
142
245
|
cors: {
|
|
@@ -472,10 +575,12 @@ Sentry.setupExpressErrorHandler(app);
|
|
|
472
575
|
|
|
473
576
|
const PORT = env.PORT || 3001;
|
|
474
577
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
port
|
|
478
|
-
|
|
578
|
+
connectRedis().then(() => {
|
|
579
|
+
httpServer.listen(PORT, () => {
|
|
580
|
+
logger.info(`Server running on port ${PORT}`, {
|
|
581
|
+
port: PORT,
|
|
582
|
+
services: ['tRPC', 'Socket.IO', env.REDIS_URL ? 'Redis' : null].filter(Boolean),
|
|
583
|
+
});
|
|
479
584
|
});
|
|
480
585
|
});
|
|
481
586
|
|
|
@@ -506,14 +611,16 @@ const gracefulShutdown = (signal: string) => {
|
|
|
506
611
|
|
|
507
612
|
io.close(() => {
|
|
508
613
|
logger.info('Socket.IO server closed');
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
614
|
+
|
|
615
|
+
disconnectRedis().then(() =>
|
|
616
|
+
prisma.$disconnect().then(() => {
|
|
617
|
+
logger.info('Database connections closed');
|
|
618
|
+
process.exit(0);
|
|
619
|
+
}).catch((err) => {
|
|
620
|
+
logger.error('Error disconnecting from database', { error: err });
|
|
621
|
+
process.exit(1);
|
|
622
|
+
})
|
|
623
|
+
);
|
|
517
624
|
});
|
|
518
625
|
});
|
|
519
626
|
|
package/src/lib/config/env.ts
CHANGED
|
@@ -36,6 +36,7 @@ const fullSchema = baseSchema.extend({
|
|
|
36
36
|
BACKEND_URL: z.string().url().default('http://localhost:3001'),
|
|
37
37
|
SENTRY_DSN: z.string().url().optional(),
|
|
38
38
|
EMAIL_HOST: z.string().min(1, 'EMAIL_HOST is required'),
|
|
39
|
+
EMAIL_PORT: z.string().transform(Number).default('587'),
|
|
39
40
|
EMAIL_USER: z.string().email('EMAIL_USER must be a valid email'),
|
|
40
41
|
EMAIL_PASS: z.string().min(1, 'EMAIL_PASS is required'),
|
|
41
42
|
EMAIL_DRY_RUN: z.string().optional().default('false'),
|
|
@@ -47,6 +48,8 @@ const fullSchema = baseSchema.extend({
|
|
|
47
48
|
PUSHER_KEY: z.string().min(1, 'PUSHER_KEY is required'),
|
|
48
49
|
PUSHER_SECRET: z.string().min(1, 'PUSHER_SECRET is required'),
|
|
49
50
|
PUSHER_CLUSTER: z.string().min(1, 'PUSHER_CLUSTER is required'),
|
|
51
|
+
PUSHER_AUTH_COOKIE_NAME: z.string().optional(), // Cookie name for session token (default: 'token')
|
|
52
|
+
REDIS_URL: z.string().url().optional(), // Redis connection URL (e.g. redis://localhost:6379)
|
|
50
53
|
INFERENCE_API_KEY: z.string().optional(),
|
|
51
54
|
INFERENCE_API_BASE_URL: z.string().url().optional(),
|
|
52
55
|
OPENAI_API_KEY: z.string().optional(),
|
|
@@ -59,6 +62,7 @@ const testSchema = baseSchema.extend({
|
|
|
59
62
|
BACKEND_URL: z.string().url().optional().default('http://localhost:3001'),
|
|
60
63
|
SENTRY_DSN: z.string().url().optional(),
|
|
61
64
|
EMAIL_HOST: z.string().optional().default('smtp.test.com'),
|
|
65
|
+
EMAIL_PORT: z.string().transform(Number).default('587'),
|
|
62
66
|
EMAIL_USER: z.string().email().optional().default('test@test.com'),
|
|
63
67
|
EMAIL_PASS: z.string().optional().default('test'),
|
|
64
68
|
EMAIL_DRY_RUN: z.string().optional().default('false'),
|
|
@@ -70,6 +74,8 @@ const testSchema = baseSchema.extend({
|
|
|
70
74
|
PUSHER_KEY: z.string().optional().default('test-key'),
|
|
71
75
|
PUSHER_SECRET: z.string().optional().default('test-secret'),
|
|
72
76
|
PUSHER_CLUSTER: z.string().optional().default('us2'),
|
|
77
|
+
PUSHER_AUTH_COOKIE_NAME: z.string().optional(),
|
|
78
|
+
REDIS_URL: z.string().url().optional(),
|
|
73
79
|
INFERENCE_API_KEY: z.string().optional(),
|
|
74
80
|
OPENAI_API_KEY: z.string().optional(),
|
|
75
81
|
INFERENCE_API_BASE_URL: z.string().url().optional(),
|
package/src/lib/fileUpload.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { TRPCError } from "@trpc/server";
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
3
|
import { getSignedUrl, objectExists } from "./googleCloudStorage.js";
|
|
4
|
-
import { generateMediaThumbnail } from "./thumbnailGenerator.js";
|
|
5
4
|
import { prisma } from "./prisma.js";
|
|
6
5
|
import { logger } from "../utils/logger.js";
|
|
7
6
|
import { env } from "./config/env.js";
|
|
@@ -81,4 +81,21 @@ export async function objectExists(bucketName: string, objectPath: string): Prom
|
|
|
81
81
|
message: 'Failed to check object existence',
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Copies a file within the same bucket to a new path
|
|
88
|
+
* @param sourcePath The GCS path of the source file
|
|
89
|
+
* @param destPath The GCS path for the destination
|
|
90
|
+
*/
|
|
91
|
+
export async function copyFile(sourcePath: string, destPath: string): Promise<void> {
|
|
92
|
+
try {
|
|
93
|
+
await bucket.file(sourcePath).copy(destPath);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('Error copying file in Google Cloud Storage:', error);
|
|
96
|
+
throw new TRPCError({
|
|
97
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
98
|
+
message: 'Failed to copy file in storage',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
84
101
|
}
|
package/src/lib/pusher.ts
CHANGED
|
@@ -9,4 +9,8 @@ const pusher = new Pusher({
|
|
|
9
9
|
useTLS: env.NODE_ENV !== 'development',
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const chatChannel = (conversationId: string) => `private-conversation-${conversationId}`;
|
|
13
|
+
const worksheetChannel = (worksheetResponseId: string) => `private-worksheet-${worksheetResponseId}`;
|
|
14
|
+
const teacherChannel = (classId: string) => `private-teacher-${classId}`;
|
|
15
|
+
|
|
16
|
+
export { pusher, chatChannel, worksheetChannel, teacherChannel };
|
package/src/lib/redis.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import { env } from './config/env.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
let redis: Redis | null = null;
|
|
6
|
+
|
|
7
|
+
export function getRedis(): Redis | null {
|
|
8
|
+
return redis;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function connectRedis(): Promise<Redis | null> {
|
|
12
|
+
const url = env.REDIS_URL;
|
|
13
|
+
if (!url) {
|
|
14
|
+
logger.info('Redis not configured (REDIS_URL not set), skipping');
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (redis) {
|
|
19
|
+
return redis;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
redis = new Redis(url, {
|
|
23
|
+
maxRetriesPerRequest: 3,
|
|
24
|
+
retryStrategy(times) {
|
|
25
|
+
if (times > 3) return null;
|
|
26
|
+
return Math.min(times * 200, 2000);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
redis.on('error', (err) => {
|
|
31
|
+
logger.error('Redis connection error', { error: err.message });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
redis.on('connect', () => {
|
|
35
|
+
logger.info('Redis connected');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await redis.ping();
|
|
40
|
+
logger.info('Redis ready');
|
|
41
|
+
return redis;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
logger.error('Redis ping failed', { error });
|
|
44
|
+
redis.disconnect();
|
|
45
|
+
redis = null;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function disconnectRedis(): Promise<void> {
|
|
51
|
+
if (redis) {
|
|
52
|
+
await redis.quit();
|
|
53
|
+
redis = null;
|
|
54
|
+
logger.info('Redis disconnected');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,170 +1,172 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
// @deprecated: this is not used anymore
|
|
2
|
+
|
|
3
|
+
// import sharp from 'sharp';
|
|
4
|
+
// import { prisma } from './prisma.js';
|
|
5
|
+
// import { deleteFile, getSignedUrl } from './googleCloudStorage.js';
|
|
6
|
+
|
|
7
|
+
// // Thumbnail size configuration
|
|
8
|
+
// const THUMBNAIL_WIDTH = 200;
|
|
9
|
+
// const THUMBNAIL_HEIGHT = 200;
|
|
10
|
+
|
|
11
|
+
// // File type configurations
|
|
12
|
+
// const SUPPORTED_IMAGE_TYPES = [
|
|
13
|
+
// 'image/jpeg',
|
|
14
|
+
// 'image/png',
|
|
15
|
+
// 'image/gif',
|
|
16
|
+
// 'image/webp',
|
|
17
|
+
// 'image/tiff',
|
|
18
|
+
// 'image/bmp',
|
|
19
|
+
// 'image/avif'
|
|
20
|
+
// ];
|
|
21
|
+
|
|
22
|
+
// const DOCUMENT_TYPES = [
|
|
23
|
+
// 'application/pdf',
|
|
24
|
+
// 'application/msword',
|
|
25
|
+
// 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx
|
|
26
|
+
// 'application/vnd.ms-excel',
|
|
27
|
+
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
|
|
28
|
+
// 'application/vnd.ms-powerpoint',
|
|
29
|
+
// 'application/vnd.openxmlformats-officedocument.presentationml.presentation', // .pptx
|
|
30
|
+
// 'text/plain',
|
|
31
|
+
// 'text/csv',
|
|
32
|
+
// 'application/json',
|
|
33
|
+
// 'text/html',
|
|
34
|
+
// 'text/javascript',
|
|
35
|
+
// 'text/css'
|
|
36
|
+
// ];
|
|
37
|
+
|
|
38
|
+
// const VIDEO_TYPES = [
|
|
39
|
+
// 'video/mp4',
|
|
40
|
+
// 'video/webm',
|
|
41
|
+
// 'video/ogg',
|
|
42
|
+
// 'video/quicktime'
|
|
43
|
+
// ];
|
|
44
|
+
|
|
45
|
+
// const AUDIO_TYPES = [
|
|
46
|
+
// 'audio/mpeg',
|
|
47
|
+
// 'audio/ogg',
|
|
48
|
+
// 'audio/wav',
|
|
49
|
+
// 'audio/webm'
|
|
50
|
+
// ];
|
|
51
|
+
|
|
52
|
+
// /**
|
|
53
|
+
// * Generates a thumbnail for an image or PDF file
|
|
54
|
+
// * @param fileBuffer The file buffer
|
|
55
|
+
// * @param fileType The MIME type of the file
|
|
56
|
+
// * @returns Thumbnail buffer
|
|
57
|
+
// */
|
|
58
|
+
// export async function generateMediaThumbnail(fileBuffer: Buffer, fileType: string): Promise<Buffer> {
|
|
59
|
+
// if (fileType === 'application/pdf') {
|
|
60
|
+
// // For PDFs, we need to use a different approach
|
|
61
|
+
// try {
|
|
62
|
+
// return await sharp(fileBuffer, {
|
|
63
|
+
// density: 300, // Higher density for better quality
|
|
64
|
+
// page: 0 // First page only
|
|
65
|
+
// })
|
|
66
|
+
// .resize(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, {
|
|
67
|
+
// fit: 'inside',
|
|
68
|
+
// withoutEnlargement: true,
|
|
69
|
+
// })
|
|
70
|
+
// .jpeg({ quality: 80 })
|
|
71
|
+
// .toBuffer();
|
|
72
|
+
// } catch (error) {
|
|
73
|
+
// console.warn('Failed to generate PDF thumbnail:', error);
|
|
74
|
+
// return generateGenericThumbnail(fileType);
|
|
75
|
+
// }
|
|
76
|
+
// }
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
async function generateGenericThumbnail(fileType: string): Promise<Buffer> {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
export async function generateThumbnail(fileName: string, fileType: string): Promise<Buffer | null> {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
78
|
+
// // For regular images
|
|
79
|
+
// return sharp(fileBuffer)
|
|
80
|
+
// .resize(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, {
|
|
81
|
+
// fit: 'inside',
|
|
82
|
+
// withoutEnlargement: true,
|
|
83
|
+
// })
|
|
84
|
+
// .jpeg({ quality: 80 })
|
|
85
|
+
// .toBuffer();
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// /**
|
|
89
|
+
// * Generates a generic icon-based thumbnail for a file type
|
|
90
|
+
// * @param fileType The MIME type of the file
|
|
91
|
+
// * @returns Thumbnail buffer
|
|
92
|
+
// */
|
|
93
|
+
// async function generateGenericThumbnail(fileType: string): Promise<Buffer> {
|
|
94
|
+
// // Create a blank canvas with a colored background based on file type
|
|
95
|
+
// const canvas = sharp({
|
|
96
|
+
// create: {
|
|
97
|
+
// width: THUMBNAIL_WIDTH,
|
|
98
|
+
// height: THUMBNAIL_HEIGHT,
|
|
99
|
+
// channels: 4,
|
|
100
|
+
// background: { r: 245, g: 245, b: 245, alpha: 1 }
|
|
101
|
+
// }
|
|
102
|
+
// });
|
|
103
|
+
|
|
104
|
+
// // Add a colored overlay based on file type
|
|
105
|
+
// let color = { r: 200, g: 200, b: 200, alpha: 0.5 }; // Default gray
|
|
106
|
+
|
|
107
|
+
// if (DOCUMENT_TYPES.includes(fileType)) {
|
|
108
|
+
// color = { r: 52, g: 152, b: 219, alpha: 0.5 }; // Blue for documents
|
|
109
|
+
// } else if (VIDEO_TYPES.includes(fileType)) {
|
|
110
|
+
// color = { r: 231, g: 76, b: 60, alpha: 0.5 }; // Red for videos
|
|
111
|
+
// } else if (AUDIO_TYPES.includes(fileType)) {
|
|
112
|
+
// color = { r: 46, g: 204, b: 113, alpha: 0.5 }; // Green for audio
|
|
113
|
+
// }
|
|
114
|
+
|
|
115
|
+
// return canvas
|
|
116
|
+
// .composite([{
|
|
117
|
+
// input: Buffer.from([color.r, color.g, color.b, Math.floor(color.alpha * 255)]),
|
|
118
|
+
// raw: {
|
|
119
|
+
// width: 1,
|
|
120
|
+
// height: 1,
|
|
121
|
+
// channels: 4
|
|
122
|
+
// },
|
|
123
|
+
// tile: true,
|
|
124
|
+
// blend: 'overlay'
|
|
125
|
+
// }])
|
|
126
|
+
// .jpeg({ quality: 80 })
|
|
127
|
+
// .toBuffer();
|
|
128
|
+
// }
|
|
129
|
+
|
|
130
|
+
// /**
|
|
131
|
+
// * Generates a thumbnail for a file
|
|
132
|
+
// * @param fileName The name of the file in Google Cloud Storage
|
|
133
|
+
// * @param fileType The MIME type of the file
|
|
134
|
+
// * @returns The thumbnail buffer or null if thumbnail generation is not supported
|
|
135
|
+
// */
|
|
136
|
+
// export async function generateThumbnail(fileName: string, fileType: string): Promise<Buffer | null> {
|
|
137
|
+
// try {
|
|
138
|
+
// const signedUrl = await getSignedUrl(fileName);
|
|
139
|
+
// const response = await fetch(signedUrl);
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// DEPRECATED: This function is no longer used - thumbnails are generated during direct uploads
|
|
170
|
-
// Thumbnail generation is now handled in the direct upload flow
|
|
141
|
+
// if (!response.ok) {
|
|
142
|
+
// throw new Error(`Failed to download file from storage: ${response.status} ${response.statusText}`);
|
|
143
|
+
// }
|
|
144
|
+
|
|
145
|
+
// const fileBuffer = await response.arrayBuffer();
|
|
146
|
+
|
|
147
|
+
// if (SUPPORTED_IMAGE_TYPES.includes(fileType) || fileType === 'application/pdf') {
|
|
148
|
+
// try {
|
|
149
|
+
// const thumbnail = await generateMediaThumbnail(Buffer.from(fileBuffer), fileType);
|
|
150
|
+
// return thumbnail;
|
|
151
|
+
// } catch (error) {
|
|
152
|
+
// return generateGenericThumbnail(fileType);
|
|
153
|
+
// }
|
|
154
|
+
// } else if ([...DOCUMENT_TYPES, ...VIDEO_TYPES, ...AUDIO_TYPES].includes(fileType)) {
|
|
155
|
+
// return generateGenericThumbnail(fileType);
|
|
156
|
+
// }
|
|
157
|
+
|
|
158
|
+
// return null; // Unsupported file type
|
|
159
|
+
// } catch (error) {
|
|
160
|
+
// return null;
|
|
161
|
+
// }
|
|
162
|
+
// }
|
|
163
|
+
|
|
164
|
+
// /**
|
|
165
|
+
// * Stores a thumbnail in Google Cloud Storage and creates a File entry
|
|
166
|
+
// * @param thumbnailBuffer The thumbnail buffer to store
|
|
167
|
+
// * @param originalFileName The original file name
|
|
168
|
+
// * @param userId The user ID who owns the file
|
|
169
|
+
// * @returns The ID of the created thumbnail File
|
|
170
|
+
// */
|
|
171
|
+
// // DEPRECATED: This function is no longer used - thumbnails are generated during direct uploads
|
|
172
|
+
// // Thumbnail generation is now handled in the direct upload flow
|