mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-10 03:37:43 +02:00
Fix email notifications (#10376)
* Fix email notifications Signed-off-by: Artem Savchenko <armisav@gmail.com> * Add warning Signed-off-by: Artem Savchenko <armisav@gmail.com> * Clean up Signed-off-by: Artem Savchenko <armisav@gmail.com> --------- Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
@@ -13,9 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import contact, { Channel, Employee, formatName, Person } from '@hcengineering/contact'
|
||||
import contact, { Channel, formatName, Person, SocialIdentity } from '@hcengineering/contact'
|
||||
import core, {
|
||||
AccountUuid,
|
||||
PersonId,
|
||||
Class,
|
||||
concatLink,
|
||||
@@ -30,7 +29,8 @@ import core, {
|
||||
TxCreateDoc,
|
||||
TxProcessor,
|
||||
groupByArray,
|
||||
SocialIdType
|
||||
SocialIdType,
|
||||
Domain
|
||||
} from '@hcengineering/core'
|
||||
import gmail, { Message } from '@hcengineering/gmail'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
@@ -122,18 +122,10 @@ export async function sendEmailNotification (
|
||||
try {
|
||||
const mailURL = getMetadata(serverNotification.metadata.MailUrl)
|
||||
if (mailURL === undefined || mailURL === '') {
|
||||
ctx.error('sendEmailNotification: Email service URL not configured')
|
||||
ctx.error('Please provide email service url to enable email notifications.')
|
||||
return
|
||||
}
|
||||
const mailAuth: string | undefined = getMetadata(serverNotification.metadata.MailAuthToken)
|
||||
|
||||
ctx.info('sendEmailNotification: Sending email', {
|
||||
receiver,
|
||||
subject,
|
||||
mailURL,
|
||||
hasAuth: mailAuth != null
|
||||
})
|
||||
|
||||
const response = await fetch(concatLink(mailURL, '/send'), {
|
||||
method: 'post',
|
||||
keepalive: true,
|
||||
@@ -149,24 +141,10 @@ export async function sendEmailNotification (
|
||||
})
|
||||
})
|
||||
if (!response.ok) {
|
||||
ctx.error('sendEmailNotification: Failed to send email', {
|
||||
receiver,
|
||||
subject,
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
})
|
||||
} else {
|
||||
ctx.info('sendEmailNotification: Email sent successfully', {
|
||||
receiver,
|
||||
subject
|
||||
})
|
||||
ctx.error(`Failed to send email notification: ${response.statusText}`)
|
||||
}
|
||||
} catch (err) {
|
||||
ctx.error('sendEmailNotification: Exception while sending email', {
|
||||
err,
|
||||
receiver,
|
||||
subject
|
||||
})
|
||||
ctx.error('Could not send email notification', { err, receiver })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,17 +158,6 @@ async function notifyByEmail (
|
||||
data: InboxNotification,
|
||||
message: ActivityMessage
|
||||
): Promise<void> {
|
||||
control.ctx.info('notifyByEmail: Preparing email notification', {
|
||||
notificationId: data._id,
|
||||
notificationUser: data.user,
|
||||
type,
|
||||
docId: doc._id,
|
||||
docClass: doc._class,
|
||||
email,
|
||||
messageId: message._id,
|
||||
senderSocialId
|
||||
})
|
||||
|
||||
let senderName = sender !== undefined ? formatName(sender.name, control.branding?.lastNameFirst) : ''
|
||||
if (senderName === '' && senderSocialId === core.account.System) {
|
||||
senderName = 'System'
|
||||
@@ -199,14 +166,6 @@ async function notifyByEmail (
|
||||
|
||||
if (content !== undefined) {
|
||||
await sendEmailNotification(control.ctx, content.text, content.html, content.subject, email)
|
||||
} else {
|
||||
control.ctx.warn('notifyByEmail: No content generated for email', {
|
||||
notificationId: data._id,
|
||||
notificationUser: data.user,
|
||||
type,
|
||||
docId: doc._id,
|
||||
email
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,50 +193,19 @@ async function getNotificationMessages (
|
||||
}
|
||||
|
||||
async function processEmailNotifications (control: TriggerControl, notifications: InboxNotification[]): Promise<void> {
|
||||
if (notifications.length === 0) {
|
||||
control.ctx.info('processEmailNotifications: No notifications to process')
|
||||
return
|
||||
}
|
||||
if (notifications.length === 0) return
|
||||
const docId = notifications[0].objectId
|
||||
const docClass = notifications[0].objectClass
|
||||
|
||||
control.ctx.info('processEmailNotifications: Starting email notification processing', {
|
||||
docId,
|
||||
docClass,
|
||||
notificationCount: notifications.length
|
||||
})
|
||||
|
||||
const doc = (await control.findAll(control.ctx, docClass, { _id: docId }))[0]
|
||||
if (doc === undefined) {
|
||||
control.ctx.warn('processEmailNotifications: Document not found', {
|
||||
docId,
|
||||
docClass
|
||||
})
|
||||
return
|
||||
}
|
||||
if (doc === undefined) return
|
||||
const messages = await getNotificationMessages(notifications, control)
|
||||
const { hierarchy } = control
|
||||
|
||||
control.ctx.info('processEmailNotifications: Retrieved activity messages', {
|
||||
docId,
|
||||
messageCount: messages.length
|
||||
})
|
||||
|
||||
const senders = new Map<PersonId, Person>()
|
||||
const skipped: Array<{
|
||||
notificationId: Ref<InboxNotification>
|
||||
user: AccountUuid
|
||||
reason: string
|
||||
employeeId?: Ref<Employee>
|
||||
}> = []
|
||||
let sentCount = 0
|
||||
|
||||
for (const n of notifications) {
|
||||
const type = (n.types ?? [])[0]
|
||||
if (type === undefined) {
|
||||
skipped.push({ notificationId: n._id, user: n.user, reason: 'no_notification_type' })
|
||||
continue
|
||||
}
|
||||
if (type === undefined) continue
|
||||
let message: ActivityMessage | undefined
|
||||
if (hierarchy.isDerived(n._class, notification.class.ActivityInboxNotification)) {
|
||||
const activityNotification = n as ActivityInboxNotification
|
||||
@@ -289,28 +217,28 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
}
|
||||
}
|
||||
|
||||
if (message === undefined) {
|
||||
skipped.push({ notificationId: n._id, user: n.user, reason: 'activity_message_not_found' })
|
||||
continue
|
||||
}
|
||||
if (message === undefined) continue
|
||||
const employee = await getEmployeeByAcc(control, n.user)
|
||||
if (employee === undefined) {
|
||||
skipped.push({ notificationId: n._id, user: n.user, reason: 'employee_not_found' })
|
||||
continue
|
||||
}
|
||||
const emails = await control.findAll(control.ctx, contact.class.SocialIdentity, {
|
||||
if (employee === undefined) continue
|
||||
const emailQuery = {
|
||||
attachedTo: employee._id,
|
||||
type: { $in: [SocialIdType.EMAIL, SocialIdType.GOOGLE] },
|
||||
verifiedOn: { $gt: 0 },
|
||||
isDeleted: { $ne: true }
|
||||
})
|
||||
}
|
||||
|
||||
let emails: SocialIdentity[] = []
|
||||
try {
|
||||
// Use rawFindAll to avoid filters from permission middleware
|
||||
emails = await control.lowLevel.rawFindAll<SocialIdentity>('channel' as Domain, emailQuery, { limit: 10 })
|
||||
} catch (err) {
|
||||
// Fallback to regular findAll if lowLevel fails
|
||||
control.ctx.warn('processEmailNotifications: Raw find all failed', { employeeId: employee._id, err })
|
||||
const emailsResult = await control.findAll(control.ctx, contact.class.SocialIdentity, emailQuery)
|
||||
emails = emailsResult as SocialIdentity[]
|
||||
}
|
||||
|
||||
if (emails.length === 0) {
|
||||
skipped.push({
|
||||
notificationId: n._id,
|
||||
user: n.user,
|
||||
reason: 'no_verified_email_found',
|
||||
employeeId: employee._id
|
||||
})
|
||||
control.ctx.warn('processEmailNotifications: No verified email found for employee', {
|
||||
notificationId: n._id,
|
||||
user: n.user,
|
||||
@@ -319,15 +247,6 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
continue
|
||||
}
|
||||
|
||||
control.ctx.info('processEmailNotifications: Found verified email for user', {
|
||||
notificationId: n._id,
|
||||
user: n.user,
|
||||
employeeId: employee._id,
|
||||
email: emails[0].value,
|
||||
emailType: emails[0].type,
|
||||
totalEmails: emails.length
|
||||
})
|
||||
|
||||
const senderSocialId = message.createdBy ?? message.modifiedBy
|
||||
const sender = senders.get(senderSocialId) ?? (await getPerson(control, senderSocialId))
|
||||
if (sender != null) {
|
||||
@@ -335,23 +254,10 @@ async function processEmailNotifications (control: TriggerControl, notifications
|
||||
}
|
||||
|
||||
await notifyByEmail(control, type, doc, sender, senderSocialId, emails[0].value, n, message)
|
||||
sentCount++
|
||||
}
|
||||
|
||||
control.ctx.info('processEmailNotifications: Completed email notification processing', {
|
||||
docId,
|
||||
totalNotifications: notifications.length,
|
||||
sentCount,
|
||||
skippedCount: skipped.length,
|
||||
skipped
|
||||
})
|
||||
}
|
||||
|
||||
async function NotificationsHandler (txes: TxCreateDoc<InboxNotification>[], control: TriggerControl): Promise<Tx[]> {
|
||||
control.ctx.info('NotificationsHandler: Processing email notifications', {
|
||||
totalTxes: txes.length
|
||||
})
|
||||
|
||||
const availableProviders: AvailableProvidersCache = control.contextCache.get(AvailableProvidersCacheKey) ?? new Map()
|
||||
|
||||
const all: InboxNotification[] = txes
|
||||
@@ -360,35 +266,18 @@ async function NotificationsHandler (txes: TxCreateDoc<InboxNotification>[], con
|
||||
(it) => availableProviders.get(it._id)?.find((p) => p === gmail.providers.EmailNotificationProvider) !== undefined
|
||||
)
|
||||
|
||||
const filteredOut = txes.length - all.length
|
||||
if (filteredOut > 0) {
|
||||
control.ctx.info('NotificationsHandler: Filtered out notifications without email provider', {
|
||||
filteredOut,
|
||||
totalTxes: txes.length,
|
||||
remaining: all.length
|
||||
})
|
||||
}
|
||||
|
||||
if (all.length === 0) {
|
||||
control.ctx.info('NotificationsHandler: No notifications with email provider found')
|
||||
return []
|
||||
}
|
||||
|
||||
const notificationsByDocId = groupByArray(all, (n) => n.objectId)
|
||||
|
||||
control.ctx.info('NotificationsHandler: Grouped notifications by document', {
|
||||
totalNotifications: all.length,
|
||||
documentCount: notificationsByDocId.size
|
||||
})
|
||||
|
||||
await Promise.all(
|
||||
Array.from(notificationsByDocId.entries()).map(([docId, notifications]) =>
|
||||
processEmailNotifications(control, notifications)
|
||||
)
|
||||
)
|
||||
|
||||
control.ctx.info('NotificationsHandler: Completed processing all email notifications')
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
@@ -1501,10 +1501,6 @@ export async function getCollaborators (
|
||||
const mixin = getClassCollaborators(control.modelDb, control.hierarchy, doc._class)
|
||||
|
||||
if (mixin === undefined) {
|
||||
ctx.info('getCollaborators: No collaborator mixin found', {
|
||||
docId: doc._id,
|
||||
docClass: doc._class
|
||||
})
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -1513,27 +1509,12 @@ export async function getCollaborators (
|
||||
})
|
||||
|
||||
if (collaborators.length > 0) {
|
||||
const accountUuids = collaborators.map((p) => p.collaborator)
|
||||
ctx.info('getCollaborators: Found existing collaborators', {
|
||||
docId: doc._id,
|
||||
docClass: doc._class,
|
||||
collaboratorCount: accountUuids.length,
|
||||
collaborators: accountUuids
|
||||
})
|
||||
return accountUuids
|
||||
return collaborators.map((p) => p.collaborator)
|
||||
} else {
|
||||
const collaborators = await getDocCollaborators(ctx, doc, mixin, control)
|
||||
const accountUuids = collaborators
|
||||
|
||||
ctx.info('getCollaborators: Computed collaborators from doc', {
|
||||
docId: doc._id,
|
||||
docClass: doc._class,
|
||||
collaboratorCount: accountUuids.length,
|
||||
collaborators: accountUuids
|
||||
})
|
||||
|
||||
res.push(...getAddCollaboratTxes(tx.objectId, tx.objectClass, tx.objectSpace, control, collaborators))
|
||||
return accountUuids
|
||||
return collaborators
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import core, {
|
||||
matchQuery,
|
||||
type MeasureContext,
|
||||
MixinUpdate,
|
||||
notEmpty,
|
||||
PersonId,
|
||||
Ref,
|
||||
Space,
|
||||
@@ -180,33 +181,16 @@ export function isAllowed (
|
||||
)
|
||||
|
||||
if (providerSettings.length > 0 && providerSettings.every((s) => !s.enabled)) {
|
||||
control.ctx.info('isAllowed: Provider disabled by user settings', {
|
||||
provider: provider._id,
|
||||
type: type._id,
|
||||
receiverIds,
|
||||
providerSettings: providerSettings.map((s) => ({ createdBy: s.createdBy, enabled: s.enabled }))
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (providerSettings.length === 0 && !provider.defaultEnabled) {
|
||||
control.ctx.info('isAllowed: Provider disabled by default (no user settings)', {
|
||||
provider: provider._id,
|
||||
type: type._id,
|
||||
receiverIds,
|
||||
defaultEnabled: provider.defaultEnabled
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
const providerDefaults = control.modelDb.findAllSync(notification.class.NotificationProviderDefaults, {})
|
||||
|
||||
if (providerDefaults.some((it) => it.provider === provider._id && it.ignoredTypes.includes(type._id))) {
|
||||
control.ctx.info('isAllowed: Notification type ignored in provider defaults', {
|
||||
provider: provider._id,
|
||||
type: type._id,
|
||||
receiverIds
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -215,41 +199,16 @@ export function isAllowed (
|
||||
)
|
||||
|
||||
if (setting !== undefined) {
|
||||
control.ctx.info('isAllowed: Using specific type setting', {
|
||||
provider: provider._id,
|
||||
type: type._id,
|
||||
receiverIds,
|
||||
enabled: setting.enabled
|
||||
})
|
||||
return setting.enabled
|
||||
}
|
||||
|
||||
if (providerDefaults.some((it) => it.provider === provider._id && it.enabledTypes.includes(type._id))) {
|
||||
control.ctx.info('isAllowed: Notification type enabled in provider defaults', {
|
||||
provider: provider._id,
|
||||
type: type._id,
|
||||
receiverIds
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
if (type === undefined) {
|
||||
control.ctx.warn('isAllowed: Notification type is undefined', {
|
||||
provider: provider._id,
|
||||
receiverIds
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (type === undefined) return false
|
||||
|
||||
const result = type.defaultEnabled
|
||||
control.ctx.info('isAllowed: Using type default enabled', {
|
||||
provider: provider._id,
|
||||
type: type._id,
|
||||
receiverIds,
|
||||
defaultEnabled: type.defaultEnabled,
|
||||
result
|
||||
})
|
||||
return result
|
||||
return type.defaultEnabled
|
||||
}
|
||||
|
||||
export async function isShouldNotifyTx (
|
||||
@@ -267,35 +226,12 @@ export async function isShouldNotifyTx (
|
||||
const result = new Map<Ref<NotificationProvider>, NotificationType[]>()
|
||||
let providers: NotificationProvider[] = control.modelDb.findAllSync(notification.class.NotificationProvider, {})
|
||||
|
||||
control.ctx.info('isShouldNotifyTx: Starting notification check', {
|
||||
objectId: object._id,
|
||||
objectClass: object._class,
|
||||
receiverAccount: receiver.account,
|
||||
receiverEmployee: receiver.employee,
|
||||
receiverSocialIds: receiver.socialIds,
|
||||
modifiedBy: modifiedByPersonId,
|
||||
matchedTypesCount: types.length,
|
||||
matchedTypes: types.map((t) => t._id),
|
||||
isOwn,
|
||||
isSpace
|
||||
})
|
||||
|
||||
if (getMetadata(serverNotification.metadata.InboxOnlyNotifications) === true) {
|
||||
providers = providers.filter((it) => it._id === notification.providers.InboxNotificationProvider)
|
||||
control.ctx.info('isShouldNotifyTx: InboxOnlyNotifications enabled, filtering providers')
|
||||
}
|
||||
|
||||
const skippedTypes: Array<{ type: Ref<NotificationType>, reason: string }> = []
|
||||
const allowedProviders: Array<{ provider: Ref<NotificationProvider>, type: Ref<NotificationType> }> = []
|
||||
const disallowedProviders: Array<{
|
||||
provider: Ref<NotificationProvider>
|
||||
type: Ref<NotificationType>
|
||||
reason: string
|
||||
}> = []
|
||||
|
||||
for (const type of types) {
|
||||
if (type.allowedForAuthor !== true && receiver.socialIds.includes(modifiedByPersonId)) {
|
||||
skippedTypes.push({ type: type._id, reason: 'author_not_allowed_for_this_type' })
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -307,10 +243,7 @@ export async function isShouldNotifyTx (
|
||||
if (res instanceof Promise) {
|
||||
res = await res
|
||||
}
|
||||
if (!res) {
|
||||
skippedTypes.push({ type: type._id, reason: 'type_match_function_returned_false' })
|
||||
continue
|
||||
}
|
||||
if (!res) continue
|
||||
}
|
||||
}
|
||||
for (const provider of providers) {
|
||||
@@ -319,32 +252,10 @@ export async function isShouldNotifyTx (
|
||||
if (allowed) {
|
||||
const cur = result.get(provider._id) ?? []
|
||||
result.set(provider._id, [...cur, type])
|
||||
allowedProviders.push({ provider: provider._id, type: type._id })
|
||||
} else {
|
||||
disallowedProviders.push({ provider: provider._id, type: type._id, reason: 'isAllowed_returned_false' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size === 0) {
|
||||
control.ctx.warn('isShouldNotifyTx: No notification providers allowed', {
|
||||
objectId: object._id,
|
||||
receiverAccount: receiver.account,
|
||||
receiverEmployee: receiver.employee,
|
||||
receiverSocialIds: receiver.socialIds,
|
||||
skippedTypes,
|
||||
disallowedProviders,
|
||||
matchedTypesCount: types.length
|
||||
})
|
||||
} else {
|
||||
control.ctx.info('isShouldNotifyTx: Notification providers allowed', {
|
||||
objectId: object._id,
|
||||
receiverAccount: receiver.account,
|
||||
allowedProviders,
|
||||
resultSize: result.size
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -573,12 +484,7 @@ export async function getReceiversInfo (
|
||||
accounts: AccountUuid[],
|
||||
control: TriggerControl
|
||||
): Promise<ReceiverInfo[]> {
|
||||
if (accounts.length === 0) {
|
||||
ctx.info('getReceiversInfo: No accounts provided')
|
||||
return []
|
||||
}
|
||||
|
||||
ctx.info('getReceiversInfo: Processing accounts', { accountCount: accounts.length, accounts })
|
||||
if (accounts.length === 0) return []
|
||||
|
||||
const employees: Pick<Employee, '_id' | 'personUuid' | 'role'>[] = await control.findAll(
|
||||
ctx,
|
||||
@@ -586,28 +492,10 @@ export async function getReceiversInfo (
|
||||
{ personUuid: { $in: accounts }, active: true },
|
||||
{ projection: { _id: 1, personUuid: 1, role: 1 } }
|
||||
)
|
||||
if (employees.length === 0) {
|
||||
ctx.warn('getReceiversInfo: No active employees found', { accounts })
|
||||
return []
|
||||
}
|
||||
|
||||
const foundAccountUuids = new Set(employees.map((it) => it.personUuid))
|
||||
const missingAccounts = accounts.filter((acc) => !foundAccountUuids.has(acc))
|
||||
if (missingAccounts.length > 0) {
|
||||
ctx.warn('getReceiversInfo: Some accounts have no active employee', {
|
||||
missingAccounts,
|
||||
foundEmployees: employees.map((it) => ({ personUuid: it.personUuid, employeeId: it._id }))
|
||||
})
|
||||
}
|
||||
if (employees.length === 0) return []
|
||||
|
||||
const spaces = await getPersonSpaces(control)
|
||||
if (spaces.length === 0) {
|
||||
ctx.warn('getReceiversInfo: No person spaces found', {
|
||||
accounts,
|
||||
employeeCount: employees.length
|
||||
})
|
||||
return []
|
||||
}
|
||||
if (spaces.length === 0) return []
|
||||
|
||||
const socialIds: Pick<SocialIdentity, '_id' | 'attachedTo'>[] = await control.findAll(
|
||||
ctx,
|
||||
@@ -620,55 +508,23 @@ export async function getReceiversInfo (
|
||||
const spaceByPerson = new Map(spaces.map((it) => [it.person, it]))
|
||||
const socialIdsByEmployee = groupByArray(socialIds, (it) => it.attachedTo)
|
||||
|
||||
const result: ReceiverInfo[] = []
|
||||
const filteredOut: Array<{ account: AccountUuid, reason: string }> = []
|
||||
return accounts
|
||||
.map((account) => {
|
||||
const employee = employeeByAccount.get(account)
|
||||
if (employee === undefined) return undefined
|
||||
const space = spaceByPerson.get(employee._id)
|
||||
if (space === undefined) return undefined
|
||||
|
||||
for (const account of accounts) {
|
||||
const employee = employeeByAccount.get(account)
|
||||
if (employee === undefined) {
|
||||
filteredOut.push({ account, reason: 'no_active_employee' })
|
||||
continue
|
||||
}
|
||||
const space = spaceByPerson.get(employee._id)
|
||||
if (space === undefined) {
|
||||
filteredOut.push({ account, reason: 'no_person_space' })
|
||||
continue
|
||||
}
|
||||
|
||||
const employeeSocialIds = socialIdsByEmployee.get(employee._id)?.map((it) => it._id) ?? []
|
||||
if (employeeSocialIds.length === 0) {
|
||||
ctx.warn('getReceiversInfo: Employee has no social identities', {
|
||||
const info: ReceiverInfo = {
|
||||
employee: employee._id,
|
||||
role: employee.role,
|
||||
space: space._id,
|
||||
account,
|
||||
employeeId: employee._id,
|
||||
personUuid: employee.personUuid
|
||||
})
|
||||
}
|
||||
|
||||
const info: ReceiverInfo = {
|
||||
employee: employee._id,
|
||||
role: employee.role,
|
||||
space: space._id,
|
||||
account,
|
||||
socialIds: employeeSocialIds
|
||||
}
|
||||
result.push(info)
|
||||
}
|
||||
|
||||
if (filteredOut.length > 0) {
|
||||
ctx.warn('getReceiversInfo: Some accounts were filtered out', {
|
||||
filteredOut,
|
||||
resultCount: result.length,
|
||||
totalAccounts: accounts.length
|
||||
socialIds: socialIdsByEmployee.get(employee._id)?.map((it) => it._id) ?? []
|
||||
}
|
||||
return info
|
||||
})
|
||||
}
|
||||
|
||||
ctx.info('getReceiversInfo: Completed', {
|
||||
inputCount: accounts.length,
|
||||
resultCount: result.length,
|
||||
filteredOutCount: filteredOut.length
|
||||
})
|
||||
|
||||
return result
|
||||
.filter(notEmpty)
|
||||
}
|
||||
|
||||
export async function getSenderInfo (
|
||||
|
||||
@@ -153,53 +153,16 @@ async function getRequestNotificationTx (
|
||||
)
|
||||
const collaborators = await getCollaborators(control.ctx, request, control, tx, res)
|
||||
|
||||
ctx.info('getRequestNotificationTx: Collaborators retrieved', {
|
||||
requestId: request._id,
|
||||
docId: doc._id,
|
||||
collaboratorCount: collaborators.length,
|
||||
collaborators
|
||||
})
|
||||
|
||||
if (collaborators.length === 0) {
|
||||
ctx.warn('getRequestNotificationTx: No collaborators found, skipping notifications', {
|
||||
requestId: request._id,
|
||||
docId: doc._id
|
||||
})
|
||||
return res
|
||||
}
|
||||
if (collaborators.length === 0) return res
|
||||
|
||||
const notifyContexts = await control.findAll(control.ctx, notification.class.DocNotifyContext, {
|
||||
objectId: doc._id
|
||||
})
|
||||
const receiverInfos = await getReceiversInfo(ctx, Array.from(collaborators), control)
|
||||
|
||||
ctx.info('getRequestNotificationTx: Receiver info retrieved', {
|
||||
requestId: request._id,
|
||||
docId: doc._id,
|
||||
collaboratorCount: collaborators.length,
|
||||
receiverInfoCount: receiverInfos.length,
|
||||
receivers: receiverInfos.map((r) => ({
|
||||
account: r.account,
|
||||
employee: r.employee,
|
||||
socialIdsCount: r.socialIds.length,
|
||||
socialIds: r.socialIds
|
||||
}))
|
||||
})
|
||||
|
||||
if (receiverInfos.length === 0) {
|
||||
ctx.warn('getRequestNotificationTx: No receiver info after filtering, skipping notifications', {
|
||||
requestId: request._id,
|
||||
docId: doc._id,
|
||||
collaboratorCount: collaborators.length
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
const senderInfo = await getSenderInfo(ctx, tx.modifiedBy, control)
|
||||
|
||||
const notificationControl = await getNotificationProviderControl(ctx, control)
|
||||
|
||||
let notificationTxCount = 0
|
||||
for (const receiver of receiverInfos) {
|
||||
const txes = await getNotificationTxes(
|
||||
ctx,
|
||||
@@ -213,29 +176,9 @@ async function getRequestNotificationTx (
|
||||
messages,
|
||||
notificationControl
|
||||
)
|
||||
notificationTxCount += txes.length
|
||||
res.push(...txes)
|
||||
|
||||
if (txes.length === 0) {
|
||||
ctx.warn('getRequestNotificationTx: No notification transactions generated for receiver', {
|
||||
requestId: request._id,
|
||||
docId: doc._id,
|
||||
receiverAccount: receiver.account,
|
||||
receiverEmployee: receiver.employee,
|
||||
receiverSocialIds: receiver.socialIds
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ctx.info('getRequestNotificationTx: Completed', {
|
||||
requestId: request._id,
|
||||
docId: doc._id,
|
||||
collaboratorCount: collaborators.length,
|
||||
receiverInfoCount: receiverInfos.length,
|
||||
notificationTxCount,
|
||||
totalTxCount: res.length
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user