@studious-lms/server 1.4.4 → 1.4.5
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/dist/index.js +3 -17
- package/dist/index.js.map +1 -1
- package/dist/models/conversation.d.ts +22 -0
- 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/labChat.d.ts +3 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/message.d.ts +2 -0
- package/dist/models/message.d.ts.map +1 -1
- package/dist/models/newtonChat.d.ts +1 -0
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/routers/_app.d.ts +110 -0
- 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/conversation.d.ts +14 -0
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/conversation.js +6 -3
- package/dist/routers/conversation.js.map +1 -1
- package/dist/routers/labChat.d.ts +2 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/search.d.ts +41 -0
- package/dist/routers/search.d.ts.map +1 -0
- package/dist/routers/search.js +15 -0
- package/dist/routers/search.js.map +1 -0
- package/dist/services/conversation.d.ts +8 -0
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/conversation.js +24 -6
- package/dist/services/conversation.js.map +1 -1
- package/dist/services/labChat.d.ts +2 -0
- package/dist/services/labChat.d.ts.map +1 -1
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +9 -2
- package/dist/services/message.js.map +1 -1
- package/dist/services/search.d.ts +21 -0
- package/dist/services/search.d.ts.map +1 -0
- package/dist/services/search.js +352 -0
- package/dist/services/search.js.map +1 -0
- package/package.json +1 -1
- package/prisma/migrations/20260417083000_add_conversation_member_hidden_at/migration.sql +2 -0
- package/prisma/schema.prisma +1 -0
- package/src/index.ts +1 -17
- package/src/models/conversation.ts +12 -0
- package/src/routers/_app.ts +2 -0
- package/src/routers/conversation.ts +7 -0
- package/src/routers/search.ts +14 -0
- package/src/services/conversation.ts +27 -3
- package/src/services/message.ts +8 -0
- package/src/services/search.ts +397 -0
- package/tests/routers/conversation.test.ts +114 -2
- package/tests/routers/search.test.ts +109 -0
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
* Handles member management and unread counts.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
6
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1e016f9f-4be7-566a-a3f9-ad63c2a0ada1")}catch(e){}}();
|
|
7
7
|
import { TRPCError } from "@trpc/server";
|
|
8
|
-
import { findConversationsByUserId, countUnreadMessages, countUnreadMentions, findUserByUsername, findDmConversationsByUserId, findUsersByIdsOrUsernames, createConversation, findConversationByIdAndMember, createConversationMember, deleteConversationMember, } from "../models/conversation.js";
|
|
8
|
+
import { findConversationsByUserId, countUnreadMessages, countUnreadMentions, findUserByUsername, findDmConversationsByUserId, findUsersByIdsOrUsernames, createConversation, findConversationByIdAndMember, createConversationMember, hideConversationMember, deleteConversationMember, } from "../models/conversation.js";
|
|
9
9
|
/** List user's conversations with unread and mention counts. */
|
|
10
10
|
export async function listConversations(userId) {
|
|
11
11
|
const conversations = await findConversationsByUserId(userId);
|
|
12
|
-
const
|
|
12
|
+
const visibleConversations = conversations.filter((conversation) => {
|
|
13
|
+
const userMembership = conversation.members.find((member) => member.userId === userId);
|
|
14
|
+
if (!userMembership?.hiddenAt) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return conversation.updatedAt > userMembership.hiddenAt;
|
|
18
|
+
});
|
|
19
|
+
const conversationsWithUnread = await Promise.all(visibleConversations.map(async (conversation) => {
|
|
13
20
|
const userMembership = conversation.members.find((m) => m.userId === userId);
|
|
14
21
|
const lastViewedAt = userMembership?.lastViewedAt;
|
|
15
22
|
const lastViewedMentionAt = userMembership?.lastViewedMentionAt;
|
|
@@ -35,6 +42,17 @@ export async function listConversations(userId) {
|
|
|
35
42
|
}));
|
|
36
43
|
return conversationsWithUnread;
|
|
37
44
|
}
|
|
45
|
+
export async function hideConversation(userId, conversationId) {
|
|
46
|
+
const conversation = await findConversationByIdAndMember(conversationId, userId);
|
|
47
|
+
if (!conversation) {
|
|
48
|
+
throw new TRPCError({
|
|
49
|
+
code: "FORBIDDEN",
|
|
50
|
+
message: "Not a member of this conversation",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
await hideConversationMember(userId, conversationId);
|
|
54
|
+
return { success: true };
|
|
55
|
+
}
|
|
38
56
|
/** Create a DM or group conversation with the given members. */
|
|
39
57
|
export async function createConversationRecord(userId, type, name, memberIds) {
|
|
40
58
|
if (type === "GROUP" && !name) {
|
|
@@ -50,11 +68,11 @@ export async function createConversationRecord(userId, type, name, memberIds) {
|
|
|
50
68
|
});
|
|
51
69
|
}
|
|
52
70
|
if (type === "DM") {
|
|
53
|
-
const targetUser = await
|
|
71
|
+
const [targetUser] = await findUsersByIdsOrUsernames([memberIds[0]]);
|
|
54
72
|
if (!targetUser) {
|
|
55
73
|
throw new TRPCError({
|
|
56
74
|
code: "BAD_REQUEST",
|
|
57
|
-
message: `User
|
|
75
|
+
message: `User not found`,
|
|
58
76
|
});
|
|
59
77
|
}
|
|
60
78
|
const existingDMs = await findDmConversationsByUserId(userId);
|
|
@@ -135,4 +153,4 @@ export async function removeMemberFromConversation(userId, conversationId, membe
|
|
|
135
153
|
return conversation;
|
|
136
154
|
}
|
|
137
155
|
//# sourceMappingURL=conversation.js.map
|
|
138
|
-
//# debugId=
|
|
156
|
+
//# debugId=1e016f9f-4be7-566a-a3f9-ad63c2a0ada1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.js","sources":["services/conversation.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Conversation service – list, create, and manage DM/group conversations.\n * Handles member management and unread counts.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findConversationsByUserId,\n countUnreadMessages,\n countUnreadMentions,\n findUserByUsername,\n findDmConversationsByUserId,\n findUsersByIdsOrUsernames,\n createConversation,\n findConversationByIdAndMember,\n createConversationMember,\n deleteConversationMember,\n} from \"../models/conversation.js\";\n\n/** List user's conversations with unread and mention counts. */\nexport async function listConversations(userId: string) {\n const conversations = await findConversationsByUserId(userId);\n\n const conversationsWithUnread = await Promise.all(\n conversations.map(async (conversation) => {\n const userMembership = conversation.members.find(\n (m) => m.userId === userId\n );\n const lastViewedAt = userMembership?.lastViewedAt;\n const lastViewedMentionAt = userMembership?.lastViewedMentionAt;\n\n const unreadCount = await countUnreadMessages(\n conversation.id,\n userId,\n lastViewedAt ?? undefined\n );\n\n const mentionCutoffTime =\n lastViewedMentionAt && lastViewedAt\n ? lastViewedMentionAt > lastViewedAt\n ? lastViewedMentionAt\n : lastViewedAt\n : lastViewedMentionAt || lastViewedAt;\n\n const unreadMentionCount = await countUnreadMentions(\n conversation.id,\n userId,\n mentionCutoffTime ?? undefined\n );\n\n return {\n id: conversation.id,\n type: conversation.type,\n name: conversation.name,\n createdAt: conversation.createdAt,\n updatedAt: conversation.updatedAt,\n labChat: conversation.labChat,\n members: conversation.members,\n lastMessage: conversation.messages[0] || null,\n unreadCount,\n unreadMentionCount,\n };\n })\n );\n\n return conversationsWithUnread;\n}\n\n/** Create a DM or group conversation with the given members. */\nexport async function createConversationRecord(\n userId: string,\n type: \"DM\" | \"GROUP\",\n name: string | undefined,\n memberIds: string[]\n) {\n if (type === \"GROUP\" && !name) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Group conversations must have a name\",\n });\n }\n\n if (type === \"DM\" && memberIds.length !== 1) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"DM conversations must have exactly one other member\",\n });\n }\n\n if (type === \"DM\") {\n const targetUser = await findUserByUsername(memberIds[0]);\n if (!targetUser) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: `User \"${memberIds[0]}\" not found`,\n });\n }\n\n const existingDMs = await findDmConversationsByUserId(userId);\n const existingDM = existingDMs.find((conv) => {\n const memberUserIds = conv.members.map((m) => m.userId);\n return (\n memberUserIds.length === 2 &&\n memberUserIds.includes(userId) &&\n memberUserIds.includes(targetUser.id)\n );\n });\n\n if (existingDM) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: `A conversation with ${targetUser.username} already exists`,\n });\n }\n }\n\n const members = await findUsersByIdsOrUsernames(memberIds);\n type Member = (typeof members)[number];\n const uniqueMembers = Array.from(\n new Map(members.map((m) => [m.id, m] as [string, Member])).values()\n );\n\n if (uniqueMembers.length !== memberIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more members not found\",\n });\n }\n\n const toAddIds = memberIds\n .map((id) =>\n uniqueMembers.find((m) => m.id === id || m.username === id)?.id\n )\n .filter(Boolean) as string[];\n\n return createConversation({\n type,\n name,\n members: [\n { userId, role: type === \"GROUP\" ? \"ADMIN\" : \"MEMBER\" },\n ...toAddIds.map((uid) => ({ userId: uid, role: \"MEMBER\" })),\n ],\n });\n}\n\nexport async function getConversation(\n userId: string,\n conversationId: string\n) {\n const conversation = await findConversationByIdAndMember(\n conversationId,\n userId\n );\n if (!conversation) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Conversation not found or access denied\",\n });\n }\n return conversation;\n}\n\nexport async function addMemberToConversation(\n userId: string,\n conversationId: string,\n memberUsername: string\n) {\n const conversation = await findConversationByIdAndMember(\n conversationId,\n userId\n );\n if (!conversation) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Conversation not found or access denied\",\n });\n }\n\n const member = await findUserByUsername(memberUsername);\n if (!member) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Member not found\",\n });\n }\n\n await createConversationMember({\n userId: member.id,\n conversationId,\n role: \"MEMBER\",\n });\n\n return conversation;\n}\n\nexport async function removeMemberFromConversation(\n userId: string,\n conversationId: string,\n memberId: string\n) {\n const conversation = await findConversationByIdAndMember(\n conversationId,\n userId\n );\n if (!conversation) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Conversation not found or access denied\",\n });\n }\n\n await deleteConversationMember(memberId, conversationId);\n return conversation;\n}\n"],"names":[],"mappings":"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","debug_id":"4ed19abe-3dc1-5bea-9dac-6a700dc76438"}
|
|
1
|
+
{"version":3,"file":"conversation.js","sources":["services/conversation.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Conversation service – list, create, and manage DM/group conversations.\n * Handles member management and unread counts.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findConversationsByUserId,\n countUnreadMessages,\n countUnreadMentions,\n findUserByUsername,\n findDmConversationsByUserId,\n findUsersByIdsOrUsernames,\n createConversation,\n findConversationByIdAndMember,\n createConversationMember,\n hideConversationMember,\n deleteConversationMember,\n} from \"../models/conversation.js\";\n\n/** List user's conversations with unread and mention counts. */\nexport async function listConversations(userId: string) {\n const conversations = await findConversationsByUserId(userId);\n const visibleConversations = conversations.filter((conversation) => {\n const userMembership = conversation.members.find((member) => member.userId === userId);\n\n if (!userMembership?.hiddenAt) {\n return true;\n }\n\n return conversation.updatedAt > userMembership.hiddenAt;\n });\n\n const conversationsWithUnread = await Promise.all(\n visibleConversations.map(async (conversation) => {\n const userMembership = conversation.members.find(\n (m) => m.userId === userId\n );\n const lastViewedAt = userMembership?.lastViewedAt;\n const lastViewedMentionAt = userMembership?.lastViewedMentionAt;\n\n const unreadCount = await countUnreadMessages(\n conversation.id,\n userId,\n lastViewedAt ?? undefined\n );\n\n const mentionCutoffTime =\n lastViewedMentionAt && lastViewedAt\n ? lastViewedMentionAt > lastViewedAt\n ? lastViewedMentionAt\n : lastViewedAt\n : lastViewedMentionAt || lastViewedAt;\n\n const unreadMentionCount = await countUnreadMentions(\n conversation.id,\n userId,\n mentionCutoffTime ?? undefined\n );\n\n return {\n id: conversation.id,\n type: conversation.type,\n name: conversation.name,\n createdAt: conversation.createdAt,\n updatedAt: conversation.updatedAt,\n labChat: conversation.labChat,\n members: conversation.members,\n lastMessage: conversation.messages[0] || null,\n unreadCount,\n unreadMentionCount,\n };\n })\n );\n\n return conversationsWithUnread;\n}\n\nexport async function hideConversation(userId: string, conversationId: string) {\n const conversation = await findConversationByIdAndMember(conversationId, userId);\n if (!conversation) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await hideConversationMember(userId, conversationId);\n\n return { success: true };\n}\n\n/** Create a DM or group conversation with the given members. */\nexport async function createConversationRecord(\n userId: string,\n type: \"DM\" | \"GROUP\",\n name: string | undefined,\n memberIds: string[]\n) {\n if (type === \"GROUP\" && !name) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Group conversations must have a name\",\n });\n }\n\n if (type === \"DM\" && memberIds.length !== 1) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"DM conversations must have exactly one other member\",\n });\n }\n\n if (type === \"DM\") {\n const [targetUser] = await findUsersByIdsOrUsernames([memberIds[0]]);\n if (!targetUser) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: `User not found`,\n });\n }\n\n const existingDMs = await findDmConversationsByUserId(userId);\n const existingDM = existingDMs.find((conv) => {\n const memberUserIds = conv.members.map((m) => m.userId);\n return (\n memberUserIds.length === 2 &&\n memberUserIds.includes(userId) &&\n memberUserIds.includes(targetUser.id)\n );\n });\n\n if (existingDM) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: `A conversation with ${targetUser.username} already exists`,\n });\n }\n }\n\n const members = await findUsersByIdsOrUsernames(memberIds);\n type Member = (typeof members)[number];\n const uniqueMembers = Array.from(\n new Map(members.map((m) => [m.id, m] as [string, Member])).values()\n );\n\n if (uniqueMembers.length !== memberIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more members not found\",\n });\n }\n\n const toAddIds = memberIds\n .map((id) =>\n uniqueMembers.find((m) => m.id === id || m.username === id)?.id\n )\n .filter(Boolean) as string[];\n\n return createConversation({\n type,\n name,\n members: [\n { userId, role: type === \"GROUP\" ? \"ADMIN\" : \"MEMBER\" },\n ...toAddIds.map((uid) => ({ userId: uid, role: \"MEMBER\" })),\n ],\n });\n}\n\nexport async function getConversation(\n userId: string,\n conversationId: string\n) {\n const conversation = await findConversationByIdAndMember(\n conversationId,\n userId\n );\n if (!conversation) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Conversation not found or access denied\",\n });\n }\n return conversation;\n}\n\nexport async function addMemberToConversation(\n userId: string,\n conversationId: string,\n memberUsername: string\n) {\n const conversation = await findConversationByIdAndMember(\n conversationId,\n userId\n );\n if (!conversation) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Conversation not found or access denied\",\n });\n }\n\n const member = await findUserByUsername(memberUsername);\n if (!member) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Member not found\",\n });\n }\n\n await createConversationMember({\n userId: member.id,\n conversationId,\n role: \"MEMBER\",\n });\n\n return conversation;\n}\n\nexport async function removeMemberFromConversation(\n userId: string,\n conversationId: string,\n memberId: string\n) {\n const conversation = await findConversationByIdAndMember(\n conversationId,\n userId\n );\n if (!conversation) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Conversation not found or access denied\",\n });\n }\n\n await deleteConversationMember(memberId, conversationId);\n return conversation;\n}\n"],"names":[],"mappings":"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","debug_id":"1e016f9f-4be7-566a-a3f9-ad63c2a0ada1"}
|
|
@@ -26,6 +26,7 @@ export declare function createLabChat(userId: string, input: {
|
|
|
26
26
|
conversationId: string;
|
|
27
27
|
role: import(".prisma/client").$Enums.ConversationRole;
|
|
28
28
|
joinedAt: Date;
|
|
29
|
+
hiddenAt: Date | null;
|
|
29
30
|
lastViewedAt: Date | null;
|
|
30
31
|
lastViewedMentionAt: Date | null;
|
|
31
32
|
})[];
|
|
@@ -78,6 +79,7 @@ export declare function getLabChat(userId: string, labChatId: string): Promise<{
|
|
|
78
79
|
conversationId: string;
|
|
79
80
|
role: import(".prisma/client").$Enums.ConversationRole;
|
|
80
81
|
joinedAt: Date;
|
|
82
|
+
hiddenAt: Date | null;
|
|
81
83
|
lastViewedAt: Date | null;
|
|
82
84
|
lastViewedMentionAt: Date | null;
|
|
83
85
|
})[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["services/labChat.ts"],"names":[],"mappings":"AA2BA,sFAAsF;AACtF,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE
|
|
1
|
+
{"version":3,"file":"labChat.d.ts","sourceRoot":"/","sources":["services/labChat.ts"],"names":[],"mappings":"AA2BA,sFAAsF;AACtF,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsH3D;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCjE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqBjE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;GAuIF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiDpE;AAED,8FAA8F;AAC9F,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;GAiFxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["services/message.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAmB7C,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,EAAO,QAQvB;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,CAAC,iBAAiB,EAC5B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAM,EAAO,iBA2BvB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD,8DAmBF;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;;;GAuCF;AAED,oEAAoE;AACpE,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;;;;;;;;;;;;;;;;;;;;cAoCyB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;GAMlD;AAED,oFAAoF;AACpF,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"/","sources":["services/message.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAmB7C,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,EAAO,QAQvB;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,CAAC,iBAAiB,EAC5B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAM,EAAO,iBA2BvB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD,8DAmBF;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;;;GAuCF;AAED,oEAAoE;AACpE,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;;;;;;;;;;;;;;;;;;;;cAoCyB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;GAMlD;AAED,oFAAoF;AACpF,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;GAgIF;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;;;;;;;;;;;;;;;GAmHF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;GAuDpE;AAUD,mFAAmF;AACnF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;;GA6HF;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;GA+BtE;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM;;GAgCvB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM;;;GA8BvB"}
|
package/dist/services/message.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="181d07f5-4bad-5e9c-a71e-3c354adb855b")}catch(e){}}();
|
|
3
3
|
import { TRPCError } from "@trpc/server";
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
5
|
import { chatChannel, pusher } from "../lib/pusher.js";
|
|
@@ -156,6 +156,13 @@ export async function sendMessage(userId, input) {
|
|
|
156
156
|
})),
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
|
+
await tx.conversationMember.updateMany({
|
|
160
|
+
where: {
|
|
161
|
+
conversationId,
|
|
162
|
+
hiddenAt: { not: null },
|
|
163
|
+
},
|
|
164
|
+
data: { hiddenAt: null },
|
|
165
|
+
});
|
|
159
166
|
await tx.conversation.update({
|
|
160
167
|
where: { id: conversationId },
|
|
161
168
|
data: { updatedAt: new Date() },
|
|
@@ -537,4 +544,4 @@ export async function getUnreadCount(userId, conversationId) {
|
|
|
537
544
|
return { unreadCount, unreadMentionCount };
|
|
538
545
|
}
|
|
539
546
|
//# sourceMappingURL=message.js.map
|
|
540
|
-
//# debugId=
|
|
547
|
+
//# debugId=181d07f5-4bad-5e9c-a71e-3c354adb855b
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","sources":["services/message.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Message service – send, update, delete messages; mark as read; track mentions.\n * Broadcasts real-time updates via Pusher.\n */\nimport type { Prisma } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { chatChannel, pusher } from \"../lib/pusher.js\";\nimport {\n confirmDirectUpload,\n createDirectUploadFiles,\n} from \"../lib/fileUpload.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n findConversationMembership,\n findMessages,\n findMentionedMemberships,\n findMessageById,\n findMessageByIdMinimal,\n countUnreadMessages,\n countUnreadMentions,\n} from \"../models/message.js\";\n\nexport function ensureMessageHasContentOrAttachments(\n content: string,\n fileIds: string[] = []\n) {\n if (!content.trim() && fileIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Message must include content or attachments\",\n });\n }\n}\n\nexport async function claimMessageAttachments(\n tx: Prisma.TransactionClient,\n userId: string,\n conversationId: string,\n messageId: string,\n fileIds: string[] = []\n) {\n const uniqueFileIds = [...new Set(fileIds)];\n\n if (uniqueFileIds.length === 0) {\n return;\n }\n\n const { count } = await tx.file.updateMany({\n where: {\n id: { in: uniqueFileIds },\n userId,\n conversationId,\n uploadStatus: \"COMPLETED\",\n messageId: null,\n },\n data: {\n messageId,\n },\n });\n\n if (count !== uniqueFileIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Some attachments are missing, unfinished, or unavailable\",\n });\n }\n}\n\nexport async function getMessageUploadUrls(\n userId: string,\n input: {\n conversationId: string;\n files: { name: string; type: string; size: number }[];\n }\n) {\n const membership = await findConversationMembership(input.conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n return createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n undefined,\n undefined,\n undefined,\n input.conversationId\n );\n}\n\nexport async function confirmMessageUpload(\n userId: string,\n input: {\n conversationId: string;\n fileId: string;\n uploadSuccess: boolean;\n errorMessage?: string;\n }\n) {\n const membership = await findConversationMembership(input.conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const file = await prisma.file.findFirst({\n where: {\n id: input.fileId,\n userId,\n conversationId: input.conversationId,\n messageId: null,\n },\n select: { id: true },\n });\n\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found or you do not have permission\",\n });\n }\n\n await confirmDirectUpload(\n input.fileId,\n input.uploadSuccess,\n input.errorMessage\n );\n\n return {\n success: true,\n message: input.uploadSuccess\n ? \"Upload confirmed successfully\"\n : \"Upload failed\",\n };\n}\n\n/** List messages in a conversation with cursor-based pagination. */\nexport async function listMessages(\n userId: string,\n input: {\n conversationId: string;\n cursor?: string;\n limit: number;\n }\n) {\n const { conversationId, cursor, limit } = input;\n\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const messages = await findMessages(conversationId, {\n cursor: cursor ? new Date(cursor) : undefined,\n limit,\n });\n\n let nextCursor: string | undefined;\n if (messages.length > limit) {\n const nextItem = messages.pop();\n nextCursor = nextItem!.createdAt.toISOString();\n }\n\n return {\n messages: messages.reverse().map((message) => ({\n id: message.id,\n content: message.content,\n senderId: message.senderId,\n conversationId: message.conversationId,\n createdAt: message.createdAt,\n sender: message.sender,\n attachments: message.attachments.map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type,\n })),\n meta: message.meta as Record<string, unknown>,\n mentions: message.mentions.map((m) => ({ user: m.user })),\n mentionsMe: message.mentions.some((m) => m.userId === userId),\n })),\n nextCursor,\n };\n}\n\n/** Send a message. Validates membership, creates message, broadcasts via Pusher. */\nexport async function sendMessage(\n userId: string,\n input: {\n conversationId: string;\n content: string;\n mentionedUserIds?: string[];\n fileIds?: string[];\n }\n) {\n const {\n conversationId,\n content,\n mentionedUserIds = [],\n fileIds = [],\n } = input;\n\n ensureMessageHasContentOrAttachments(content, fileIds);\n const normalizedContent = content.trim();\n\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await findMentionedMemberships(\n conversationId,\n mentionedUserIds\n );\n if (mentionedMemberships.length !== mentionedUserIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Some mentioned users are not members of this conversation\",\n });\n }\n }\n\n const result = await prisma.$transaction(async (tx) => {\n const message = await tx.message.create({\n data: {\n content: normalizedContent,\n senderId: userId,\n conversationId,\n },\n });\n\n await claimMessageAttachments(\n tx,\n userId,\n conversationId,\n message.id,\n fileIds\n );\n\n if (mentionedUserIds.length > 0) {\n await tx.mention.createMany({\n data: mentionedUserIds.map((mentionedUserId) => ({\n messageId: message.id,\n userId: mentionedUserId,\n })),\n });\n }\n\n await tx.conversation.update({\n where: { id: conversationId },\n data: { updatedAt: new Date() },\n });\n\n return tx.message.findUniqueOrThrow({\n where: { id: message.id },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n attachments: {\n select: { id: true, name: true, type: true },\n },\n },\n });\n });\n\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"new-message\",\n {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n meta: (result.meta as Record<string, unknown>) ?? {},\n mentionedUserIds,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast message via Pusher\", {\n error,\n conversationId,\n messageId: result.id,\n });\n }\n\n return {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n mentionedUserIds,\n };\n}\n\nexport async function updateMessage(\n userId: string,\n input: {\n messageId: string;\n content: string;\n mentionedUserIds?: string[];\n }\n) {\n const { messageId, content, mentionedUserIds = [] } = input;\n\n const existingMessage = await findMessageById(messageId);\n if (!existingMessage) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n if (existingMessage.senderId !== userId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not the sender of this message\",\n });\n }\n\n const membership = await findConversationMembership(\n existingMessage.conversationId,\n userId\n );\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await findMentionedMemberships(\n existingMessage.conversationId,\n mentionedUserIds\n );\n if (mentionedMemberships.length !== mentionedUserIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Some mentioned users are not members of this conversation\",\n });\n }\n }\n\n const updatedMessage = await prisma.$transaction(async (tx) => {\n const message = await tx.message.update({\n where: { id: messageId },\n data: { content },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n attachments: {\n select: { id: true, name: true, type: true },\n },\n },\n });\n\n await tx.mention.deleteMany({\n where: { messageId },\n });\n\n if (mentionedUserIds.length > 0) {\n await tx.mention.createMany({\n data: mentionedUserIds.map((mentionedUserId) => ({\n messageId,\n userId: mentionedUserId,\n })),\n });\n }\n\n return message;\n });\n\n try {\n await pusher.trigger(\n chatChannel(existingMessage.conversationId),\n \"message-updated\",\n {\n id: updatedMessage.id,\n content: updatedMessage.content,\n senderId: updatedMessage.senderId,\n conversationId: updatedMessage.conversationId,\n createdAt: updatedMessage.createdAt,\n sender: updatedMessage.sender,\n attachments: updatedMessage.attachments ?? [],\n meta: (updatedMessage.meta as Record<string, unknown>) ?? {},\n mentionedUserIds,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast message update via Pusher\", {\n error,\n conversationId: existingMessage.conversationId,\n messageId,\n });\n }\n\n return {\n id: updatedMessage.id,\n content: updatedMessage.content,\n senderId: updatedMessage.senderId,\n conversationId: updatedMessage.conversationId,\n createdAt: updatedMessage.createdAt,\n sender: updatedMessage.sender,\n mentionedUserIds,\n };\n}\n\nexport async function deleteMessage(userId: string, messageId: string) {\n const existingMessage = await findMessageByIdMinimal(messageId);\n if (!existingMessage) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n if (existingMessage.senderId !== userId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not the sender of this message\",\n });\n }\n\n const membership = await findConversationMembership(\n existingMessage.conversationId,\n userId\n );\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await prisma.$transaction(async (tx) => {\n await tx.mention.deleteMany({\n where: { messageId },\n });\n await tx.message.delete({\n where: { id: messageId },\n });\n });\n\n try {\n await pusher.trigger(\n chatChannel(existingMessage.conversationId),\n \"message-deleted\",\n {\n messageId,\n conversationId: existingMessage.conversationId,\n senderId: existingMessage.senderId,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast message deletion via Pusher\", {\n error,\n conversationId: existingMessage.conversationId,\n messageId,\n });\n }\n\n return { success: true, messageId };\n}\n\nconst CREATED_INDICES_KEY: Record<\"assignment\" | \"worksheet\" | \"section\", string> = {\n assignment: \"assignments\",\n worksheet: \"worksheets\",\n section: \"sections\",\n};\n\nconst MARK_SUGGESTION_MAX_RETRIES = 5;\n\n/** Mark an AI-suggested item as created. Stores in message meta for fast reads. */\nexport async function markSuggestionCreated(\n userId: string,\n input: {\n messageId: string;\n type: \"assignment\" | \"worksheet\" | \"section\";\n index: number;\n }\n) {\n const { messageId, type, index } = input;\n\n const existingMessage = await findMessageByIdMinimal(messageId);\n if (!existingMessage) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n const membership = await findConversationMembership(\n existingMessage.conversationId,\n userId\n );\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const conversationId = existingMessage.conversationId;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < MARK_SUGGESTION_MAX_RETRIES; attempt++) {\n const msg = await prisma.message.findUnique({\n where: { id: messageId },\n select: { meta: true },\n });\n if (!msg) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n const currentMeta = (msg.meta as Record<string, unknown>) ?? {};\n const createdIndices = (currentMeta.createdIndices as Record<string, number[]>) ?? {};\n const typeIndices = createdIndices[CREATED_INDICES_KEY[type]] ?? [];\n if (typeIndices.includes(index)) return { success: true };\n\n const newTypeIndices = [...typeIndices, index].sort((a, b) => a - b);\n const newMeta = {\n ...currentMeta,\n createdIndices: {\n ...createdIndices,\n [CREATED_INDICES_KEY[type]]: newTypeIndices,\n },\n };\n\n const newMetaJson = JSON.stringify(newMeta);\n const currentMetaJson = msg.meta === null ? null : JSON.stringify(msg.meta);\n\n const updatedCount =\n currentMetaJson === null\n ? await prisma.$executeRaw`\n UPDATE \"Message\" SET meta = ${newMetaJson}::jsonb\n WHERE id = ${messageId} AND meta IS NULL\n `\n : await prisma.$executeRaw`\n UPDATE \"Message\" SET meta = ${newMetaJson}::jsonb\n WHERE id = ${messageId} AND meta = ${currentMetaJson}::jsonb\n `;\n\n if (Number(updatedCount) > 0) {\n const updated = await prisma.message.findUnique({\n where: { id: messageId },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n attachments: { select: { id: true, name: true, type: true } },\n mentions: { select: { userId: true } },\n },\n });\n\n if (updated) {\n const mentionedUserIds = updated.mentions.map((m) => m.userId);\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"message-updated\",\n {\n id: updated.id,\n content: updated.content,\n senderId: updated.senderId,\n conversationId: updated.conversationId,\n createdAt: updated.createdAt,\n sender: updated.sender,\n attachments: updated.attachments ?? [],\n meta: newMeta,\n mentionedUserIds,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast suggestion status via Pusher\", {\n error,\n messageId,\n });\n }\n }\n return { success: true };\n }\n\n lastError = new Error(\"Concurrent update conflict\");\n }\n\n logger.error(\"markSuggestionCreated failed after retries\", {\n messageId,\n type,\n index,\n error: lastError,\n });\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"Failed to update suggestion status after retries due to concurrent updates\",\n });\n}\n\nexport async function markAsRead(userId: string, conversationId: string) {\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await prisma.conversationMember.update({\n where: { id: membership.id },\n data: { lastViewedAt: new Date() },\n });\n\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"conversation-viewed\",\n {\n userId,\n viewedAt: new Date(),\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast conversation-viewed via Pusher\", {\n error,\n conversationId,\n });\n }\n\n return { success: true };\n}\n\nexport async function markMentionsAsRead(\n userId: string,\n conversationId: string\n) {\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await prisma.conversationMember.update({\n where: { id: membership.id },\n data: { lastViewedMentionAt: new Date() },\n });\n\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"mentions-viewed\",\n {\n userId,\n viewedAt: new Date(),\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast mentions-viewed via Pusher\", {\n error,\n conversationId,\n });\n }\n\n return { success: true };\n}\n\nexport async function getUnreadCount(\n userId: string,\n conversationId: string\n) {\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const unreadCount = await countUnreadMessages(\n conversationId,\n userId,\n membership.lastViewedAt ?? undefined\n );\n\n const mentionCutoffTime =\n membership.lastViewedMentionAt && membership.lastViewedAt\n ? membership.lastViewedMentionAt > membership.lastViewedAt\n ? membership.lastViewedMentionAt\n : membership.lastViewedAt\n : membership.lastViewedMentionAt ?? membership.lastViewedAt;\n\n const unreadMentionCount = await countUnreadMentions(\n conversationId,\n userId,\n mentionCutoffTime ?? undefined\n );\n\n return { unreadCount, unreadMentionCount };\n}\n"],"names":[],"mappings":"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","debug_id":"e7cba144-6d25-5aa6-ab33-fe02e67b41f4"}
|
|
1
|
+
{"version":3,"file":"message.js","sources":["services/message.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Message service – send, update, delete messages; mark as read; track mentions.\n * Broadcasts real-time updates via Pusher.\n */\nimport type { Prisma } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { chatChannel, pusher } from \"../lib/pusher.js\";\nimport {\n confirmDirectUpload,\n createDirectUploadFiles,\n} from \"../lib/fileUpload.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n findConversationMembership,\n findMessages,\n findMentionedMemberships,\n findMessageById,\n findMessageByIdMinimal,\n countUnreadMessages,\n countUnreadMentions,\n} from \"../models/message.js\";\n\nexport function ensureMessageHasContentOrAttachments(\n content: string,\n fileIds: string[] = []\n) {\n if (!content.trim() && fileIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Message must include content or attachments\",\n });\n }\n}\n\nexport async function claimMessageAttachments(\n tx: Prisma.TransactionClient,\n userId: string,\n conversationId: string,\n messageId: string,\n fileIds: string[] = []\n) {\n const uniqueFileIds = [...new Set(fileIds)];\n\n if (uniqueFileIds.length === 0) {\n return;\n }\n\n const { count } = await tx.file.updateMany({\n where: {\n id: { in: uniqueFileIds },\n userId,\n conversationId,\n uploadStatus: \"COMPLETED\",\n messageId: null,\n },\n data: {\n messageId,\n },\n });\n\n if (count !== uniqueFileIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Some attachments are missing, unfinished, or unavailable\",\n });\n }\n}\n\nexport async function getMessageUploadUrls(\n userId: string,\n input: {\n conversationId: string;\n files: { name: string; type: string; size: number }[];\n }\n) {\n const membership = await findConversationMembership(input.conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n return createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n undefined,\n undefined,\n undefined,\n input.conversationId\n );\n}\n\nexport async function confirmMessageUpload(\n userId: string,\n input: {\n conversationId: string;\n fileId: string;\n uploadSuccess: boolean;\n errorMessage?: string;\n }\n) {\n const membership = await findConversationMembership(input.conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const file = await prisma.file.findFirst({\n where: {\n id: input.fileId,\n userId,\n conversationId: input.conversationId,\n messageId: null,\n },\n select: { id: true },\n });\n\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"File not found or you do not have permission\",\n });\n }\n\n await confirmDirectUpload(\n input.fileId,\n input.uploadSuccess,\n input.errorMessage\n );\n\n return {\n success: true,\n message: input.uploadSuccess\n ? \"Upload confirmed successfully\"\n : \"Upload failed\",\n };\n}\n\n/** List messages in a conversation with cursor-based pagination. */\nexport async function listMessages(\n userId: string,\n input: {\n conversationId: string;\n cursor?: string;\n limit: number;\n }\n) {\n const { conversationId, cursor, limit } = input;\n\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const messages = await findMessages(conversationId, {\n cursor: cursor ? new Date(cursor) : undefined,\n limit,\n });\n\n let nextCursor: string | undefined;\n if (messages.length > limit) {\n const nextItem = messages.pop();\n nextCursor = nextItem!.createdAt.toISOString();\n }\n\n return {\n messages: messages.reverse().map((message) => ({\n id: message.id,\n content: message.content,\n senderId: message.senderId,\n conversationId: message.conversationId,\n createdAt: message.createdAt,\n sender: message.sender,\n attachments: message.attachments.map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type,\n })),\n meta: message.meta as Record<string, unknown>,\n mentions: message.mentions.map((m) => ({ user: m.user })),\n mentionsMe: message.mentions.some((m) => m.userId === userId),\n })),\n nextCursor,\n };\n}\n\n/** Send a message. Validates membership, creates message, broadcasts via Pusher. */\nexport async function sendMessage(\n userId: string,\n input: {\n conversationId: string;\n content: string;\n mentionedUserIds?: string[];\n fileIds?: string[];\n }\n) {\n const {\n conversationId,\n content,\n mentionedUserIds = [],\n fileIds = [],\n } = input;\n\n ensureMessageHasContentOrAttachments(content, fileIds);\n const normalizedContent = content.trim();\n\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await findMentionedMemberships(\n conversationId,\n mentionedUserIds\n );\n if (mentionedMemberships.length !== mentionedUserIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Some mentioned users are not members of this conversation\",\n });\n }\n }\n\n const result = await prisma.$transaction(async (tx) => {\n const message = await tx.message.create({\n data: {\n content: normalizedContent,\n senderId: userId,\n conversationId,\n },\n });\n\n await claimMessageAttachments(\n tx,\n userId,\n conversationId,\n message.id,\n fileIds\n );\n\n if (mentionedUserIds.length > 0) {\n await tx.mention.createMany({\n data: mentionedUserIds.map((mentionedUserId) => ({\n messageId: message.id,\n userId: mentionedUserId,\n })),\n });\n }\n\n await tx.conversationMember.updateMany({\n where: {\n conversationId,\n hiddenAt: { not: null },\n },\n data: { hiddenAt: null },\n });\n\n await tx.conversation.update({\n where: { id: conversationId },\n data: { updatedAt: new Date() },\n });\n\n return tx.message.findUniqueOrThrow({\n where: { id: message.id },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n attachments: {\n select: { id: true, name: true, type: true },\n },\n },\n });\n });\n\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"new-message\",\n {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n meta: (result.meta as Record<string, unknown>) ?? {},\n mentionedUserIds,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast message via Pusher\", {\n error,\n conversationId,\n messageId: result.id,\n });\n }\n\n return {\n id: result.id,\n content: result.content,\n senderId: result.senderId,\n conversationId: result.conversationId,\n createdAt: result.createdAt,\n sender: result.sender,\n attachments: result.attachments ?? [],\n mentionedUserIds,\n };\n}\n\nexport async function updateMessage(\n userId: string,\n input: {\n messageId: string;\n content: string;\n mentionedUserIds?: string[];\n }\n) {\n const { messageId, content, mentionedUserIds = [] } = input;\n\n const existingMessage = await findMessageById(messageId);\n if (!existingMessage) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n if (existingMessage.senderId !== userId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not the sender of this message\",\n });\n }\n\n const membership = await findConversationMembership(\n existingMessage.conversationId,\n userId\n );\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n if (mentionedUserIds.length > 0) {\n const mentionedMemberships = await findMentionedMemberships(\n existingMessage.conversationId,\n mentionedUserIds\n );\n if (mentionedMemberships.length !== mentionedUserIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Some mentioned users are not members of this conversation\",\n });\n }\n }\n\n const updatedMessage = await prisma.$transaction(async (tx) => {\n const message = await tx.message.update({\n where: { id: messageId },\n data: { content },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n },\n },\n attachments: {\n select: { id: true, name: true, type: true },\n },\n },\n });\n\n await tx.mention.deleteMany({\n where: { messageId },\n });\n\n if (mentionedUserIds.length > 0) {\n await tx.mention.createMany({\n data: mentionedUserIds.map((mentionedUserId) => ({\n messageId,\n userId: mentionedUserId,\n })),\n });\n }\n\n return message;\n });\n\n try {\n await pusher.trigger(\n chatChannel(existingMessage.conversationId),\n \"message-updated\",\n {\n id: updatedMessage.id,\n content: updatedMessage.content,\n senderId: updatedMessage.senderId,\n conversationId: updatedMessage.conversationId,\n createdAt: updatedMessage.createdAt,\n sender: updatedMessage.sender,\n attachments: updatedMessage.attachments ?? [],\n meta: (updatedMessage.meta as Record<string, unknown>) ?? {},\n mentionedUserIds,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast message update via Pusher\", {\n error,\n conversationId: existingMessage.conversationId,\n messageId,\n });\n }\n\n return {\n id: updatedMessage.id,\n content: updatedMessage.content,\n senderId: updatedMessage.senderId,\n conversationId: updatedMessage.conversationId,\n createdAt: updatedMessage.createdAt,\n sender: updatedMessage.sender,\n mentionedUserIds,\n };\n}\n\nexport async function deleteMessage(userId: string, messageId: string) {\n const existingMessage = await findMessageByIdMinimal(messageId);\n if (!existingMessage) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n if (existingMessage.senderId !== userId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not the sender of this message\",\n });\n }\n\n const membership = await findConversationMembership(\n existingMessage.conversationId,\n userId\n );\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await prisma.$transaction(async (tx) => {\n await tx.mention.deleteMany({\n where: { messageId },\n });\n await tx.message.delete({\n where: { id: messageId },\n });\n });\n\n try {\n await pusher.trigger(\n chatChannel(existingMessage.conversationId),\n \"message-deleted\",\n {\n messageId,\n conversationId: existingMessage.conversationId,\n senderId: existingMessage.senderId,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast message deletion via Pusher\", {\n error,\n conversationId: existingMessage.conversationId,\n messageId,\n });\n }\n\n return { success: true, messageId };\n}\n\nconst CREATED_INDICES_KEY: Record<\"assignment\" | \"worksheet\" | \"section\", string> = {\n assignment: \"assignments\",\n worksheet: \"worksheets\",\n section: \"sections\",\n};\n\nconst MARK_SUGGESTION_MAX_RETRIES = 5;\n\n/** Mark an AI-suggested item as created. Stores in message meta for fast reads. */\nexport async function markSuggestionCreated(\n userId: string,\n input: {\n messageId: string;\n type: \"assignment\" | \"worksheet\" | \"section\";\n index: number;\n }\n) {\n const { messageId, type, index } = input;\n\n const existingMessage = await findMessageByIdMinimal(messageId);\n if (!existingMessage) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n const membership = await findConversationMembership(\n existingMessage.conversationId,\n userId\n );\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const conversationId = existingMessage.conversationId;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < MARK_SUGGESTION_MAX_RETRIES; attempt++) {\n const msg = await prisma.message.findUnique({\n where: { id: messageId },\n select: { meta: true },\n });\n if (!msg) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Message not found\",\n });\n }\n\n const currentMeta = (msg.meta as Record<string, unknown>) ?? {};\n const createdIndices = (currentMeta.createdIndices as Record<string, number[]>) ?? {};\n const typeIndices = createdIndices[CREATED_INDICES_KEY[type]] ?? [];\n if (typeIndices.includes(index)) return { success: true };\n\n const newTypeIndices = [...typeIndices, index].sort((a, b) => a - b);\n const newMeta = {\n ...currentMeta,\n createdIndices: {\n ...createdIndices,\n [CREATED_INDICES_KEY[type]]: newTypeIndices,\n },\n };\n\n const newMetaJson = JSON.stringify(newMeta);\n const currentMetaJson = msg.meta === null ? null : JSON.stringify(msg.meta);\n\n const updatedCount =\n currentMetaJson === null\n ? await prisma.$executeRaw`\n UPDATE \"Message\" SET meta = ${newMetaJson}::jsonb\n WHERE id = ${messageId} AND meta IS NULL\n `\n : await prisma.$executeRaw`\n UPDATE \"Message\" SET meta = ${newMetaJson}::jsonb\n WHERE id = ${messageId} AND meta = ${currentMetaJson}::jsonb\n `;\n\n if (Number(updatedCount) > 0) {\n const updated = await prisma.message.findUnique({\n where: { id: messageId },\n include: {\n sender: {\n select: {\n id: true,\n username: true,\n profile: {\n select: { displayName: true, profilePicture: true },\n },\n },\n },\n attachments: { select: { id: true, name: true, type: true } },\n mentions: { select: { userId: true } },\n },\n });\n\n if (updated) {\n const mentionedUserIds = updated.mentions.map((m) => m.userId);\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"message-updated\",\n {\n id: updated.id,\n content: updated.content,\n senderId: updated.senderId,\n conversationId: updated.conversationId,\n createdAt: updated.createdAt,\n sender: updated.sender,\n attachments: updated.attachments ?? [],\n meta: newMeta,\n mentionedUserIds,\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast suggestion status via Pusher\", {\n error,\n messageId,\n });\n }\n }\n return { success: true };\n }\n\n lastError = new Error(\"Concurrent update conflict\");\n }\n\n logger.error(\"markSuggestionCreated failed after retries\", {\n messageId,\n type,\n index,\n error: lastError,\n });\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"Failed to update suggestion status after retries due to concurrent updates\",\n });\n}\n\nexport async function markAsRead(userId: string, conversationId: string) {\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await prisma.conversationMember.update({\n where: { id: membership.id },\n data: { lastViewedAt: new Date() },\n });\n\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"conversation-viewed\",\n {\n userId,\n viewedAt: new Date(),\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast conversation-viewed via Pusher\", {\n error,\n conversationId,\n });\n }\n\n return { success: true };\n}\n\nexport async function markMentionsAsRead(\n userId: string,\n conversationId: string\n) {\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n await prisma.conversationMember.update({\n where: { id: membership.id },\n data: { lastViewedMentionAt: new Date() },\n });\n\n try {\n await pusher.trigger(\n chatChannel(conversationId),\n \"mentions-viewed\",\n {\n userId,\n viewedAt: new Date(),\n }\n );\n } catch (error) {\n logger.error(\"Failed to broadcast mentions-viewed via Pusher\", {\n error,\n conversationId,\n });\n }\n\n return { success: true };\n}\n\nexport async function getUnreadCount(\n userId: string,\n conversationId: string\n) {\n const membership = await findConversationMembership(conversationId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Not a member of this conversation\",\n });\n }\n\n const unreadCount = await countUnreadMessages(\n conversationId,\n userId,\n membership.lastViewedAt ?? undefined\n );\n\n const mentionCutoffTime =\n membership.lastViewedMentionAt && membership.lastViewedAt\n ? membership.lastViewedMentionAt > membership.lastViewedAt\n ? membership.lastViewedMentionAt\n : membership.lastViewedAt\n : membership.lastViewedMentionAt ?? membership.lastViewedAt;\n\n const unreadMentionCount = await countUnreadMentions(\n conversationId,\n userId,\n mentionCutoffTime ?? undefined\n );\n\n return { unreadCount, unreadMentionCount };\n}\n"],"names":[],"mappings":"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","debug_id":"181d07f5-4bad-5e9c-a71e-3c354adb855b"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type SearchScope = "class" | "assignment" | "event" | "syllabus" | "worksheet" | "file" | "announcement" | "section";
|
|
2
|
+
type SearchResult = {
|
|
3
|
+
id: string;
|
|
4
|
+
type: SearchScope;
|
|
5
|
+
title: string;
|
|
6
|
+
subtitle: string;
|
|
7
|
+
classId: string;
|
|
8
|
+
className: string;
|
|
9
|
+
href: string;
|
|
10
|
+
matchedText?: string | null;
|
|
11
|
+
date?: Date | null;
|
|
12
|
+
};
|
|
13
|
+
export declare function searchGlobalClasses(userId: string, input: {
|
|
14
|
+
query?: string;
|
|
15
|
+
limit?: number;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
query: string;
|
|
18
|
+
results: SearchResult[];
|
|
19
|
+
}>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"/","sources":["services/search.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GACZ,OAAO,GACP,YAAY,GACZ,OAAO,GACP,UAAU,GACV,WAAW,GACX,MAAM,GACN,cAAc,GACd,SAAS,CAAC;AAEd,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB,CAAC;AA6CF,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;aA0BR,YAAY,EAAE;GA6ShD"}
|