feat(notifications): include federated chat messages and invitations

This commit is contained in:
2026-10-15 17:50:38 +02:00
parent 697f538b6e
commit 7b61857fb8
11 changed files with 413 additions and 16 deletions

View File

@@ -56,6 +56,24 @@ export class NotificationService {
) {}
async trigger(input: TriggerInput) {
const { items, skipped } = await this.createItems(input)
if (skipped !== undefined) return { success: true, created: 0, delivered: 0, skipped }
const results = []
for (const item of items) results.push(await this.deliver(item))
return {
success: results.every((result) => result.success),
created: results.length,
delivered: results.filter((result) => result.success).length,
failed: results.filter((result) => !result.success).length,
}
}
/** Persist first; callers using a transaction can deliver after it commits. */
async enqueue(input: TriggerInput) {
return (await this.createItems(input)).items
}
private async createItems(input: TriggerInput): Promise<{ items: (typeof notificationsItems.$inferSelect)[]; skipped?: number }> {
const tenantId = input.tenantId
const userIds = Array.from(new Set([...(input.userIds || []), input.userId].filter(Boolean))) as string[]
@@ -68,7 +86,7 @@ export class NotificationService {
const channels = requestedChannels.filter((channel) => allowedChannels.includes(channel))
if (!channels.length) {
return { success: true, created: 0, delivered: 0, skipped: userIds.length }
return { items: [], skipped: userIds.length }
}
const results = []
@@ -99,16 +117,11 @@ export class NotificationService {
const item = itemRows[0]
if (!item) continue
results.push(await this.deliver(item))
results.push(item)
}
}
return {
success: results.every((result) => result.success),
created: results.length,
delivered: results.filter((result) => result.success).length,
failed: results.filter((result) => !result.success).length,
}
return { items: results }
}
async preferences(tenantId: number, userId: string) {
@@ -295,11 +308,12 @@ export class NotificationService {
const defaultPref = defaults.find((pref) => pref.channel === channel)
if (defaultPref) return defaultPref.enabled
if (input.eventType === "collaboration.message.new") return channel === "inapp"
return channel !== "email" && input.eventType !== "communication.message.room" && input.eventType !== "communication.room.invited"
})
}
private async deliver(item: typeof notificationsItems.$inferSelect) {
async deliver(item: typeof notificationsItems.$inferSelect) {
if (item.channel === "inapp") {
await this.markSent(item.id)
return { success: true, id: item.id, channel: item.channel }