@studious-lms/server 1.12.0 → 1.14.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/.dockerignore +28 -0
- package/.env.example +5 -0
- package/.github/workflows/deploy.yml +123 -0
- package/Dockerfile +54 -0
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/config/cors.d.ts.map +1 -1
- package/dist/lib/config/cors.js +31 -11
- package/dist/lib/config/cors.js.map +1 -1
- package/dist/lib/config/env.d.ts +17 -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/googleCloudStorage.d.ts +10 -0
- package/dist/lib/googleCloudStorage.d.ts.map +1 -1
- package/dist/lib/googleCloudStorage.js +22 -2
- package/dist/lib/googleCloudStorage.js.map +1 -1
- package/dist/lib/prisma.d.ts +104 -5
- package/dist/lib/prisma.d.ts.map +1 -1
- package/dist/lib/prisma.js +33 -2
- package/dist/lib/prisma.js.map +1 -1
- package/dist/models/agenda.d.ts +20 -20
- package/dist/models/announcement.d.ts +177 -69
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +551 -353
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/attendance.d.ts +197 -71
- package/dist/models/attendance.d.ts.map +1 -1
- package/dist/models/auth.d.ts +348 -114
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/class.d.ts +324 -188
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/class.js +9 -3
- package/dist/models/class.js.map +1 -1
- package/dist/models/cloudStorage.d.ts +146 -56
- package/dist/models/cloudStorage.d.ts.map +1 -1
- package/dist/models/comment.d.ts +269 -110
- package/dist/models/comment.d.ts.map +1 -1
- package/dist/models/comment.js +3 -2
- package/dist/models/comment.js.map +1 -1
- package/dist/models/conversation.d.ts +206 -63
- package/dist/models/conversation.d.ts.map +1 -1
- package/dist/models/conversation.js +13 -2
- package/dist/models/conversation.js.map +1 -1
- package/dist/models/department.d.ts +196 -70
- package/dist/models/department.d.ts.map +1 -1
- package/dist/models/event.d.ts +287 -125
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/file.d.ts +389 -263
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/folder.d.ts +433 -181
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +209 -83
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/marketing.d.ts +80 -26
- package/dist/models/marketing.d.ts.map +1 -1
- package/dist/models/message.d.ts +95 -41
- package/dist/models/message.d.ts.map +1 -1
- package/dist/models/newtonChat.d.ts +83 -29
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/models/notification.d.ts +61 -25
- package/dist/models/notification.d.ts.map +1 -1
- package/dist/models/school.d.ts +349 -133
- package/dist/models/school.d.ts.map +1 -1
- package/dist/models/section.d.ts +216 -72
- package/dist/models/section.d.ts.map +1 -1
- package/dist/models/tag.d.ts +156 -48
- package/dist/models/tag.d.ts.map +1 -1
- package/dist/models/user.d.ts +82 -28
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/worksheet.d.ts +1023 -410
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/models/worksheet.js +19 -7
- package/dist/models/worksheet.js.map +1 -1
- package/dist/pipelines/aiLabChat.d.ts +0 -8
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +50 -67
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +393 -388
- package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
- package/dist/pipelines/aiLabChatContract.js +63 -69
- package/dist/pipelines/aiLabChatContract.js.map +1 -1
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
- package/dist/pipelines/aiNewtonChat.js +41 -41
- package/dist/pipelines/aiNewtonChat.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts +8 -8
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/pipelines/gradeWorksheet.js +59 -14
- package/dist/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/pipelines/labChatPrompt.d.ts +10 -8
- package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
- package/dist/pipelines/labChatPrompt.js +47 -67
- package/dist/pipelines/labChatPrompt.js.map +1 -1
- package/dist/pipelines/pdfToWorksheet.d.ts +2 -2
- package/dist/routers/_app.d.ts +2268 -1898
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/admin.d.ts +13 -13
- package/dist/routers/agenda.d.ts +18 -18
- package/dist/routers/announcement.d.ts +32 -28
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +6 -5
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +335 -327
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +4 -4
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/attendance.d.ts +10 -10
- package/dist/routers/auth.d.ts +2 -2
- package/dist/routers/class.d.ts +170 -147
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/comment.d.ts +40 -16
- package/dist/routers/comment.d.ts.map +1 -1
- package/dist/routers/comment.js +6 -3
- package/dist/routers/comment.js.map +1 -1
- package/dist/routers/conversation.d.ts +17 -17
- package/dist/routers/department.d.ts +34 -34
- package/dist/routers/discussion.d.ts +65 -53
- package/dist/routers/discussion.d.ts.map +1 -1
- package/dist/routers/discussion.js +6 -2
- package/dist/routers/discussion.js.map +1 -1
- package/dist/routers/embedding.d.ts +69 -0
- package/dist/routers/embedding.d.ts.map +1 -0
- package/dist/routers/embedding.js +143 -0
- package/dist/routers/embedding.js.map +1 -0
- package/dist/routers/event.d.ts +39 -39
- package/dist/routers/file.d.ts +12 -12
- package/dist/routers/folder.d.ts +38 -38
- package/dist/routers/labChat.d.ts +19 -19
- package/dist/routers/marketing.d.ts +2 -2
- package/dist/routers/message.d.ts +13 -13
- package/dist/routers/newtonChat.d.ts +4 -4
- package/dist/routers/notifications.d.ts +9 -9
- package/dist/routers/permissions.d.ts +14 -14
- package/dist/routers/school.d.ts +55 -55
- package/dist/routers/search.d.ts +22 -1
- package/dist/routers/search.d.ts.map +1 -1
- package/dist/routers/search.js +9 -3
- package/dist/routers/search.js.map +1 -1
- package/dist/routers/section.d.ts +4 -4
- package/dist/routers/studentProgress.d.ts +1 -1
- package/dist/routers/tag.d.ts +7 -7
- package/dist/routers/user.d.ts +10 -10
- package/dist/routers/worksheet.d.ts +97 -56
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +28 -2
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/routes/aiStream.d.ts +2 -0
- package/dist/routes/aiStream.d.ts.map +1 -0
- package/dist/routes/aiStream.js +696 -0
- package/dist/routes/aiStream.js.map +1 -0
- package/dist/seedDatabase.d.ts +2 -2
- package/dist/seedDatabase.js +6 -6
- package/dist/seedDatabase.js.map +1 -1
- package/dist/services/adminInstitution.d.ts +8 -8
- package/dist/services/adminRunJob.d.ts +4 -4
- package/dist/services/agenda.d.ts +18 -18
- package/dist/services/aiChatStream.d.ts +68 -0
- package/dist/services/aiChatStream.d.ts.map +1 -0
- package/dist/services/aiChatStream.js +157 -0
- package/dist/services/aiChatStream.js.map +1 -0
- package/dist/services/aiModifierTools.d.ts +32 -0
- package/dist/services/aiModifierTools.d.ts.map +1 -0
- package/dist/services/aiModifierTools.js +165 -0
- package/dist/services/aiModifierTools.js.map +1 -0
- package/dist/services/aiProposalTools.d.ts +32 -0
- package/dist/services/aiProposalTools.d.ts.map +1 -0
- package/dist/services/aiProposalTools.js +207 -0
- package/dist/services/aiProposalTools.js.map +1 -0
- package/dist/services/aiStream.d.ts +22 -0
- package/dist/services/aiStream.d.ts.map +1 -0
- package/dist/services/aiStream.js +142 -0
- package/dist/services/aiStream.js.map +1 -0
- package/dist/services/aiTools.d.ts +16 -0
- package/dist/services/aiTools.d.ts.map +1 -0
- package/dist/services/aiTools.js +271 -0
- package/dist/services/aiTools.js.map +1 -0
- package/dist/services/announcement.d.ts +12 -12
- package/dist/services/announcement.d.ts.map +1 -1
- package/dist/services/announcement.js +7 -2
- package/dist/services/announcement.js.map +1 -1
- package/dist/services/assignment.d.ts +293 -293
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +36 -15
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/attendance.d.ts +10 -10
- package/dist/services/auth.d.ts +2 -2
- package/dist/services/class.d.ts +161 -138
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +25 -4
- package/dist/services/class.js.map +1 -1
- package/dist/services/comment.d.ts +34 -16
- package/dist/services/comment.d.ts.map +1 -1
- package/dist/services/comment.js +23 -2
- package/dist/services/comment.js.map +1 -1
- package/dist/services/conversation.d.ts +17 -17
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/conversation.js +9 -7
- package/dist/services/conversation.js.map +1 -1
- package/dist/services/department.d.ts +33 -33
- package/dist/services/discussion.d.ts +58 -58
- package/dist/services/discussion.d.ts.map +1 -1
- package/dist/services/discussion.js +8 -8
- package/dist/services/discussion.js.map +1 -1
- package/dist/services/discussionSummary.d.ts +21 -0
- package/dist/services/discussionSummary.d.ts.map +1 -0
- package/dist/services/discussionSummary.js +179 -0
- package/dist/services/discussionSummary.js.map +1 -0
- package/dist/services/embedding.d.ts +67 -0
- package/dist/services/embedding.d.ts.map +1 -0
- package/dist/services/embedding.js +226 -0
- package/dist/services/embedding.js.map +1 -0
- package/dist/services/event.d.ts +37 -37
- package/dist/services/file.d.ts +24 -24
- package/dist/services/folder.d.ts +36 -36
- package/dist/services/labChat.d.ts +19 -19
- package/dist/services/marketing.d.ts +2 -2
- package/dist/services/message.d.ts +11 -11
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +2 -2
- package/dist/services/message.js.map +1 -1
- package/dist/services/newtonChat.d.ts +4 -4
- package/dist/services/notification.d.ts +9 -9
- package/dist/services/notification.d.ts.map +1 -1
- package/dist/services/permissions.d.ts +14 -14
- package/dist/services/school.d.ts +50 -50
- package/dist/services/search.d.ts +12 -0
- package/dist/services/search.d.ts.map +1 -1
- package/dist/services/search.js +120 -2
- package/dist/services/search.js.map +1 -1
- package/dist/services/section.d.ts +3 -3
- package/dist/services/tag.d.ts +4 -4
- package/dist/services/user.d.ts +4 -4
- package/dist/services/user.d.ts.map +1 -1
- package/dist/services/user.js +10 -15
- package/dist/services/user.js.map +1 -1
- package/dist/services/worksheet.d.ts +62 -47
- package/dist/services/worksheet.d.ts.map +1 -1
- package/dist/services/worksheet.js +11 -4
- package/dist/services/worksheet.js.map +1 -1
- package/dist/utils/streamBroadcast.d.ts +11 -0
- package/dist/utils/streamBroadcast.d.ts.map +1 -0
- package/dist/utils/streamBroadcast.js +32 -0
- package/dist/utils/streamBroadcast.js.map +1 -0
- package/dist/workers/connection.d.ts +15 -0
- package/dist/workers/connection.d.ts.map +1 -1
- package/dist/workers/connection.js +20 -2
- package/dist/workers/connection.js.map +1 -1
- package/dist/workers/index.js +4 -2
- package/dist/workers/index.js.map +1 -1
- package/dist/workers/processors/createRunJobWorker.d.ts.map +1 -1
- package/dist/workers/processors/createRunJobWorker.js +31 -8
- package/dist/workers/processors/createRunJobWorker.js.map +1 -1
- package/dist/workers/processors/embedContent.d.ts +8 -0
- package/dist/workers/processors/embedContent.d.ts.map +1 -0
- package/dist/workers/processors/embedContent.js +76 -0
- package/dist/workers/processors/embedContent.js.map +1 -0
- package/dist/workers/queues.d.ts +6 -0
- package/dist/workers/queues.d.ts.map +1 -1
- package/dist/workers/queues.js +5 -3
- package/dist/workers/queues.js.map +1 -1
- package/package.json +5 -1
- package/prisma/migrations/20260609215135_init/migration.sql +1412 -0
- package/prisma/migrations/20260610130000_add_worksheet_tags/migration.sql +13 -0
- package/prisma/schema.prisma +39 -7
- package/src/index.ts +4 -0
- package/src/lib/config/cors.ts +32 -9
- package/src/lib/config/env.ts +6 -0
- package/src/lib/googleCloudStorage.ts +21 -0
- package/src/lib/prisma.ts +40 -1
- package/src/models/class.ts +10 -3
- package/src/models/comment.ts +1 -0
- package/src/models/conversation.ts +12 -0
- package/src/models/worksheet.ts +16 -7
- package/src/pipelines/aiLabChat.ts +54 -79
- package/src/pipelines/aiLabChatContract.ts +69 -69
- package/src/pipelines/aiNewtonChat.ts +46 -43
- package/src/pipelines/gradeWorksheet.ts +66 -15
- package/src/pipelines/labChatPrompt.ts +63 -88
- package/src/routers/_app.ts +2 -0
- package/src/routers/announcement.ts +4 -3
- package/src/routers/assignment.ts +2 -2
- package/src/routers/comment.ts +5 -0
- package/src/routers/discussion.ts +7 -0
- package/src/routers/embedding.ts +159 -0
- package/src/routers/search.ts +10 -1
- package/src/routers/worksheet.ts +29 -0
- package/src/routes/aiStream.ts +789 -0
- package/src/seedDatabase.ts +4 -4
- package/src/services/aiChatStream.ts +219 -0
- package/src/services/aiModifierTools.ts +188 -0
- package/src/services/aiProposalTools.ts +272 -0
- package/src/services/aiStream.ts +175 -0
- package/src/services/aiTools.ts +290 -0
- package/src/services/announcement.ts +7 -0
- package/src/services/assignment.ts +36 -14
- package/src/services/class.ts +30 -2
- package/src/services/comment.ts +22 -0
- package/src/services/conversation.ts +9 -4
- package/src/services/discussion.ts +9 -8
- package/src/services/discussionSummary.ts +226 -0
- package/src/services/embedding.ts +285 -0
- package/src/services/message.ts +2 -2
- package/src/services/search.ts +138 -0
- package/src/services/user.ts +9 -17
- package/src/services/worksheet.ts +10 -2
- package/src/utils/streamBroadcast.ts +48 -0
- package/src/workers/connection.ts +19 -0
- package/src/workers/index.ts +2 -0
- package/src/workers/processors/createRunJobWorker.ts +32 -6
- package/src/workers/processors/embedContent.ts +90 -0
- package/src/workers/queues.ts +5 -1
- package/tests/integration/routers/conversation.test.ts +9 -7
- package/tests/unit/pipelines/aiLabChatContract.test.ts +38 -21
- package/tests/unit/pipelines/labChatPrompt.test.ts +23 -36
- package/prisma/migrations/20250807062924_init/migration.sql +0 -436
- package/prisma/migrations/20250919063704_init2/migration.sql +0 -24
- package/prisma/migrations/20250920143543_add_profile_fields/migration.sql +0 -15
- package/prisma/migrations/20250922165147_add_chat_models/migration.sql +0 -68
- package/prisma/migrations/20250922165314_add_chat_roles/migration.sql +0 -5
- package/prisma/migrations/20250922171242_refactor_notification_system/migration.sql +0 -20
- package/prisma/migrations/20250922172208_add_mentions_system/migration.sql +0 -21
- package/prisma/migrations/20250925072732_add_lab_chat_system/migration.sql +0 -25
- package/prisma/migrations/20251020151505_add_upload_tracking_fields/migration.sql +0 -57
- package/prisma/migrations/20251109122857_annuoncements_comments/migration.sql +0 -30
- package/prisma/migrations/20251109135555_reactions_announcements_comments/migration.sql +0 -35
- package/prisma/migrations/20260410124000_add_submission_recommendation_state/migration.sql +0 -14
- package/prisma/migrations/20260415103500_add_session_type/migration.sql +0 -33
- package/prisma/migrations/20260415133000_add_ai_usage_logs/migration.sql +0 -19
- package/prisma/migrations/20260417083000_add_conversation_member_hidden_at/migration.sql +0 -2
- package/prisma/migrations/20260502230500_assignment_submission_comments/migration.sql +0 -60
- package/prisma/migrations/20260504071500_school_memberships/migration.sql +0 -81
- package/prisma/migrations/20260506162500_consolidate_run_job_and_unify_job_status/migration.sql +0 -166
- package/prisma/migrations/20260509220000_institution_overhaul/migration.sql +0 -184
- package/prisma/migrations/20260510170000_add_folder_permissions/migration.sql +0 -93
- package/prisma/migrations/20260510183000_add_discussions/migration.sql +0 -151
- /package/.github/workflows/{npm.yml → npm-publish.yml} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discussion.js","sources":["services/discussion.ts"],"sourceRoot":"/","sourcesContent":["import type { DiscussionType, Prisma, ReactionType } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { discussionChannel, pusher } from \"../lib/pusher.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { classMemberOrSchoolAdminWhere, classTeacherOrSchoolAdminWhere } from \"../models/access.js\";\nimport { sendNotification } from \"./notification.js\";\n\nconst authorSelect = {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n} as const;\n\nconst attachmentSelect = {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadedAt: true,\n uploadStatus: true,\n thumbnailId: true,\n thumbnail: { select: { id: true, path: true } },\n} as const;\n\nconst reactionCounts = {\n THUMBSUP: 0,\n CELEBRATE: 0,\n CARE: 0,\n HEART: 0,\n IDEA: 0,\n HAPPY: 0,\n} satisfies Record<ReactionType, number>;\n\nconst discussionInclude = {\n author: { select: authorSelect },\n attachments: { select: attachmentSelect },\n tags: { select: { id: true, name: true, color: true } },\n reactions: { select: { type: true, userId: true } },\n _count: { select: { replies: { where: { deletedAt: null } } } },\n} as const;\n\nconst replyInclude = {\n author: { select: authorSelect },\n attachments: { select: attachmentSelect },\n reactions: { select: { type: true, userId: true } },\n} as const;\n\nconst unclaimedAttachmentWhere = {\n assignmentId: null,\n submissionId: null,\n annotationId: null,\n classDraftId: null,\n folderId: null,\n messageId: null,\n announcementId: null,\n discussionId: null,\n discussionReplyId: null,\n} satisfies Prisma.FileWhereInput;\n\ntype DiscussionWithInclude = Prisma.DiscussionGetPayload<{ include: typeof discussionInclude }>;\ntype ReplyWithInclude = Prisma.DiscussionReplyGetPayload<{ include: typeof replyInclude }>;\n\nexport type DiscussionReplyTree = ReturnType<typeof serializeReply> & {\n children: DiscussionReplyTree[];\n};\n\nfunction summarizeReactions(reactions: { type: ReactionType; userId: string }[], userId: string) {\n const counts = { ...reactionCounts };\n for (const reaction of reactions) {\n counts[reaction.type] += 1;\n }\n return {\n counts,\n userReaction: reactions.find((reaction) => reaction.userId === userId)?.type ?? null,\n total: reactions.length,\n };\n}\n\nfunction serializeDiscussion(discussion: DiscussionWithInclude, userId: string, unreadCount = 0) {\n return {\n id: discussion.id,\n classId: discussion.classId,\n authorId: discussion.authorId,\n title: discussion.title,\n body: discussion.body,\n type: discussion.type,\n isPinned: discussion.isPinned,\n isLocked: discussion.isLocked,\n allowReplies: discussion.allowReplies,\n createdAt: discussion.createdAt,\n updatedAt: discussion.updatedAt,\n lastActivityAt: discussion.lastActivityAt,\n author: discussion.author,\n attachments: discussion.attachments,\n tags: discussion.tags,\n replyCount: discussion._count.replies,\n hasAcceptedAnswer: false,\n reactions: summarizeReactions(discussion.reactions, userId),\n unreadCount,\n };\n}\n\nfunction serializeReply(reply: ReplyWithInclude, userId: string) {\n return {\n id: reply.id,\n discussionId: reply.discussionId,\n authorId: reply.authorId,\n parentId: reply.parentId,\n body: reply.deletedAt ? \"[deleted]\" : reply.body,\n deletedAt: reply.deletedAt,\n isAcceptedAnswer: reply.isAcceptedAnswer,\n createdAt: reply.createdAt,\n modifiedAt: reply.modifiedAt,\n author: reply.author,\n attachments: reply.deletedAt ? [] : reply.attachments,\n reactions: summarizeReactions(reply.reactions, userId),\n };\n}\n\nexport function buildReplyTree(replies: ReplyWithInclude[], userId: string): DiscussionReplyTree[] {\n const byId = new Map<string, DiscussionReplyTree>();\n const roots: DiscussionReplyTree[] = [];\n\n for (const reply of replies) {\n byId.set(reply.id, { ...serializeReply(reply, userId), children: [] });\n }\n\n for (const reply of replies) {\n const node = byId.get(reply.id)!;\n if (reply.parentId && byId.has(reply.parentId)) {\n byId.get(reply.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n\nfunction isWithinAuthorEditWindow(createdAt: Date) {\n return Date.now() - createdAt.getTime() <= 24 * 60 * 60 * 1000;\n}\n\nexport async function getDiscussionClassAccess(classId: string, userId: string) {\n const classData = await prisma.class.findFirst({\n where: { id: classId, ...classMemberOrSchoolAdminWhere(userId) },\n select: {\n id: true,\n name: true,\n discussionsEnabled: true,\n studentsCanCreateDiscussions: true,\n studentsCanReply: true,\n students: { where: { id: userId }, select: { id: true } },\n teachers: { where: { id: userId }, select: { id: true } },\n school: {\n select: {\n memberships: {\n where: { userId, role: \"ADMIN\", status: \"ACTIVE\" },\n select: { userId: true },\n },\n },\n },\n },\n });\n\n if (!classData) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a member in this class\" });\n }\n if (!classData.discussionsEnabled) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Discussions are disabled for this class\" });\n }\n\n const isTeacher = classData.teachers.length > 0 || Boolean(classData.school?.memberships.length);\n const isStudent = classData.students.length > 0;\n\n return { ...classData, isTeacher, isStudent };\n}\n\nasync function getDiscussionForUser(discussionId: string, userId: string) {\n const discussion = await prisma.discussion.findUnique({\n where: { id: discussionId },\n include: {\n class: {\n select: {\n id: true,\n discussionsEnabled: true,\n studentsCanReply: true,\n teachers: { where: { id: userId }, select: { id: true } },\n students: { where: { id: userId }, select: { id: true } },\n school: {\n select: {\n memberships: {\n where: { userId, role: \"ADMIN\", status: \"ACTIVE\" },\n select: { userId: true },\n },\n },\n },\n },\n },\n },\n });\n\n if (!discussion) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Discussion not found\" });\n }\n\n const isTeacher = discussion.class.teachers.length > 0 || Boolean(discussion.class.school?.memberships.length);\n const isStudent = discussion.class.students.length > 0;\n if (!isTeacher && !isStudent) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a member in this class\" });\n }\n if (!discussion.class.discussionsEnabled) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Discussions are disabled for this class\" });\n }\n\n return { discussion, isTeacher, isStudent };\n}\n\nasync function claimDiscussionAttachments(\n tx: Prisma.TransactionClient,\n userId: string,\n discussionId: string,\n attachmentIds: string[] = []\n) {\n const uniqueIds = [...new Set(attachmentIds)];\n if (!uniqueIds.length) return;\n const { count } = await tx.file.updateMany({\n where: { id: { in: uniqueIds }, userId, uploadStatus: \"COMPLETED\", ...unclaimedAttachmentWhere },\n data: { discussionId },\n });\n if (count !== uniqueIds.length) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Some attachments are missing, unfinished, or unavailable\" });\n }\n}\n\nasync function claimReplyAttachments(\n tx: Prisma.TransactionClient,\n userId: string,\n replyId: string,\n attachmentIds: string[] = []\n) {\n const uniqueIds = [...new Set(attachmentIds)];\n if (!uniqueIds.length) return;\n const { count } = await tx.file.updateMany({\n where: { id: { in: uniqueIds }, userId, uploadStatus: \"COMPLETED\", ...unclaimedAttachmentWhere },\n data: { discussionReplyId: replyId },\n });\n if (count !== uniqueIds.length) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Some attachments are missing, unfinished, or unavailable\" });\n }\n}\n\nfunction triggerDiscussionEvent(classId: string, event: string, payload: unknown) {\n pusher.trigger(discussionChannel(classId), event, payload).catch((error) => {\n logger.error(\"Failed to trigger discussion pusher event\", { event, classId, error });\n });\n}\n\nasync function notify(receiverId: string | null | undefined, senderId: string, title: string, content: string) {\n if (!receiverId || receiverId === senderId) return;\n await sendNotification({ receiverId, title, content }).catch((error) => {\n logger.error(\"Failed to send discussion notification\", { receiverId, error });\n });\n}\n\nasync function notifyMentionedUsers(classId: string, senderId: string, body: string, link: string) {\n const usernames = [...new Set(Array.from(body.matchAll(/(^|\\s)@([a-zA-Z0-9_.-]{2,32})/g)).map((match) => match[2]))];\n if (!usernames.length) return;\n const members = await prisma.user.findMany({\n where: {\n username: { in: usernames },\n OR: [\n { teacherIn: { some: { id: classId } } },\n { studentIn: { some: { id: classId } } },\n { schoolMemberships: { some: { status: \"ACTIVE\", school: { classes: { some: { id: classId } } } } } },\n ],\n },\n select: { id: true },\n });\n await Promise.all(members.map((member) => notify(member.id, senderId, \"You were mentioned in a discussion\", link)));\n}\n\nasync function unreadCountsForDiscussions(userId: string, discussionIds: string[]) {\n if (!discussionIds.length) return new Map<string, number>();\n const reads = await prisma.discussionRead.findMany({\n where: { userId, discussionId: { in: discussionIds } },\n select: { discussionId: true, lastReadAt: true },\n });\n const readMap = new Map(reads.map((read) => [read.discussionId, read.lastReadAt]));\n const neverRead = discussionIds.filter((discussionId) => !readMap.has(discussionId));\n const readPredicates = Array.from(readMap.entries()).map(([discussionId, lastReadAt]) => ({\n discussionId,\n createdAt: { gt: lastReadAt },\n }));\n const counts = new Map<string, number>();\n\n const [neverReadCounts, readCounts] = await Promise.all([\n neverRead.length\n ? prisma.discussionReply.groupBy({\n by: [\"discussionId\"],\n where: { discussionId: { in: neverRead }, deletedAt: null, authorId: { not: userId } },\n _count: true,\n })\n : Promise.resolve([]),\n readPredicates.length\n ? prisma.discussionReply.groupBy({\n by: [\"discussionId\"],\n where: { deletedAt: null, authorId: { not: userId }, OR: readPredicates },\n _count: true,\n })\n : Promise.resolve([]),\n ]);\n\n for (const item of [...neverReadCounts, ...readCounts]) {\n counts.set(item.discussionId, item._count);\n }\n return counts;\n}\n\nexport async function listDiscussions(userId: string, input: {\n classId: string;\n tagId?: string;\n type?: DiscussionType;\n search?: string;\n sort: \"recent\" | \"activity\" | \"top\";\n cursor?: string;\n limit: number;\n}) {\n await getDiscussionClassAccess(input.classId, userId);\n const where: Prisma.DiscussionWhereInput = {\n classId: input.classId,\n ...(input.tagId ? { tags: { some: { id: input.tagId } } } : {}),\n ...(input.type ? { type: input.type } : {}),\n ...(input.search?.trim()\n ? {\n OR: [\n { title: { contains: input.search.trim(), mode: \"insensitive\" } },\n { body: { contains: input.search.trim(), mode: \"insensitive\" } },\n ],\n }\n : {}),\n };\n const orderBy: Prisma.DiscussionOrderByWithRelationInput[] =\n input.sort === \"recent\"\n ? [{ isPinned: \"desc\" }, { createdAt: \"desc\" }]\n : input.sort === \"top\"\n ? [{ isPinned: \"desc\" }, { replies: { _count: \"desc\" } }, { lastActivityAt: \"desc\" }]\n : [{ isPinned: \"desc\" }, { lastActivityAt: \"desc\" }];\n\n const items = await prisma.discussion.findMany({\n where,\n include: discussionInclude,\n orderBy,\n cursor: input.cursor ? { id: input.cursor } : undefined,\n skip: input.cursor ? 1 : 0,\n take: input.limit + 1,\n });\n const next = items.length > input.limit ? items.pop() : undefined;\n const unreadMap = await unreadCountsForDiscussions(userId, items.map((item) => item.id));\n const accepted = await prisma.discussionReply.groupBy({\n by: [\"discussionId\"],\n where: { discussionId: { in: items.map((item) => item.id) }, isAcceptedAnswer: true, deletedAt: null },\n });\n const acceptedIds = new Set(accepted.map((item) => item.discussionId));\n\n return {\n items: items.map((item) => ({\n ...serializeDiscussion(item, userId, unreadMap.get(item.id) ?? 0),\n hasAcceptedAnswer: acceptedIds.has(item.id),\n })),\n nextCursor: next?.id,\n };\n}\n\nexport async function getDiscussion(userId: string, discussionId: string) {\n const { discussion: accessDiscussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n const [discussion, replies, myReaction] = await Promise.all([\n prisma.discussion.findUniqueOrThrow({ where: { id: discussionId }, include: discussionInclude }),\n prisma.discussionReply.findMany({ where: { discussionId }, include: replyInclude, orderBy: { createdAt: \"asc\" } }),\n prisma.reaction.findUnique({ where: { userId_discussionId: { userId, discussionId } }, select: { type: true } }),\n prisma.discussionRead.upsert({\n where: { userId_discussionId: { userId, discussionId } },\n update: { lastReadAt: new Date() },\n create: { userId, discussionId },\n }),\n ]);\n const canAuthorEdit = accessDiscussion.authorId === userId && isWithinAuthorEditWindow(accessDiscussion.createdAt);\n const canReply = isTeacher || (accessDiscussion.class.studentsCanReply && !accessDiscussion.isLocked && accessDiscussion.allowReplies);\n return {\n discussion: serializeDiscussion(discussion, userId),\n replies: buildReplyTree(replies, userId),\n myReaction: myReaction?.type ?? null,\n canEdit: isTeacher || canAuthorEdit,\n canPin: isTeacher,\n canLock: isTeacher,\n canDelete: isTeacher || accessDiscussion.authorId === userId,\n canReply,\n };\n}\n\nexport async function createDiscussion(userId: string, input: {\n classId: string;\n title: string;\n body: string;\n type: DiscussionType;\n tagIds?: string[];\n attachmentIds?: string[];\n}) {\n const classData = await getDiscussionClassAccess(input.classId, userId);\n if (!classData.isTeacher && !classData.studentsCanCreateDiscussions) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Students cannot create discussions in this class\" });\n }\n if (input.type === \"ANNOUNCEMENT\" && !classData.isTeacher) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can create announcement discussions\" });\n }\n\n const discussion = await prisma.$transaction(async (tx) => {\n const created = await tx.discussion.create({\n data: {\n classId: input.classId,\n authorId: userId,\n title: input.title.trim(),\n body: input.body.trim(),\n type: input.type,\n allowReplies: input.type !== \"ANNOUNCEMENT\",\n tags: input.tagIds?.length ? { connect: input.tagIds.map((id) => ({ id })) } : undefined,\n },\n });\n await claimDiscussionAttachments(tx, userId, created.id, input.attachmentIds);\n return tx.discussion.findUniqueOrThrow({ where: { id: created.id }, include: discussionInclude });\n });\n\n triggerDiscussionEvent(input.classId, \"discussion-created\", { discussion: serializeDiscussion(discussion, userId) });\n await notifyMentionedUsers(input.classId, userId, input.body, `/class/${input.classId}/discussions/${discussion.id}`);\n return { discussion: serializeDiscussion(discussion, userId) };\n}\n\nexport async function updateDiscussion(userId: string, input: {\n discussionId: string;\n title?: string;\n body?: string;\n type?: DiscussionType;\n tagIds?: string[];\n allowReplies?: boolean;\n}) {\n const { discussion, isTeacher } = await getDiscussionForUser(input.discussionId, userId);\n if (!isTeacher && !(discussion.authorId === userId && isWithinAuthorEditWindow(discussion.createdAt))) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot edit this discussion\" });\n }\n if (input.type === \"ANNOUNCEMENT\" && !isTeacher) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can create announcement discussions\" });\n }\n const updated = await prisma.discussion.update({\n where: { id: input.discussionId },\n data: {\n ...(input.title !== undefined ? { title: input.title.trim() } : {}),\n ...(input.body !== undefined ? { body: input.body.trim() } : {}),\n ...(input.type !== undefined ? { type: input.type } : {}),\n ...(input.allowReplies !== undefined ? { allowReplies: input.allowReplies } : {}),\n ...(input.tagIds ? { tags: { set: input.tagIds.map((id) => ({ id })) } } : {}),\n },\n include: discussionInclude,\n });\n triggerDiscussionEvent(updated.classId, \"discussion-updated\", { discussion: serializeDiscussion(updated, userId) });\n return { discussion: serializeDiscussion(updated, userId) };\n}\n\nexport async function deleteDiscussion(userId: string, discussionId: string) {\n const { discussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n if (!isTeacher && discussion.authorId !== userId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot delete this discussion\" });\n }\n await prisma.discussion.delete({ where: { id: discussionId } });\n triggerDiscussionEvent(discussion.classId, \"discussion-deleted\", { discussionId });\n return { success: true };\n}\n\nexport async function replyToDiscussion(userId: string, input: {\n discussionId: string;\n parentReplyId?: string;\n body: string;\n attachmentIds?: string[];\n}) {\n const { discussion, isTeacher } = await getDiscussionForUser(input.discussionId, userId);\n const canReply = isTeacher || (discussion.class.studentsCanReply && !discussion.isLocked && discussion.allowReplies);\n if (!canReply) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot reply to this discussion\" });\n }\n let parent: { id: string; authorId: string } | null = null;\n if (input.parentReplyId) {\n parent = await prisma.discussionReply.findFirst({\n where: { id: input.parentReplyId, discussionId: input.discussionId },\n select: { id: true, authorId: true },\n });\n if (!parent) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Parent reply not found\" });\n }\n const reply = await prisma.$transaction(async (tx) => {\n const created = await tx.discussionReply.create({\n data: {\n discussionId: input.discussionId,\n authorId: userId,\n parentId: input.parentReplyId,\n body: input.body.trim(),\n },\n });\n await claimReplyAttachments(tx, userId, created.id, input.attachmentIds);\n await tx.discussion.update({\n where: { id: input.discussionId },\n data: { lastActivityAt: new Date() },\n });\n return tx.discussionReply.findUniqueOrThrow({ where: { id: created.id }, include: replyInclude });\n });\n const link = `/class/${discussion.classId}/discussions/${discussion.id}#${reply.id}`;\n await Promise.all([\n notify(discussion.authorId, userId, \"New reply to your discussion\", link),\n notify(parent?.authorId, userId, \"New reply in a discussion thread\", link),\n notifyMentionedUsers(discussion.classId, userId, input.body, link),\n ]);\n triggerDiscussionEvent(discussion.classId, \"reply-created\", { discussionId: discussion.id, reply: serializeReply(reply, userId) });\n return { reply: serializeReply(reply, userId) };\n}\n\nexport async function updateDiscussionReply(userId: string, input: { replyId: string; body: string; attachmentIds?: string[] }) {\n const existing = await prisma.discussionReply.findUnique({ where: { id: input.replyId }, include: { discussion: true } });\n if (!existing) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n const { isTeacher } = await getDiscussionForUser(existing.discussionId, userId);\n if (!isTeacher && !(existing.authorId === userId && isWithinAuthorEditWindow(existing.createdAt))) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot edit this reply\" });\n }\n const reply = await prisma.$transaction(async (tx) => {\n await tx.file.updateMany({ where: { discussionReplyId: input.replyId }, data: { discussionReplyId: null } });\n await claimReplyAttachments(tx, userId, input.replyId, input.attachmentIds);\n return tx.discussionReply.update({\n where: { id: input.replyId },\n data: { body: input.body.trim(), deletedAt: null },\n include: replyInclude,\n });\n });\n triggerDiscussionEvent(existing.discussion.classId, \"reply-updated\", { discussionId: existing.discussionId, reply: serializeReply(reply, userId) });\n return { reply: serializeReply(reply, userId) };\n}\n\nexport async function deleteDiscussionReply(userId: string, replyId: string) {\n const existing = await prisma.discussionReply.findUnique({ where: { id: replyId }, include: { discussion: true } });\n if (!existing) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n const { isTeacher } = await getDiscussionForUser(existing.discussionId, userId);\n if (!isTeacher && existing.authorId !== userId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot delete this reply\" });\n }\n const reply = await prisma.discussionReply.update({\n where: { id: replyId },\n data: { body: \"[deleted]\", deletedAt: new Date(), isAcceptedAnswer: false },\n include: replyInclude,\n });\n triggerDiscussionEvent(existing.discussion.classId, \"reply-deleted\", { discussionId: existing.discussionId, reply: serializeReply(reply, userId) });\n return { reply: serializeReply(reply, userId) };\n}\n\nexport async function pinDiscussion(userId: string, discussionId: string, isPinned: boolean) {\n const { discussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n if (!isTeacher) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can pin discussions\" });\n const updated = await prisma.discussion.update({ where: { id: discussionId }, data: { isPinned }, include: discussionInclude });\n await notify(discussion.authorId, userId, isPinned ? \"Your discussion was pinned\" : \"Your discussion was unpinned\", `/class/${discussion.classId}/discussions/${discussionId}`);\n triggerDiscussionEvent(discussion.classId, \"discussion-updated\", { discussion: serializeDiscussion(updated, userId) });\n return { discussion: serializeDiscussion(updated, userId) };\n}\n\nexport async function lockDiscussion(userId: string, discussionId: string, isLocked: boolean) {\n const { discussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n if (!isTeacher) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can lock discussions\" });\n const updated = await prisma.discussion.update({ where: { id: discussionId }, data: { isLocked }, include: discussionInclude });\n triggerDiscussionEvent(discussion.classId, \"discussion-updated\", { discussion: serializeDiscussion(updated, userId) });\n return { discussion: serializeDiscussion(updated, userId) };\n}\n\nexport async function markAcceptedAnswer(userId: string, replyId: string, accepted: boolean) {\n const reply = await prisma.discussionReply.findUnique({ where: { id: replyId }, include: { discussion: true } });\n if (!reply) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n const { isTeacher } = await getDiscussionForUser(reply.discussionId, userId);\n if (reply.discussion.type !== \"QUESTION\") {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Only question discussions can have accepted answers\" });\n }\n if (!isTeacher && reply.discussion.authorId !== userId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only the discussion author or a teacher can mark an accepted answer\" });\n }\n const updated = await prisma.$transaction(async (tx) => {\n if (accepted) {\n await tx.discussionReply.updateMany({ where: { discussionId: reply.discussionId, isAcceptedAnswer: true }, data: { isAcceptedAnswer: false } });\n }\n return tx.discussionReply.update({ where: { id: replyId }, data: { isAcceptedAnswer: accepted }, include: replyInclude });\n });\n await notify(reply.authorId, userId, accepted ? \"Your answer was accepted\" : \"Your answer was unmarked\", `/class/${reply.discussion.classId}/discussions/${reply.discussionId}#${replyId}`);\n triggerDiscussionEvent(reply.discussion.classId, \"reply-updated\", { discussionId: reply.discussionId, reply: serializeReply(updated, userId) });\n return { reply: serializeReply(updated, userId) };\n}\n\nexport async function markDiscussionRead(userId: string, discussionId: string) {\n await getDiscussionForUser(discussionId, userId);\n return prisma.discussionRead.upsert({\n where: { userId_discussionId: { userId, discussionId } },\n update: { lastReadAt: new Date() },\n create: { userId, discussionId },\n });\n}\n\nexport async function getUnreadCounts(userId: string, classIds: string[]) {\n const allowedClasses = await prisma.class.findMany({\n where: { id: { in: classIds }, discussionsEnabled: true, ...classMemberOrSchoolAdminWhere(userId) },\n select: { id: true },\n });\n const result: Record<string, { total: number; perDiscussion: { discussionId: string; count: number }[] }> = {};\n for (const cls of allowedClasses) {\n const discussions = await prisma.discussion.findMany({\n where: { classId: cls.id },\n select: { id: true },\n });\n const countMap = await unreadCountsForDiscussions(userId, discussions.map((discussion) => discussion.id));\n const perDiscussion = Array.from(countMap.entries())\n .map(([discussionId, count]) => ({ discussionId, count }))\n .filter((item) => item.count > 0);\n result[cls.id] = {\n total: perDiscussion.reduce((sum, item) => sum + item.count, 0),\n perDiscussion,\n };\n }\n return result;\n}\n\nexport async function toggleDiscussionReaction(userId: string, input: { discussionId?: string; discussionReplyId?: string; type: ReactionType }) {\n if (!input.discussionId && !input.discussionReplyId) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"A discussion or reply is required\" });\n }\n if (input.discussionId && input.discussionReplyId) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"React to either a discussion or a reply\" });\n }\n const target = input.discussionId\n ? await getDiscussionForUser(input.discussionId, userId).then(({ discussion }) => ({ classId: discussion.classId, discussionId: discussion.id }))\n : await prisma.discussionReply.findUnique({ where: { id: input.discussionReplyId }, include: { discussion: true } }).then(async (reply) => {\n if (!reply) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n await getDiscussionForUser(reply.discussionId, userId);\n return { classId: reply.discussion.classId, discussionId: reply.discussionId };\n });\n const where = input.discussionId\n ? { userId_discussionId: { userId, discussionId: input.discussionId } }\n : { userId_discussionReplyId: { userId, discussionReplyId: input.discussionReplyId! } };\n const existing = await prisma.reaction.findUnique({ where });\n if (existing?.type === input.type) {\n await prisma.reaction.delete({ where: { id: existing.id } });\n triggerDiscussionEvent(target.classId, \"reaction-removed\", { discussionId: target.discussionId, discussionReplyId: input.discussionReplyId, userId });\n return { reaction: null };\n }\n const reaction = input.discussionId\n ? await prisma.reaction.upsert({\n where: { userId_discussionId: { userId, discussionId: input.discussionId } },\n update: { type: input.type },\n create: { userId, discussionId: input.discussionId, type: input.type },\n })\n : await prisma.reaction.upsert({\n where: { userId_discussionReplyId: { userId, discussionReplyId: input.discussionReplyId! } },\n update: { type: input.type },\n create: { userId, discussionReplyId: input.discussionReplyId!, type: input.type },\n });\n triggerDiscussionEvent(target.classId, \"reaction-added\", { discussionId: target.discussionId, discussionReplyId: input.discussionReplyId, reaction });\n return { reaction };\n}\n\nexport async function listDiscussionTags(userId: string, classId: string) {\n await getDiscussionClassAccess(classId, userId);\n return prisma.discussionTag.findMany({ where: { classId }, orderBy: { name: \"asc\" } });\n}\n\nexport async function createDiscussionTag(userId: string, input: { classId: string; name: string; color: string }) {\n const isTeacher = await prisma.class.findFirst({ where: { id: input.classId, ...classTeacherOrSchoolAdminWhere(userId) }, select: { id: true, discussionsEnabled: true } });\n if (!isTeacher?.discussionsEnabled) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can manage discussion tags\" });\n return prisma.discussionTag.create({ data: { classId: input.classId, name: input.name.trim(), color: input.color } });\n}\n\nexport async function updateDiscussionTag(userId: string, input: { tagId: string; name?: string; color?: string }) {\n const tag = await prisma.discussionTag.findUnique({ where: { id: input.tagId }, select: { classId: true } });\n if (!tag) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Tag not found\" });\n const isTeacher = await prisma.class.findFirst({ where: { id: tag.classId, ...classTeacherOrSchoolAdminWhere(userId) }, select: { id: true, discussionsEnabled: true } });\n if (!isTeacher?.discussionsEnabled) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can manage discussion tags\" });\n return prisma.discussionTag.update({ where: { id: input.tagId }, data: { name: input.name?.trim(), color: input.color } });\n}\n\nexport async function deleteDiscussionTag(userId: string, tagId: string) {\n const tag = await prisma.discussionTag.findUnique({ where: { id: tagId }, select: { classId: true } });\n if (!tag) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Tag not found\" });\n const isTeacher = await prisma.class.findFirst({ where: { id: tag.classId, ...classTeacherOrSchoolAdminWhere(userId) }, select: { id: true, discussionsEnabled: true } });\n if (!isTeacher?.discussionsEnabled) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can manage discussion tags\" });\n await prisma.discussionTag.delete({ where: { id: tagId } });\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"b10e68f8-0106-57a7-be41-a32938701565"}
|
|
1
|
+
{"version":3,"file":"discussion.js","sources":["services/discussion.ts"],"sourceRoot":"/","sourcesContent":["import type { DiscussionType, Prisma, ReactionType } from \"@prisma/client\";\nimport type { AppTransactionClient } from \"../lib/prisma.js\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { discussionChannel, pusher } from \"../lib/pusher.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { classMemberOrSchoolAdminWhere, classTeacherOrSchoolAdminWhere } from \"../models/access.js\";\nimport { sendNotification } from \"./notification.js\";\n\nconst authorSelect = {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n} as const;\n\nconst attachmentSelect = {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadedAt: true,\n uploadStatus: true,\n thumbnailId: true,\n thumbnail: { select: { id: true, path: true } },\n} as const;\n\nconst reactionCounts = {\n THUMBSUP: 0,\n CELEBRATE: 0,\n CARE: 0,\n HEART: 0,\n IDEA: 0,\n HAPPY: 0,\n} satisfies Record<ReactionType, number>;\n\nconst discussionInclude = {\n author: { select: authorSelect },\n attachments: { select: attachmentSelect },\n tags: { select: { id: true, name: true, color: true } },\n reactions: { select: { type: true, userId: true } },\n _count: { select: { replies: { where: { deletedAt: null } } } },\n} as const;\n\nconst replyInclude = {\n author: { select: authorSelect },\n attachments: { select: attachmentSelect },\n reactions: { select: { type: true, userId: true } },\n} as const;\n\nconst unclaimedAttachmentWhere = {\n assignmentId: null,\n submissionId: null,\n annotationId: null,\n classDraftId: null,\n folderId: null,\n messageId: null,\n announcementId: null,\n discussionId: null,\n discussionReplyId: null,\n} satisfies Prisma.FileWhereInput;\n\ntype DiscussionWithInclude = Prisma.DiscussionGetPayload<{ include: typeof discussionInclude }>;\ntype ReplyWithInclude = Prisma.DiscussionReplyGetPayload<{ include: typeof replyInclude }>;\n\nexport type DiscussionReplyTree = ReturnType<typeof serializeReply> & {\n children: DiscussionReplyTree[];\n};\n\nfunction summarizeReactions(reactions: { type: ReactionType; userId: string }[], userId: string) {\n const counts = { ...reactionCounts };\n for (const reaction of reactions) {\n counts[reaction.type] += 1;\n }\n return {\n counts,\n userReaction: reactions.find((reaction) => reaction.userId === userId)?.type ?? null,\n total: reactions.length,\n };\n}\n\nfunction serializeDiscussion(discussion: DiscussionWithInclude, userId: string, unreadCount = 0) {\n return {\n id: discussion.id,\n classId: discussion.classId,\n authorId: discussion.authorId,\n title: discussion.title,\n body: discussion.body,\n type: discussion.type,\n isPinned: discussion.isPinned,\n isLocked: discussion.isLocked,\n allowReplies: discussion.allowReplies,\n createdAt: discussion.createdAt,\n updatedAt: discussion.updatedAt,\n lastActivityAt: discussion.lastActivityAt,\n author: discussion.author,\n attachments: discussion.attachments,\n tags: discussion.tags,\n replyCount: discussion._count.replies,\n hasAcceptedAnswer: false,\n reactions: summarizeReactions(discussion.reactions, userId),\n unreadCount,\n };\n}\n\nfunction serializeReply(reply: ReplyWithInclude, userId: string) {\n return {\n id: reply.id,\n discussionId: reply.discussionId,\n authorId: reply.authorId,\n parentId: reply.parentId,\n body: reply.deletedAt ? \"[deleted]\" : reply.body,\n deletedAt: reply.deletedAt,\n isAcceptedAnswer: reply.isAcceptedAnswer,\n createdAt: reply.createdAt,\n modifiedAt: reply.modifiedAt,\n author: reply.author,\n attachments: reply.deletedAt ? [] : reply.attachments,\n reactions: summarizeReactions(reply.reactions, userId),\n };\n}\n\nexport function buildReplyTree(replies: ReplyWithInclude[], userId: string): DiscussionReplyTree[] {\n const byId = new Map<string, DiscussionReplyTree>();\n const roots: DiscussionReplyTree[] = [];\n\n for (const reply of replies) {\n byId.set(reply.id, { ...serializeReply(reply, userId), children: [] });\n }\n\n for (const reply of replies) {\n const node = byId.get(reply.id)!;\n if (reply.parentId && byId.has(reply.parentId)) {\n byId.get(reply.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n}\n\nfunction isWithinAuthorEditWindow(createdAt: Date) {\n return Date.now() - createdAt.getTime() <= 24 * 60 * 60 * 1000;\n}\n\nexport async function getDiscussionClassAccess(classId: string, userId: string) {\n const classData = await prisma.class.findFirst({\n where: { id: classId, ...classMemberOrSchoolAdminWhere(userId) },\n select: {\n id: true,\n name: true,\n discussionsEnabled: true,\n studentsCanCreateDiscussions: true,\n studentsCanReply: true,\n students: { where: { id: userId }, select: { id: true } },\n teachers: { where: { id: userId }, select: { id: true } },\n school: {\n select: {\n memberships: {\n where: { userId, role: \"ADMIN\", status: \"ACTIVE\" },\n select: { userId: true },\n },\n },\n },\n },\n });\n\n if (!classData) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a member in this class\" });\n }\n if (!classData.discussionsEnabled) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Discussions are disabled for this class\" });\n }\n\n const isTeacher = classData.teachers.length > 0 || Boolean(classData.school?.memberships.length);\n const isStudent = classData.students.length > 0;\n\n return { ...classData, isTeacher, isStudent };\n}\n\nasync function getDiscussionForUser(discussionId: string, userId: string) {\n const discussion = await prisma.discussion.findUnique({\n where: { id: discussionId },\n include: {\n class: {\n select: {\n id: true,\n discussionsEnabled: true,\n studentsCanReply: true,\n teachers: { where: { id: userId }, select: { id: true } },\n students: { where: { id: userId }, select: { id: true } },\n school: {\n select: {\n memberships: {\n where: { userId, role: \"ADMIN\", status: \"ACTIVE\" },\n select: { userId: true },\n },\n },\n },\n },\n },\n },\n });\n\n if (!discussion) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Discussion not found\" });\n }\n\n const isTeacher = discussion.class.teachers.length > 0 || Boolean(discussion.class.school?.memberships.length);\n const isStudent = discussion.class.students.length > 0;\n if (!isTeacher && !isStudent) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a member in this class\" });\n }\n if (!discussion.class.discussionsEnabled) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Discussions are disabled for this class\" });\n }\n\n return { discussion, isTeacher, isStudent };\n}\n\nasync function claimDiscussionAttachments(\n tx: AppTransactionClient,\n userId: string,\n discussionId: string,\n attachmentIds: string[] = []\n) {\n const uniqueIds = [...new Set(attachmentIds)];\n if (!uniqueIds.length) return;\n const { count } = await tx.file.updateMany({\n where: { id: { in: uniqueIds }, userId, uploadStatus: \"COMPLETED\", ...unclaimedAttachmentWhere },\n data: { discussionId },\n });\n if (count !== uniqueIds.length) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Some attachments are missing, unfinished, or unavailable\" });\n }\n}\n\nasync function claimReplyAttachments(\n tx: AppTransactionClient,\n userId: string,\n replyId: string,\n attachmentIds: string[] = []\n) {\n const uniqueIds = [...new Set(attachmentIds)];\n if (!uniqueIds.length) return;\n const { count } = await tx.file.updateMany({\n where: { id: { in: uniqueIds }, userId, uploadStatus: \"COMPLETED\", ...unclaimedAttachmentWhere },\n data: { discussionReplyId: replyId },\n });\n if (count !== uniqueIds.length) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Some attachments are missing, unfinished, or unavailable\" });\n }\n}\n\nfunction triggerDiscussionEvent(classId: string, event: string, payload: unknown) {\n pusher.trigger(discussionChannel(classId), event, payload).catch((error) => {\n logger.error(\"Failed to trigger discussion pusher event\", { event, classId, error });\n });\n}\n\nasync function notify(receiverId: string | null | undefined, senderId: string, title: string, content: string) {\n if (!receiverId || receiverId === senderId) return;\n await sendNotification({ receiverId, title, content }).catch((error) => {\n logger.error(\"Failed to send discussion notification\", { receiverId, error });\n });\n}\n\nasync function notifyMentionedUsers(classId: string, senderId: string, body: string, link: string) {\n const usernames = [...new Set(Array.from(body.matchAll(/(^|\\s)@([a-zA-Z0-9_.-]{2,32})/g)).map((match) => match[2]))];\n if (!usernames.length) return;\n const members = await prisma.user.findMany({\n where: {\n username: { in: usernames },\n OR: [\n { teacherIn: { some: { id: classId } } },\n { studentIn: { some: { id: classId } } },\n { schoolMemberships: { some: { status: \"ACTIVE\", school: { classes: { some: { id: classId } } } } } },\n ],\n },\n select: { id: true },\n });\n await Promise.all(members.map((member) => notify(member.id, senderId, \"You were mentioned in a discussion\", link)));\n}\n\nasync function unreadCountsForDiscussions(userId: string, discussionIds: string[]) {\n if (!discussionIds.length) return new Map<string, number>();\n const reads = await prisma.discussionRead.findMany({\n where: { userId, discussionId: { in: discussionIds } },\n select: { discussionId: true, lastReadAt: true },\n });\n const readMap = new Map(reads.map((read) => [read.discussionId, read.lastReadAt]));\n const neverRead = discussionIds.filter((discussionId) => !readMap.has(discussionId));\n const readPredicates = Array.from(readMap.entries()).map(([discussionId, lastReadAt]) => ({\n discussionId,\n createdAt: { gt: lastReadAt },\n }));\n const counts = new Map<string, number>();\n\n const [neverReadCounts, readCounts] = await Promise.all([\n neverRead.length\n ? prisma.discussionReply.groupBy({\n by: [\"discussionId\"],\n where: { discussionId: { in: neverRead }, deletedAt: null, authorId: { not: userId } },\n _count: true,\n })\n : Promise.resolve([]),\n readPredicates.length\n ? prisma.discussionReply.groupBy({\n by: [\"discussionId\"],\n where: { deletedAt: null, authorId: { not: userId }, OR: readPredicates },\n _count: true,\n })\n : Promise.resolve([]),\n ]);\n\n for (const item of [...neverReadCounts, ...readCounts]) {\n counts.set(item.discussionId, item._count);\n }\n return counts;\n}\n\nexport async function listDiscussions(userId: string, input: {\n classId: string;\n tagId?: string;\n type?: DiscussionType;\n search?: string;\n sort: \"recent\" | \"activity\" | \"top\";\n cursor?: string;\n limit: number;\n}) {\n await getDiscussionClassAccess(input.classId, userId);\n const where: Prisma.DiscussionWhereInput = {\n classId: input.classId,\n ...(input.tagId ? { tags: { some: { id: input.tagId } } } : {}),\n ...(input.type ? { type: input.type } : {}),\n ...(input.search?.trim()\n ? {\n OR: [\n { title: { contains: input.search.trim(), mode: \"insensitive\" } },\n { body: { contains: input.search.trim(), mode: \"insensitive\" } },\n ],\n }\n : {}),\n };\n const orderBy: Prisma.DiscussionOrderByWithRelationInput[] =\n input.sort === \"recent\"\n ? [{ isPinned: \"desc\" }, { createdAt: \"desc\" }]\n : input.sort === \"top\"\n ? [{ isPinned: \"desc\" }, { replies: { _count: \"desc\" } }, { lastActivityAt: \"desc\" }]\n : [{ isPinned: \"desc\" }, { lastActivityAt: \"desc\" }];\n\n const items = await prisma.discussion.findMany({\n where,\n include: discussionInclude,\n orderBy,\n cursor: input.cursor ? { id: input.cursor } : undefined,\n skip: input.cursor ? 1 : 0,\n take: input.limit + 1,\n });\n const next = items.length > input.limit ? items.pop() : undefined;\n const unreadMap = await unreadCountsForDiscussions(userId, items.map((item) => item.id));\n const accepted = await prisma.discussionReply.groupBy({\n by: [\"discussionId\"],\n where: { discussionId: { in: items.map((item) => item.id) }, isAcceptedAnswer: true, deletedAt: null },\n });\n const acceptedIds = new Set(accepted.map((item) => item.discussionId));\n\n return {\n items: items.map((item) => ({\n ...serializeDiscussion(item, userId, unreadMap.get(item.id) ?? 0),\n hasAcceptedAnswer: acceptedIds.has(item.id),\n })),\n nextCursor: next?.id,\n };\n}\n\nexport async function getDiscussion(userId: string, discussionId: string) {\n const { discussion: accessDiscussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n const [discussion, replies, myReaction] = await Promise.all([\n prisma.discussion.findUniqueOrThrow({ where: { id: discussionId }, include: discussionInclude }),\n prisma.discussionReply.findMany({ where: { discussionId }, include: replyInclude, orderBy: { createdAt: \"asc\" } }),\n prisma.reaction.findUnique({ where: { userId_discussionId: { userId, discussionId } }, select: { type: true } }),\n prisma.discussionRead.upsert({\n where: { userId_discussionId: { userId, discussionId } },\n update: { lastReadAt: new Date() },\n create: { userId, discussionId },\n }),\n ]);\n const canAuthorEdit = accessDiscussion.authorId === userId && isWithinAuthorEditWindow(accessDiscussion.createdAt);\n const canReply = isTeacher || (accessDiscussion.class.studentsCanReply && !accessDiscussion.isLocked && accessDiscussion.allowReplies);\n return {\n discussion: serializeDiscussion(discussion, userId),\n replies: buildReplyTree(replies, userId),\n myReaction: myReaction?.type ?? null,\n canEdit: isTeacher || canAuthorEdit,\n canPin: isTeacher,\n canLock: isTeacher,\n canDelete: isTeacher || accessDiscussion.authorId === userId,\n canReply,\n };\n}\n\nexport async function createDiscussion(userId: string, input: {\n classId: string;\n title: string;\n body: string;\n type: DiscussionType;\n tagIds?: string[];\n attachmentIds?: string[];\n}) {\n const classData = await getDiscussionClassAccess(input.classId, userId);\n if (!classData.isTeacher && !classData.studentsCanCreateDiscussions) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Students cannot create discussions in this class\" });\n }\n if (input.type === \"ANNOUNCEMENT\" && !classData.isTeacher) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can create announcement discussions\" });\n }\n\n const discussion = await prisma.$transaction(async (tx) => {\n const created = await tx.discussion.create({\n data: {\n classId: input.classId,\n authorId: userId,\n title: input.title.trim(),\n body: input.body.trim(),\n type: input.type,\n allowReplies: input.type !== \"ANNOUNCEMENT\",\n tags: input.tagIds?.length ? { connect: input.tagIds.map((id) => ({ id })) } : undefined,\n },\n });\n await claimDiscussionAttachments(tx, userId, created.id, input.attachmentIds);\n return tx.discussion.findUniqueOrThrow({ where: { id: created.id }, include: discussionInclude });\n });\n\n triggerDiscussionEvent(input.classId, \"discussion-created\", { discussion: serializeDiscussion(discussion, userId) });\n await notifyMentionedUsers(input.classId, userId, input.body, `/class/${input.classId}/discussions/${discussion.id}`);\n return { discussion: serializeDiscussion(discussion, userId) };\n}\n\nexport async function updateDiscussion(userId: string, input: {\n discussionId: string;\n title?: string;\n body?: string;\n type?: DiscussionType;\n tagIds?: string[];\n allowReplies?: boolean;\n}) {\n const { discussion, isTeacher } = await getDiscussionForUser(input.discussionId, userId);\n if (!isTeacher && !(discussion.authorId === userId && isWithinAuthorEditWindow(discussion.createdAt))) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot edit this discussion\" });\n }\n if (input.type === \"ANNOUNCEMENT\" && !isTeacher) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can create announcement discussions\" });\n }\n const updated = await prisma.discussion.update({\n where: { id: input.discussionId },\n data: {\n ...(input.title !== undefined ? { title: input.title.trim() } : {}),\n ...(input.body !== undefined ? { body: input.body.trim() } : {}),\n ...(input.type !== undefined ? { type: input.type } : {}),\n ...(input.allowReplies !== undefined ? { allowReplies: input.allowReplies } : {}),\n ...(input.tagIds ? { tags: { set: input.tagIds.map((id) => ({ id })) } } : {}),\n },\n include: discussionInclude,\n });\n triggerDiscussionEvent(updated.classId, \"discussion-updated\", { discussion: serializeDiscussion(updated, userId) });\n return { discussion: serializeDiscussion(updated, userId) };\n}\n\nexport async function deleteDiscussion(userId: string, discussionId: string) {\n const { discussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n if (!isTeacher && discussion.authorId !== userId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot delete this discussion\" });\n }\n await prisma.discussion.delete({ where: { id: discussionId } });\n triggerDiscussionEvent(discussion.classId, \"discussion-deleted\", { discussionId });\n return { success: true };\n}\n\nexport async function replyToDiscussion(userId: string, input: {\n discussionId: string;\n parentReplyId?: string;\n body: string;\n attachmentIds?: string[];\n}) {\n const { discussion, isTeacher } = await getDiscussionForUser(input.discussionId, userId);\n const canReply = isTeacher || (discussion.class.studentsCanReply && !discussion.isLocked && discussion.allowReplies);\n if (!canReply) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot reply to this discussion\" });\n }\n let parent: { id: string; authorId: string } | null = null;\n if (input.parentReplyId) {\n parent = await prisma.discussionReply.findFirst({\n where: { id: input.parentReplyId, discussionId: input.discussionId },\n select: { id: true, authorId: true },\n });\n if (!parent) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Parent reply not found\" });\n }\n const reply = await prisma.$transaction(async (tx) => {\n const created = await tx.discussionReply.create({\n data: {\n discussionId: input.discussionId,\n authorId: userId,\n parentId: input.parentReplyId,\n body: input.body.trim(),\n },\n });\n await claimReplyAttachments(tx, userId, created.id, input.attachmentIds);\n await tx.discussion.update({\n where: { id: input.discussionId },\n data: { lastActivityAt: new Date() },\n });\n return tx.discussionReply.findUniqueOrThrow({ where: { id: created.id }, include: replyInclude });\n });\n const link = `/class/${discussion.classId}/discussions/${discussion.id}#${reply.id}`;\n await Promise.all([\n notify(discussion.authorId, userId, \"New reply to your discussion\", link),\n notify(parent?.authorId, userId, \"New reply in a discussion thread\", link),\n notifyMentionedUsers(discussion.classId, userId, input.body, link),\n ]);\n triggerDiscussionEvent(discussion.classId, \"reply-created\", { discussionId: discussion.id, reply: serializeReply(reply, userId) });\n return { reply: serializeReply(reply, userId) };\n}\n\nexport async function updateDiscussionReply(userId: string, input: { replyId: string; body: string; attachmentIds?: string[] }) {\n const existing = await prisma.discussionReply.findUnique({ where: { id: input.replyId }, include: { discussion: true } });\n if (!existing) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n const { isTeacher } = await getDiscussionForUser(existing.discussionId, userId);\n if (!isTeacher && !(existing.authorId === userId && isWithinAuthorEditWindow(existing.createdAt))) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot edit this reply\" });\n }\n const reply = await prisma.$transaction(async (tx) => {\n await tx.file.updateMany({ where: { discussionReplyId: input.replyId }, data: { discussionReplyId: null } });\n await claimReplyAttachments(tx, userId, input.replyId, input.attachmentIds);\n return tx.discussionReply.update({\n where: { id: input.replyId },\n data: { body: input.body.trim(), deletedAt: null },\n include: replyInclude,\n });\n });\n triggerDiscussionEvent(existing.discussion.classId, \"reply-updated\", { discussionId: existing.discussionId, reply: serializeReply(reply, userId) });\n return { reply: serializeReply(reply, userId) };\n}\n\nexport async function deleteDiscussionReply(userId: string, replyId: string) {\n const existing = await prisma.discussionReply.findUnique({ where: { id: replyId }, include: { discussion: true } });\n if (!existing) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n const { isTeacher } = await getDiscussionForUser(existing.discussionId, userId);\n if (!isTeacher && existing.authorId !== userId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"You cannot delete this reply\" });\n }\n const reply = await prisma.discussionReply.update({\n where: { id: replyId },\n data: { body: \"[deleted]\", deletedAt: new Date(), isAcceptedAnswer: false },\n include: replyInclude,\n });\n triggerDiscussionEvent(existing.discussion.classId, \"reply-deleted\", { discussionId: existing.discussionId, reply: serializeReply(reply, userId) });\n return { reply: serializeReply(reply, userId) };\n}\n\nexport async function pinDiscussion(userId: string, discussionId: string, isPinned: boolean) {\n const { discussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n if (!isTeacher) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can pin discussions\" });\n const updated = await prisma.discussion.update({ where: { id: discussionId }, data: { isPinned }, include: discussionInclude });\n await notify(discussion.authorId, userId, isPinned ? \"Your discussion was pinned\" : \"Your discussion was unpinned\", `/class/${discussion.classId}/discussions/${discussionId}`);\n triggerDiscussionEvent(discussion.classId, \"discussion-updated\", { discussion: serializeDiscussion(updated, userId) });\n return { discussion: serializeDiscussion(updated, userId) };\n}\n\nexport async function lockDiscussion(userId: string, discussionId: string, isLocked: boolean) {\n const { discussion, isTeacher } = await getDiscussionForUser(discussionId, userId);\n if (!isTeacher) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can lock discussions\" });\n const updated = await prisma.discussion.update({ where: { id: discussionId }, data: { isLocked }, include: discussionInclude });\n triggerDiscussionEvent(discussion.classId, \"discussion-updated\", { discussion: serializeDiscussion(updated, userId) });\n return { discussion: serializeDiscussion(updated, userId) };\n}\n\nexport async function markAcceptedAnswer(userId: string, replyId: string, accepted: boolean) {\n const reply = await prisma.discussionReply.findUnique({ where: { id: replyId }, include: { discussion: true } });\n if (!reply) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n const { isTeacher } = await getDiscussionForUser(reply.discussionId, userId);\n if (reply.discussion.type !== \"QUESTION\") {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Only question discussions can have accepted answers\" });\n }\n if (!isTeacher && reply.discussion.authorId !== userId) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only the discussion author or a teacher can mark an accepted answer\" });\n }\n const updated = await prisma.$transaction(async (tx) => {\n if (accepted) {\n await tx.discussionReply.updateMany({ where: { discussionId: reply.discussionId, isAcceptedAnswer: true }, data: { isAcceptedAnswer: false } });\n }\n return tx.discussionReply.update({ where: { id: replyId }, data: { isAcceptedAnswer: accepted }, include: replyInclude });\n });\n await notify(reply.authorId, userId, accepted ? \"Your answer was accepted\" : \"Your answer was unmarked\", `/class/${reply.discussion.classId}/discussions/${reply.discussionId}#${replyId}`);\n triggerDiscussionEvent(reply.discussion.classId, \"reply-updated\", { discussionId: reply.discussionId, reply: serializeReply(updated, userId) });\n return { reply: serializeReply(updated, userId) };\n}\n\nexport async function markDiscussionRead(userId: string, discussionId: string) {\n await getDiscussionForUser(discussionId, userId);\n return prisma.discussionRead.upsert({\n where: { userId_discussionId: { userId, discussionId } },\n update: { lastReadAt: new Date() },\n create: { userId, discussionId },\n });\n}\n\nexport async function getUnreadCounts(userId: string, classIds: string[]) {\n const allowedClasses = await prisma.class.findMany({\n where: { id: { in: classIds }, discussionsEnabled: true, ...classMemberOrSchoolAdminWhere(userId) },\n select: { id: true },\n });\n const result: Record<string, { total: number; perDiscussion: { discussionId: string; count: number }[] }> = {};\n for (const cls of allowedClasses) {\n const discussions = await prisma.discussion.findMany({\n where: { classId: cls.id },\n select: { id: true },\n });\n const countMap = await unreadCountsForDiscussions(userId, discussions.map((discussion) => discussion.id));\n const perDiscussion = Array.from(countMap.entries())\n .map(([discussionId, count]) => ({ discussionId, count }))\n .filter((item) => item.count > 0);\n result[cls.id] = {\n total: perDiscussion.reduce((sum, item) => sum + item.count, 0),\n perDiscussion,\n };\n }\n return result;\n}\n\nexport async function toggleDiscussionReaction(userId: string, input: { discussionId?: string; discussionReplyId?: string; type: ReactionType }) {\n if (!input.discussionId && !input.discussionReplyId) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"A discussion or reply is required\" });\n }\n if (input.discussionId && input.discussionReplyId) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"React to either a discussion or a reply\" });\n }\n const target = input.discussionId\n ? await getDiscussionForUser(input.discussionId, userId).then(({ discussion }) => ({ classId: discussion.classId, discussionId: discussion.id }))\n : await prisma.discussionReply.findUnique({ where: { id: input.discussionReplyId }, include: { discussion: true } }).then(async (reply) => {\n if (!reply) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Reply not found\" });\n await getDiscussionForUser(reply.discussionId, userId);\n return { classId: reply.discussion.classId, discussionId: reply.discussionId };\n });\n const where = input.discussionId\n ? { userId_discussionId: { userId, discussionId: input.discussionId } }\n : { userId_discussionReplyId: { userId, discussionReplyId: input.discussionReplyId! } };\n const existing = await prisma.reaction.findUnique({ where });\n if (existing?.type === input.type) {\n await prisma.reaction.delete({ where: { id: existing.id } });\n triggerDiscussionEvent(target.classId, \"reaction-removed\", { discussionId: target.discussionId, discussionReplyId: input.discussionReplyId, userId });\n return { reaction: null };\n }\n const reaction = input.discussionId\n ? await prisma.reaction.upsert({\n where: { userId_discussionId: { userId, discussionId: input.discussionId } },\n update: { type: input.type },\n create: { userId, discussionId: input.discussionId, type: input.type },\n })\n : await prisma.reaction.upsert({\n where: { userId_discussionReplyId: { userId, discussionReplyId: input.discussionReplyId! } },\n update: { type: input.type },\n create: { userId, discussionReplyId: input.discussionReplyId!, type: input.type },\n });\n triggerDiscussionEvent(target.classId, \"reaction-added\", { discussionId: target.discussionId, discussionReplyId: input.discussionReplyId, reaction });\n return { reaction };\n}\n\nexport async function listDiscussionTags(userId: string, classId: string) {\n await getDiscussionClassAccess(classId, userId);\n return prisma.classTag.findMany({ where: { classId }, orderBy: { name: \"asc\" } });\n}\n\nexport async function createDiscussionTag(userId: string, input: { classId: string; name: string; color: string }) {\n const isTeacher = await prisma.class.findFirst({ where: { id: input.classId, ...classTeacherOrSchoolAdminWhere(userId) }, select: { id: true, discussionsEnabled: true } });\n if (!isTeacher?.discussionsEnabled) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can manage discussion tags\" });\n return prisma.classTag.create({ data: { classId: input.classId, name: input.name.trim(), color: input.color } });\n}\n\nexport async function updateDiscussionTag(userId: string, input: { tagId: string; name?: string; color?: string }) {\n const tag = await prisma.classTag.findUnique({ where: { id: input.tagId }, select: { classId: true } });\n if (!tag) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Tag not found\" });\n const isTeacher = await prisma.class.findFirst({ where: { id: tag.classId, ...classTeacherOrSchoolAdminWhere(userId) }, select: { id: true, discussionsEnabled: true } });\n if (!isTeacher?.discussionsEnabled) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can manage discussion tags\" });\n return prisma.classTag.update({ where: { id: input.tagId }, data: { name: input.name?.trim(), color: input.color } });\n}\n\nexport async function deleteDiscussionTag(userId: string, tagId: string) {\n const tag = await prisma.classTag.findUnique({ where: { id: tagId }, select: { classId: true } });\n if (!tag) throw new TRPCError({ code: \"NOT_FOUND\", message: \"Tag not found\" });\n const isTeacher = await prisma.class.findFirst({ where: { id: tag.classId, ...classTeacherOrSchoolAdminWhere(userId) }, select: { id: true, discussionsEnabled: true } });\n if (!isTeacher?.discussionsEnabled) throw new TRPCError({ code: \"FORBIDDEN\", message: \"Only teachers can manage discussion tags\" });\n await prisma.classTag.delete({ where: { id: tagId } });\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"032ababf-51bb-5770-84c4-b3ab35e0e66b"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const discussionSummarySchema: z.ZodObject<{
|
|
3
|
+
summary: z.ZodString;
|
|
4
|
+
keyQuestions: z.ZodArray<z.ZodString, "many">;
|
|
5
|
+
unresolvedIssues: z.ZodArray<z.ZodString, "many">;
|
|
6
|
+
participantCount: z.ZodNumber;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
summary: string;
|
|
9
|
+
keyQuestions: string[];
|
|
10
|
+
unresolvedIssues: string[];
|
|
11
|
+
participantCount: number;
|
|
12
|
+
}, {
|
|
13
|
+
summary: string;
|
|
14
|
+
keyQuestions: string[];
|
|
15
|
+
unresolvedIssues: string[];
|
|
16
|
+
participantCount: number;
|
|
17
|
+
}>;
|
|
18
|
+
export type DiscussionSummary = z.infer<typeof discussionSummarySchema>;
|
|
19
|
+
export declare function getDiscussionSummary(userId: string, discussionId: string): Promise<DiscussionSummary>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=discussionSummary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discussionSummary.d.ts","sourceRoot":"/","sources":["services/discussionSummary.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;;EAe3B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAmFxE,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,iBAAiB,CAAC,CA+G5B"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI-powered discussion summary service with in-memory + Redis caching.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
!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]="123ae967-1b03-513e-8fd2-867781eb48f0")}catch(e){}}();
|
|
6
|
+
import { prisma } from "../lib/prisma.js";
|
|
7
|
+
import { getRedis } from "../lib/redis.js";
|
|
8
|
+
import { inference } from "../utils/inference.js";
|
|
9
|
+
import { logger } from "../utils/logger.js";
|
|
10
|
+
import { TRPCError } from "@trpc/server";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { classMemberOrSchoolAdminWhere } from "../models/access.js";
|
|
13
|
+
const discussionSummarySchema = z.object({
|
|
14
|
+
summary: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe("A concise 2-4 sentence TL;DR of the entire discussion thread."),
|
|
17
|
+
keyQuestions: z
|
|
18
|
+
.array(z.string())
|
|
19
|
+
.describe("Up to 5 important student questions raised in the thread."),
|
|
20
|
+
unresolvedIssues: z
|
|
21
|
+
.array(z.string())
|
|
22
|
+
.describe("Up to 3 questions or topics that remain unanswered or unresolved."),
|
|
23
|
+
participantCount: z
|
|
24
|
+
.number()
|
|
25
|
+
.describe("Number of unique participants in the thread."),
|
|
26
|
+
});
|
|
27
|
+
const REDIS_PREFIX = "discussionSummary:";
|
|
28
|
+
const REDIS_TTL_SECONDS = 60 * 30; // 30 min
|
|
29
|
+
const memoryCache = new Map();
|
|
30
|
+
const MEMORY_TTL_MS = 10 * 60 * 1000; // 10 min
|
|
31
|
+
function memoryCacheKey(discussionId) {
|
|
32
|
+
return discussionId;
|
|
33
|
+
}
|
|
34
|
+
function getMemoryCacheHit(discussionId, currentReplyCount) {
|
|
35
|
+
const entry = memoryCache.get(memoryCacheKey(discussionId));
|
|
36
|
+
if (!entry)
|
|
37
|
+
return undefined;
|
|
38
|
+
if (Date.now() - entry.cachedAt > MEMORY_TTL_MS) {
|
|
39
|
+
memoryCache.delete(memoryCacheKey(discussionId));
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
if (entry.replyCount !== currentReplyCount) {
|
|
43
|
+
memoryCache.delete(memoryCacheKey(discussionId));
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return entry.value;
|
|
47
|
+
}
|
|
48
|
+
function setMemoryCache(discussionId, value, replyCount) {
|
|
49
|
+
memoryCache.set(memoryCacheKey(discussionId), {
|
|
50
|
+
value,
|
|
51
|
+
replyCount,
|
|
52
|
+
cachedAt: Date.now(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function getRedisCacheHit(discussionId, currentReplyCount) {
|
|
56
|
+
const redis = getRedis();
|
|
57
|
+
if (!redis)
|
|
58
|
+
return undefined;
|
|
59
|
+
try {
|
|
60
|
+
const raw = await redis.get(`${REDIS_PREFIX}${discussionId}`);
|
|
61
|
+
if (!raw)
|
|
62
|
+
return undefined;
|
|
63
|
+
const parsed = JSON.parse(raw);
|
|
64
|
+
if (parsed.replyCount !== currentReplyCount)
|
|
65
|
+
return undefined;
|
|
66
|
+
return parsed.value;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function setRedisCache(discussionId, value, replyCount) {
|
|
73
|
+
const redis = getRedis();
|
|
74
|
+
if (!redis)
|
|
75
|
+
return;
|
|
76
|
+
try {
|
|
77
|
+
await redis.set(`${REDIS_PREFIX}${discussionId}`, JSON.stringify({ value, replyCount }), "EX", REDIS_TTL_SECONDS);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
logger.error("Failed to cache discussion summary in Redis", { err });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export async function getDiscussionSummary(userId, discussionId) {
|
|
84
|
+
const discussion = await prisma.discussion.findUnique({
|
|
85
|
+
where: { id: discussionId },
|
|
86
|
+
select: {
|
|
87
|
+
id: true,
|
|
88
|
+
classId: true,
|
|
89
|
+
title: true,
|
|
90
|
+
body: true,
|
|
91
|
+
type: true,
|
|
92
|
+
replies: {
|
|
93
|
+
where: { deletedAt: null },
|
|
94
|
+
select: {
|
|
95
|
+
body: true,
|
|
96
|
+
author: {
|
|
97
|
+
select: {
|
|
98
|
+
username: true,
|
|
99
|
+
profile: { select: { displayName: true } },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
createdAt: true,
|
|
103
|
+
},
|
|
104
|
+
orderBy: { createdAt: "asc" },
|
|
105
|
+
},
|
|
106
|
+
author: {
|
|
107
|
+
select: {
|
|
108
|
+
username: true,
|
|
109
|
+
profile: { select: { displayName: true } },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
if (!discussion) {
|
|
115
|
+
throw new TRPCError({
|
|
116
|
+
code: "NOT_FOUND",
|
|
117
|
+
message: "Discussion not found",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Verify user is a member of the class
|
|
121
|
+
const classMembership = await prisma.class.findFirst({
|
|
122
|
+
where: { id: discussion.classId, ...classMemberOrSchoolAdminWhere(userId) },
|
|
123
|
+
select: { id: true },
|
|
124
|
+
});
|
|
125
|
+
if (!classMembership) {
|
|
126
|
+
throw new TRPCError({
|
|
127
|
+
code: "UNAUTHORIZED",
|
|
128
|
+
message: "You are not a member of this class",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const replyCount = discussion.replies.length;
|
|
132
|
+
// Check memory cache
|
|
133
|
+
const memoryCached = getMemoryCacheHit(discussionId, replyCount);
|
|
134
|
+
if (memoryCached)
|
|
135
|
+
return memoryCached;
|
|
136
|
+
// Check Redis cache
|
|
137
|
+
const redisCached = await getRedisCacheHit(discussionId, replyCount);
|
|
138
|
+
if (redisCached) {
|
|
139
|
+
setMemoryCache(discussionId, redisCached, replyCount);
|
|
140
|
+
return redisCached;
|
|
141
|
+
}
|
|
142
|
+
// Build context for inference
|
|
143
|
+
const authorName = discussion.author.profile?.displayName ?? discussion.author.username;
|
|
144
|
+
const threadText = [
|
|
145
|
+
`Title: ${discussion.title}`,
|
|
146
|
+
`Type: ${discussion.type}`,
|
|
147
|
+
`Author: ${authorName}`,
|
|
148
|
+
`Original post:\n${discussion.body}`,
|
|
149
|
+
"",
|
|
150
|
+
...discussion.replies.map((reply, i) => {
|
|
151
|
+
const name = reply.author.profile?.displayName ?? reply.author.username;
|
|
152
|
+
return `Reply ${i + 1} by ${name}:\n${reply.body}`;
|
|
153
|
+
}),
|
|
154
|
+
].join("\n\n");
|
|
155
|
+
const messages = [
|
|
156
|
+
{
|
|
157
|
+
role: "system",
|
|
158
|
+
content: "You are a helpful assistant summarizing a class discussion thread for a teacher. Be concise, specific, and grounded in the actual content. Do not fabricate information.",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
role: "user",
|
|
162
|
+
content: threadText,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
role: "user",
|
|
166
|
+
content: "Generate a structured summary of this discussion thread. Include a TL;DR, key student questions, and any unresolved issues.",
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
const result = await inference(messages, discussionSummarySchema, {
|
|
170
|
+
reason: "discussion_summary",
|
|
171
|
+
metadata: { discussionId, classId: discussion.classId },
|
|
172
|
+
});
|
|
173
|
+
// Cache result
|
|
174
|
+
setMemoryCache(discussionId, result, replyCount);
|
|
175
|
+
await setRedisCache(discussionId, result, replyCount);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=discussionSummary.js.map
|
|
179
|
+
//# debugId=123ae967-1b03-513e-8fd2-867781eb48f0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discussionSummary.js","sources":["services/discussionSummary.ts"],"sourceRoot":"/","sourcesContent":["/**\n * AI-powered discussion summary service with in-memory + Redis caching.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport { getRedis } from \"../lib/redis.js\";\nimport { inference } from \"../utils/inference.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { TRPCError } from \"@trpc/server\";\nimport { z } from \"zod\";\nimport { classMemberOrSchoolAdminWhere } from \"../models/access.js\";\n\nconst discussionSummarySchema = z.object({\n summary: z\n .string()\n .describe(\"A concise 2-4 sentence TL;DR of the entire discussion thread.\"),\n keyQuestions: z\n .array(z.string())\n .describe(\"Up to 5 important student questions raised in the thread.\"),\n unresolvedIssues: z\n .array(z.string())\n .describe(\n \"Up to 3 questions or topics that remain unanswered or unresolved.\",\n ),\n participantCount: z\n .number()\n .describe(\"Number of unique participants in the thread.\"),\n});\n\nexport type DiscussionSummary = z.infer<typeof discussionSummarySchema>;\n\nconst REDIS_PREFIX = \"discussionSummary:\";\nconst REDIS_TTL_SECONDS = 60 * 30; // 30 min\n\nconst memoryCache = new Map<\n string,\n { value: DiscussionSummary; replyCount: number; cachedAt: number }\n>();\nconst MEMORY_TTL_MS = 10 * 60 * 1000; // 10 min\n\nfunction memoryCacheKey(discussionId: string) {\n return discussionId;\n}\n\nfunction getMemoryCacheHit(\n discussionId: string,\n currentReplyCount: number,\n): DiscussionSummary | undefined {\n const entry = memoryCache.get(memoryCacheKey(discussionId));\n if (!entry) return undefined;\n if (Date.now() - entry.cachedAt > MEMORY_TTL_MS) {\n memoryCache.delete(memoryCacheKey(discussionId));\n return undefined;\n }\n if (entry.replyCount !== currentReplyCount) {\n memoryCache.delete(memoryCacheKey(discussionId));\n return undefined;\n }\n return entry.value;\n}\n\nfunction setMemoryCache(\n discussionId: string,\n value: DiscussionSummary,\n replyCount: number,\n) {\n memoryCache.set(memoryCacheKey(discussionId), {\n value,\n replyCount,\n cachedAt: Date.now(),\n });\n}\n\nasync function getRedisCacheHit(\n discussionId: string,\n currentReplyCount: number,\n): Promise<DiscussionSummary | undefined> {\n const redis = getRedis();\n if (!redis) return undefined;\n try {\n const raw = await redis.get(`${REDIS_PREFIX}${discussionId}`);\n if (!raw) return undefined;\n const parsed = JSON.parse(raw) as {\n value: DiscussionSummary;\n replyCount: number;\n };\n if (parsed.replyCount !== currentReplyCount) return undefined;\n return parsed.value;\n } catch {\n return undefined;\n }\n}\n\nasync function setRedisCache(\n discussionId: string,\n value: DiscussionSummary,\n replyCount: number,\n) {\n const redis = getRedis();\n if (!redis) return;\n try {\n await redis.set(\n `${REDIS_PREFIX}${discussionId}`,\n JSON.stringify({ value, replyCount }),\n \"EX\",\n REDIS_TTL_SECONDS,\n );\n } catch (err) {\n logger.error(\"Failed to cache discussion summary in Redis\", { err });\n }\n}\n\nexport async function getDiscussionSummary(\n userId: string,\n discussionId: string,\n): Promise<DiscussionSummary> {\n const discussion = await prisma.discussion.findUnique({\n where: { id: discussionId },\n select: {\n id: true,\n classId: true,\n title: true,\n body: true,\n type: true,\n replies: {\n where: { deletedAt: null },\n select: {\n body: true,\n author: {\n select: {\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n createdAt: true,\n },\n orderBy: { createdAt: \"asc\" },\n },\n author: {\n select: {\n username: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n });\n\n if (!discussion) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Discussion not found\",\n });\n }\n\n // Verify user is a member of the class\n const classMembership = await prisma.class.findFirst({\n where: { id: discussion.classId, ...classMemberOrSchoolAdminWhere(userId) },\n select: { id: true },\n });\n if (!classMembership) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not a member of this class\",\n });\n }\n\n const replyCount = discussion.replies.length;\n\n // Check memory cache\n const memoryCached = getMemoryCacheHit(discussionId, replyCount);\n if (memoryCached) return memoryCached;\n\n // Check Redis cache\n const redisCached = await getRedisCacheHit(discussionId, replyCount);\n if (redisCached) {\n setMemoryCache(discussionId, redisCached, replyCount);\n return redisCached;\n }\n\n // Build context for inference\n const authorName =\n discussion.author.profile?.displayName ?? discussion.author.username;\n const threadText = [\n `Title: ${discussion.title}`,\n `Type: ${discussion.type}`,\n `Author: ${authorName}`,\n `Original post:\\n${discussion.body}`,\n \"\",\n ...discussion.replies.map((reply, i) => {\n const name =\n reply.author.profile?.displayName ?? reply.author.username;\n return `Reply ${i + 1} by ${name}:\\n${reply.body}`;\n }),\n ].join(\"\\n\\n\");\n\n const messages = [\n {\n role: \"system\" as const,\n content:\n \"You are a helpful assistant summarizing a class discussion thread for a teacher. Be concise, specific, and grounded in the actual content. Do not fabricate information.\",\n },\n {\n role: \"user\" as const,\n content: threadText,\n },\n {\n role: \"user\" as const,\n content:\n \"Generate a structured summary of this discussion thread. Include a TL;DR, key student questions, and any unresolved issues.\",\n },\n ];\n\n const result = await inference<DiscussionSummary>(\n messages,\n discussionSummarySchema,\n {\n reason: \"discussion_summary\",\n metadata: { discussionId, classId: discussion.classId },\n },\n );\n\n // Cache result\n setMemoryCache(discussionId, result, replyCount);\n await setRedisCache(discussionId, result, replyCount);\n\n return result;\n}\n"],"names":[],"mappings":"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","debug_id":"123ae967-1b03-513e-8fd2-867781eb48f0"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { EmbeddingSourceType } from '@prisma/client';
|
|
2
|
+
export interface EmbeddingSearchResult {
|
|
3
|
+
id: string;
|
|
4
|
+
sourceType: EmbeddingSourceType;
|
|
5
|
+
sourceId: string;
|
|
6
|
+
content: string;
|
|
7
|
+
metadata: Record<string, unknown>;
|
|
8
|
+
similarity: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Split text into overlapping chunks for embedding.
|
|
12
|
+
*/
|
|
13
|
+
export declare function chunkText(text: string, chunkSize?: number, overlap?: number): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Generate embedding vector for a text string.
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateEmbedding(text: string): Promise<number[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Generate embeddings for multiple texts in a single batch.
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateEmbeddings(texts: string[]): Promise<number[][]>;
|
|
22
|
+
/**
|
|
23
|
+
* Store embeddings for a source document. Replaces any existing embeddings for
|
|
24
|
+
* the same sourceType + sourceId combination.
|
|
25
|
+
*/
|
|
26
|
+
export declare function indexContent(params: {
|
|
27
|
+
classId: string;
|
|
28
|
+
sourceType: EmbeddingSourceType;
|
|
29
|
+
sourceId: string;
|
|
30
|
+
text: string;
|
|
31
|
+
metadata?: Record<string, unknown>;
|
|
32
|
+
}): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Semantic search over class embeddings using cosine similarity.
|
|
35
|
+
*/
|
|
36
|
+
export declare function searchEmbeddings(params: {
|
|
37
|
+
classId: string;
|
|
38
|
+
query: string;
|
|
39
|
+
limit?: number;
|
|
40
|
+
sourceTypes?: EmbeddingSourceType[];
|
|
41
|
+
}): Promise<EmbeddingSearchResult[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Remove all embeddings for a given source.
|
|
44
|
+
*/
|
|
45
|
+
export declare function removeEmbeddings(params: {
|
|
46
|
+
classId: string;
|
|
47
|
+
sourceType: EmbeddingSourceType;
|
|
48
|
+
sourceId: string;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Index an assignment (title + instructions) into the vector store.
|
|
52
|
+
*/
|
|
53
|
+
export declare function indexAssignment(classId: string, assignmentId: string): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Index a worksheet (questions + answers) into the vector store.
|
|
56
|
+
*/
|
|
57
|
+
export declare function indexWorksheet(classId: string, worksheetId: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Index an announcement into the vector store.
|
|
60
|
+
*/
|
|
61
|
+
export declare function indexAnnouncement(classId: string, announcementId: string): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Fire-and-forget: enqueue a background job to embed content.
|
|
64
|
+
* Safe to call from service functions after a create/update.
|
|
65
|
+
*/
|
|
66
|
+
export declare function enqueueEmbedding(classId: string, sourceType: EmbeddingSourceType, sourceId: string): void;
|
|
67
|
+
//# sourceMappingURL=embedding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.d.ts","sourceRoot":"/","sources":["services/embedding.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQ1D,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,mBAAmB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAa,EAAE,OAAO,SAAgB,GAAG,MAAM,EAAE,CAoBjG;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQvE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAY7E;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,mBAAmB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,OAAO,CAAC,MAAM,CAAC,CAmClB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACrC,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAwCnC;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,mBAAmB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB1F;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBxF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,MAAM,GACf,IAAI,CAKN"}
|