From 9b27f289cd7df76dee98691e17c3d5985cc52821 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Tue, 28 Nov 2023 16:47:13 +0600 Subject: [PATCH] Move ses (#4078) Signed-off-by: Denis Bykhov --- models/notification/src/index.ts | 37 ++-------- plugins/notification/src/index.ts | 16 +---- pods/server/src/__start.ts | 4 ++ server-plugins/hr-resources/src/index.ts | 36 ++++------ .../notification-resources/src/index.ts | 67 +++++++++---------- server-plugins/notification/src/index.ts | 5 +- server/account/src/index.ts | 41 +++++++----- 7 files changed, 84 insertions(+), 122 deletions(-) diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index a7fb02e535..0440193031 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -17,6 +17,9 @@ import activity from '@hcengineering/activity' import chunter from '@hcengineering/chunter' import { + DOMAIN_MODEL, + Hierarchy, + IndexKind, type Account, type AttachedDoc, type Class, @@ -24,26 +27,22 @@ import { type Data, type Doc, type Domain, - DOMAIN_MODEL, - Hierarchy, - IndexKind, type Ref, type Timestamp, type Tx, type TxCUD } from '@hcengineering/core' -import { ArrOf, type Builder, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@hcengineering/model' +import { ArrOf, Index, Mixin, Model, Prop, TypeRef, TypeString, UX, type Builder } from '@hcengineering/model' import core, { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import view, { createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import { - type DocUpdates, + notificationId, type DocUpdateTx, - type EmailNotification, + type DocUpdates, type Notification, type NotificationGroup, - notificationId, type NotificationObjectPresenter, type NotificationPreferencesGroup, type NotificationPreview, @@ -76,29 +75,6 @@ export class TNotification extends TAttachedDoc implements Notification { type!: Ref } -@Model(notification.class.EmailNotification, core.class.Doc, DOMAIN_NOTIFICATION) -export class TEmaiNotification extends TDoc implements EmailNotification { - @Prop(TypeString(), 'Sender' as IntlString) - sender!: string - - @Prop(ArrOf(TypeString()), 'Receivers' as IntlString) - receivers!: string[] - - @Prop(TypeString(), 'Subject' as IntlString) - subject!: string - - @Prop(TypeString(), 'Text' as IntlString) - text!: string - - @Prop(TypeString(), 'Html' as IntlString) - html?: string - - @Prop(TypeString(), 'Status' as IntlString) - status!: 'new' | 'sent' | 'error' - - error?: string -} - @Model(notification.class.NotificationType, core.class.Doc, DOMAIN_MODEL) export class TNotificationType extends TDoc implements NotificationType { generated!: boolean @@ -182,7 +158,6 @@ export class TDocUpdates extends TDoc implements DocUpdates { export function createModel (builder: Builder): void { builder.createModel( TNotification, - TEmaiNotification, TNotificationType, TNotificationProvider, TNotificationSetting, diff --git a/plugins/notification/src/index.ts b/plugins/notification/src/index.ts index b9b37152a1..e3ee5caeab 100644 --- a/plugins/notification/src/index.ts +++ b/plugins/notification/src/index.ts @@ -29,10 +29,10 @@ import { } from '@hcengineering/core' import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' +import { Preference } from '@hcengineering/preference' import { IntegrationType } from '@hcengineering/setting' import { AnyComponent } from '@hcengineering/ui' import { Writable } from './types' -import { Preference } from '@hcengineering/preference' export * from './types' /** @@ -45,19 +45,6 @@ export interface Notification extends AttachedDoc { type: Ref } -/** - * @public - */ -export interface EmailNotification extends Doc { - sender: string - receivers: string[] - subject: string - text: string - html?: string - status: 'new' | 'sent' | 'error' - error?: string -} - /** * @public */ @@ -238,7 +225,6 @@ const notification = plugin(notificationId, { }, class: { Notification: '' as Ref>, - EmailNotification: '' as Ref>, NotificationType: '' as Ref>, NotificationProvider: '' as Ref>, NotificationSetting: '' as Ref>, diff --git a/pods/server/src/__start.ts b/pods/server/src/__start.ts index b18bca67a5..a10a3516bf 100644 --- a/pods/server/src/__start.ts +++ b/pods/server/src/__start.ts @@ -20,6 +20,7 @@ import serverCore from '@hcengineering/server-core' import serverToken from '@hcengineering/server-token' import { serverFactories } from '@hcengineering/server-ws' import { start } from '.' +import serverNotification from '@hcengineering/server-notification' const serverPort = parseInt(process.env.SERVER_PORT ?? '3333') @@ -81,8 +82,11 @@ if (frontUrl === undefined) { process.exit(1) } +const sesUrl = process.env.SES_URL + setMetadata(serverCore.metadata.FrontUrl, frontUrl) setMetadata(serverToken.metadata.Secret, serverSecret) +setMetadata(serverNotification.metadata.SesUrl, sesUrl ?? '') // eslint-disable-next-line @typescript-eslint/no-floating-promises console.log( diff --git a/server-plugins/hr-resources/src/index.ts b/server-plugins/hr-resources/src/index.ts index 128d9be7f3..3dafcbf8ac 100644 --- a/server-plugins/hr-resources/src/index.ts +++ b/server-plugins/hr-resources/src/index.ts @@ -40,7 +40,7 @@ import notification, { NotificationType } from '@hcengineering/notification' import { translate } from '@hcengineering/platform' import { TriggerControl } from '@hcengineering/server-core' import { getEmployee, getPersonAccountById } from '@hcengineering/server-notification' -import { getContent, isAllowed } from '@hcengineering/server-notification-resources' +import { getContent, isAllowed, sendEmailNotification } from '@hcengineering/server-notification-resources' async function getOldDepartment ( currentTx: TxMixin | TxUpdateDoc, @@ -221,13 +221,13 @@ export async function OnEmployeeDeactivate (tx: Tx, control: TriggerControl): Pr ) } -async function getEmailNotification ( +async function sendEmailNotifications ( control: TriggerControl, sender: PersonAccount, doc: Request | PublicHoliday, space: Ref, type: Ref -): Promise { +): Promise { const contacts = new Set>() const departments = await buildHierarchy(space, control) for (const department of departments) { @@ -257,25 +257,11 @@ async function getEmailNotification ( const senderName = senderPerson !== undefined ? formatName(senderPerson.name) : '' const content = await getContent(doc, senderName, type, control, '') - if (content === undefined) return [] + if (content === undefined) return - const res: Tx[] = [] for (const channel of channels) { - const tx = control.txFactory.createTxCreateDoc( - notification.class.EmailNotification, - notification.space.Notifications, - { - status: 'new', - sender: senderName, - receivers: [channel.value], - subject: content.subject, - text: content.text, - html: content.html - } - ) - res.push(tx) + await sendEmailNotification(content.text, content.html, content.subject, channel.value) } - return res } /** @@ -289,13 +275,14 @@ export async function OnRequestCreate (tx: Tx, control: TriggerControl): Promise const request = TxProcessor.createDoc2Doc(ctx) - return await getEmailNotification( + await sendEmailNotifications( control, sender, request, ctx.objectSpace as Ref, hr.ids.CreateRequestNotification ) + return [] } /** @@ -310,13 +297,14 @@ export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise const request = (await control.findAll(hr.class.Request, { _id: ctx.objectId }))[0] as Request if (request === undefined) return [] - return await getEmailNotification( + await sendEmailNotifications( control, sender, request, ctx.objectSpace as Ref, hr.ids.UpdateRequestNotification ) + return [] } /** @@ -331,13 +319,14 @@ export async function OnRequestRemove (tx: Tx, control: TriggerControl): Promise const request = control.removedMap.get(ctx.objectId) as Request if (request === undefined) return [] - return await getEmailNotification( + await sendEmailNotifications( control, sender, request, ctx.objectSpace as Ref, hr.ids.RemoveRequestNotification ) + return [] } /** @@ -388,13 +377,14 @@ export async function OnPublicHolidayCreate (tx: Tx, control: TriggerControl): P if (employee === undefined) return [] const publicHoliday = TxProcessor.createDoc2Doc(ctx) - return await getEmailNotification( + await sendEmailNotifications( control, sender, publicHoliday, publicHoliday.department, hr.ids.CreatePublicHolidayNotification ) + return [] } /** diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index 26ac69b559..d49d0f2ea5 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -38,25 +38,24 @@ import core, { TxProcessor, TxRemoveDoc, TxUpdateDoc, - generateId, + concatLink, matchQuery } from '@hcengineering/core' import notification, { ClassCollaborators, Collaborators, - DocUpdates, DocUpdateTx, - EmailNotification, + DocUpdates, NotificationProvider, NotificationType } from '@hcengineering/notification' -import { IntlString, getResource } from '@hcengineering/platform' +import { IntlString, getMetadata, getResource } from '@hcengineering/platform' import type { TriggerControl } from '@hcengineering/server-core' import serverNotification, { HTMLPresenter, + NotificationPresenter, TextPresenter, getEmployee, - NotificationPresenter, getPersonAccount, getPersonAccountById } from '@hcengineering/server-notification' @@ -222,15 +221,14 @@ export async function getContent ( } } -async function createEmailNotificationTxes ( +async function notifyByEmail ( control: TriggerControl, - tx: Tx, type: Ref, doc: Doc | undefined, senderId: Ref, receiverId: Ref, data: string = '' -): Promise { +): Promise { const sender = (await control.modelDb.findAll(contact.class.PersonAccount, { _id: senderId }))[0] const receiver = (await control.modelDb.findAll(contact.class.PersonAccount, { _id: receiverId }))[0] @@ -238,42 +236,43 @@ async function createEmailNotificationTxes ( let senderName = '' if (sender !== undefined) { - const senderPerson = (await control.modelDb.findAll(contact.class.Person, { _id: sender.person }))[0] + const senderPerson = (await control.findAll(contact.class.Person, { _id: sender.person }))[0] senderName = senderPerson !== undefined ? formatName(senderPerson.name) : '' } const content = await getContent(doc, senderName, type, control, data) if (content !== undefined) { - return await getEmailNotificationTx(tx, senderName, content.text, content.html, content.subject, receiver) + await sendEmailNotification(content.text, content.html, content.subject, receiver.email) } } -async function getEmailNotificationTx ( - tx: Tx, - sender: string, +export async function sendEmailNotification ( text: string, html: string, subject: string, - receiver: PersonAccount -): Promise | undefined> { - return { - _id: generateId(), - objectId: generateId(), - _class: core.class.TxCreateDoc, - space: core.space.DerivedTx, - objectClass: notification.class.EmailNotification, - objectSpace: notification.space.Notifications, - modifiedOn: tx.modifiedOn, - modifiedBy: tx.modifiedBy, - attributes: { - status: 'new', - sender, - receivers: [receiver.email], - subject, - text, - html + receiver: string +): Promise { + try { + const sesURL = getMetadata(serverNotification.metadata.SesUrl) + if (sesURL === undefined || sesURL === '') { + console.log('Please provide email service url to enable email confirmations.') + return } + await fetch(concatLink(sesURL, '/send'), { + method: 'post', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + text, + html, + subject, + to: [receiver] + }) + }) + } catch (err) { + console.log('Could not send email notification', err) } } @@ -600,17 +599,13 @@ async function getNotificationTxes ( const emp = await getEmployee(acc.person as Ref, control) if (emp?.active === true) { for (const type of allowed.emails) { - const emailTx = await createEmailNotificationTxes( + await notifyByEmail( control, - originTx, type._id, object, originTx.modifiedBy as Ref, target as Ref ) - if (emailTx !== undefined) { - res.push(emailTx) - } } } return res diff --git a/server-plugins/notification/src/index.ts b/server-plugins/notification/src/index.ts index 93e94d92ca..eed065fd21 100644 --- a/server-plugins/notification/src/index.ts +++ b/server-plugins/notification/src/index.ts @@ -17,7 +17,7 @@ import contact, { Employee, Person, PersonAccount } from '@hcengineering/contact' import { Account, Class, Doc, Mixin, Ref, Tx, TxCUD } from '@hcengineering/core' import { NotificationType, NotificationContent } from '@hcengineering/notification' -import { Plugin, Resource, plugin } from '@hcengineering/platform' +import { Metadata, Plugin, Resource, plugin } from '@hcengineering/platform' import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core' /** @@ -133,6 +133,9 @@ export interface NotificationPresenter extends Class { * @public */ export default plugin(serverNotificationId, { + metadata: { + SesUrl: '' as Metadata + }, mixin: { HTMLPresenter: '' as Ref>, TextPresenter: '' as Ref>, diff --git a/server/account/src/index.ts b/server/account/src/index.ts index d34e7f2eb7..30afadb877 100644 --- a/server/account/src/index.ts +++ b/server/account/src/index.ts @@ -407,6 +407,7 @@ async function sendConfirmation (productId: string, account: Account): Promise { - const account = await createAcc(db, productId, email, password, first, last, false) + const sesURL = getMetadata(accountPlugin.metadata.SES_URL) + const account = await createAcc(db, productId, email, password, first, last, sesURL === undefined || sesURL === '') const result = { endpoint: getEndpoint(),