From ed5983a9f850dfa3eb143070119baea09bc1a0f6 Mon Sep 17 00:00:00 2001 From: Artyom Savchenko Date: Wed, 19 Mar 2025 12:56:09 +0700 Subject: [PATCH] UBERF-9661: Use MAIL_URL env for mail integration (#8272) * UBERF-9661: Add mail url env Signed-off-by: Artem Savchenko * UBERF-9661: Use MAIL_URL env Signed-off-by: Artem Savchenko * UBERF-9661: Fix test Signed-off-by: Artem Savchenko * UBERF-9661: Remove SES_URL Signed-off-by: Artem Savchenko * UBERF-9661: Update configs Signed-off-by: Artem Savchenko --------- Signed-off-by: Artem Savchenko --- .vscode/launch.json | 8 ++-- dev/docker-compose.yaml | 10 ++--- dev/local-mongo/docker-compose.yaml | 4 +- pods/server/src/__start.ts | 6 +-- server-plugins/gmail-resources/src/index.ts | 10 ++--- .../notification-resources/src/push.ts | 6 +-- server-plugins/notification/src/index.ts | 4 +- server/account-service/src/index.ts | 8 ++-- server/account/src/__tests__/utils.test.ts | 30 +++++++-------- server/account/src/operations.ts | 14 +++---- server/account/src/plugin.ts | 4 +- server/account/src/utils.ts | 38 +++++++++---------- server/server/src/starter.ts | 12 +++--- 13 files changed, 77 insertions(+), 77 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 598c2370a0..861df5b22c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -190,7 +190,7 @@ "ACCOUNT_PORT": "3000", "FRONT_URL": "http://localhost:8080", "STATS_URL": "http://huly.local:4900", - "SES_URL": "", + "MAIL_URL": "", // "DB_NS": "account-2", // "WS_LIVENESS_DAYS": "1", "MINIO_ACCESS_KEY": "minioadmin", @@ -221,7 +221,7 @@ "ACCOUNT_PORT": "3003", "FRONT_URL": "http://localhost:8083", "STATS_URL": "http://huly.local:4901", - "SES_URL": "", + "MAIL_URL": "", // "DB_NS": "account-2", // "WS_LIVENESS_DAYS": "1", "MINIO_ACCESS_KEY": "minioadmin", @@ -267,7 +267,7 @@ "TRANSACTOR_URL": "ws://localhost:3333", "ACCOUNTS_URL": "http://localhost:3000", "FRONT_URL": "http://localhost:8080", - "SES_URL": "", + "MAIL_URL": "", "MINIO_ACCESS_KEY": "minioadmin", "MINIO_SECRET_KEY": "minioadmin", "MINIO_ENDPOINT": "localhost", @@ -300,7 +300,7 @@ "TRANSACTOR_URL": "ws://localhost:3332", "ACCOUNTS_URL": "http://localhost:3000", "FRONT_URL": "http://localhost:8080", - "SES_URL": "", + "MAIL_URL": "", "MINIO_ACCESS_KEY": "minioadmin", "MINIO_SECRET_KEY": "minioadmin", "MINIO_ENDPOINT": "localhost", diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index ec957b7669..b1532edd9c 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -99,7 +99,7 @@ services: - REGION_INFO=|Mongo;cockroach|CockroachDB # - REGION_INFO=cockroach|CockroachDB - TRANSACTOR_URL=ws://huly.local:3333,ws://huly.local:3332;;cockroach, - - SES_URL= + - MAIL_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://huly.local:8087 - RESERVED_DB_NAMES=telegram,gmail,github @@ -135,7 +135,7 @@ services: - SERVER_SECRET=secret - DB_URL=${MONGO_URL} - STATS_URL=http://huly.local:4900 - - SES_URL= + - MAIL_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - RESERVED_DB_NAMES=telegram,gmail,github - MODEL_ENABLED=* @@ -161,7 +161,7 @@ services: - SERVER_SECRET=secret - DB_URL=postgresql://root@huly.local:26257/defaultdb?sslmode=disable - STATS_URL=http://huly.local:4900 - - SES_URL= + - MAIL_URL= - REGION=cockroach - STORAGE_CONFIG=${STORAGE_CONFIG} - RESERVED_DB_NAMES=telegram,gmail,github @@ -259,7 +259,7 @@ services: - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://huly.local:8087 # - APM_SERVER_URL=http://apm-server:8200 - - SES_URL='' + - MAIL_URL='' - ACCOUNTS_URL=http://huly.local:3000 - LAST_NAME_FIRST=true - BRANDING_PATH=/var/cfg/branding.json @@ -294,7 +294,7 @@ services: - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://huly.local:8087 # - APM_SERVER_URL=http://apm-server:8200 - - SES_URL='' + - MAIL_URL='' - ACCOUNTS_URL=http://huly.local:3000 - LAST_NAME_FIRST=true - BRANDING_PATH=/var/cfg/branding.json diff --git a/dev/local-mongo/docker-compose.yaml b/dev/local-mongo/docker-compose.yaml index 5576d357c2..81b1caf323 100644 --- a/dev/local-mongo/docker-compose.yaml +++ b/dev/local-mongo/docker-compose.yaml @@ -42,7 +42,7 @@ services: - SERVER_SECRET=secret - MONGO_URL=mongodb://huly.local:27017?compressors=snappy - TRANSACTOR_URL=ws://transactor:3333;ws://localhost:3333 - - SES_URL= + - MAIL_URL= - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://localhost:8087 - RESERVED_DB_NAMES=telegram,gmail,github @@ -129,7 +129,7 @@ services: - FRONT_URL=http://localhost:8087 - UPLOAD_URL=http://localhost:8087/files # - APM_SERVER_URL=http://apm-server:8200 - - SES_URL='' + - MAIL_URL='' - ACCOUNTS_URL=http://account:3000 - LAST_NAME_FIRST=true - ELASTIC_INDEX_NAME=local_storage_index diff --git a/pods/server/src/__start.ts b/pods/server/src/__start.ts index 462719d31a..ce54b17827 100644 --- a/pods/server/src/__start.ts +++ b/pods/server/src/__start.ts @@ -71,9 +71,9 @@ setMetadata(contactPlugin.metadata.LastNameFirst, lastNameFirst) setMetadata(serverCore.metadata.FrontUrl, config.frontUrl) setMetadata(serverCore.metadata.FilesUrl, config.filesUrl) setMetadata(serverToken.metadata.Secret, config.serverSecret) -setMetadata(serverNotification.metadata.SesUrl, config.sesUrl ?? '') -setMetadata(serverNotification.metadata.SesAuthToken, config.sesAuthToken) -setMetadata(serverNotification.metadata.WebPushUrl, config.webPushUrl ?? config.sesUrl) +setMetadata(serverNotification.metadata.MailUrl, config.mailUrl ?? '') +setMetadata(serverNotification.metadata.MailAuthToken, config.mailAuthToken) +setMetadata(serverNotification.metadata.WebPushUrl, config.webPushUrl) setMetadata(serverTelegram.metadata.BotUrl, process.env.TELEGRAM_BOT_URL) setMetadata(serverAiBot.metadata.EndpointURL, process.env.AI_BOT_URL) setMetadata(serverCalendar.metadata.EndpointURL, process.env.CALENDAR_URL) diff --git a/server-plugins/gmail-resources/src/index.ts b/server-plugins/gmail-resources/src/index.ts index 5c348dd22f..c6e380cdee 100644 --- a/server-plugins/gmail-resources/src/index.ts +++ b/server-plugins/gmail-resources/src/index.ts @@ -112,18 +112,18 @@ export async function sendEmailNotification ( receiver: string ): Promise { try { - const sesURL = getMetadata(serverNotification.metadata.SesUrl) - if (sesURL === undefined || sesURL === '') { + const mailURL = getMetadata(serverNotification.metadata.MailUrl) + if (mailURL === undefined || mailURL === '') { ctx.error('Please provide email service url to enable email notifications.') return } - const sesAuth: string | undefined = getMetadata(serverNotification.metadata.SesAuthToken) - await fetch(concatLink(sesURL, '/send'), { + const mailAuth: string | undefined = getMetadata(serverNotification.metadata.MailAuthToken) + await fetch(concatLink(mailURL, '/send'), { method: 'post', keepalive: true, headers: { 'Content-Type': 'application/json', - ...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {}) + ...(mailAuth != null ? { Authorization: `Bearer ${mailAuth}` } : {}) }, body: JSON.stringify({ text, diff --git a/server-plugins/notification-resources/src/push.ts b/server-plugins/notification-resources/src/push.ts index b84c976f43..85baedff3b 100644 --- a/server-plugins/notification-resources/src/push.ts +++ b/server-plugins/notification-resources/src/push.ts @@ -157,7 +157,7 @@ export async function createPushNotification ( ): Promise { const pushURL: string | undefined = getMetadata(serverNotification.metadata.WebPushUrl) // TODO: Remove auth token after migration to new services - const authToken: string | undefined = getMetadata(serverNotification.metadata.SesAuthToken) + const authToken: string | undefined = getMetadata(serverNotification.metadata.MailAuthToken) if (pushURL === undefined || pushURL === '') return const userSubscriptions = subscriptions.filter((it) => it.user === target) const data: PushData = { @@ -190,7 +190,7 @@ export async function createPushNotification ( async function sendPushToSubscription ( pushURL: string, - sesAuth: string | undefined, + mailAuth: string | undefined, control: TriggerControl, targetUser: AccountUuid, subscriptions: PushSubscription[], @@ -204,7 +204,7 @@ async function sendPushToSubscription ( keepalive: true, headers: { 'Content-Type': 'application/json', - ...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {}) + ...(mailAuth != null ? { Authorization: `Bearer ${mailAuth}` } : {}) }, body: JSON.stringify({ subscriptions, diff --git a/server-plugins/notification/src/index.ts b/server-plugins/notification/src/index.ts index a182c9f2cb..b63bac063c 100644 --- a/server-plugins/notification/src/index.ts +++ b/server-plugins/notification/src/index.ts @@ -129,8 +129,8 @@ export const PUSH_NOTIFICATION_TITLE_SIZE = 80 */ export default plugin(serverNotificationId, { metadata: { - SesUrl: '' as Metadata, - SesAuthToken: '' as Metadata, + MailUrl: '' as Metadata, + MailAuthToken: '' as Metadata, WebPushUrl: '' as Metadata, InboxOnlyNotifications: '' as Metadata }, diff --git a/server/account-service/src/index.ts b/server/account-service/src/index.ts index f4be75501c..fc77971804 100644 --- a/server/account-service/src/index.ts +++ b/server/account-service/src/index.ts @@ -72,8 +72,8 @@ export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap } }) - const ses = process.env.SES_URL - const sesAuthToken = process.env.SES_AUTH_TOKEN + const mailUrl = process.env.MAIL_URL + const mailAuthToken = process.env.MAIL_AUTH_TOKEN const frontURL = process.env.FRONT_URL const productName = process.env.PRODUCT_NAME @@ -95,8 +95,8 @@ export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap setMetadata(account.metadata.ProductName, productName) setMetadata(account.metadata.OtpTimeToLiveSec, parseInt(process.env.OTP_TIME_TO_LIVE ?? '60')) setMetadata(account.metadata.OtpRetryDelaySec, parseInt(process.env.OTP_RETRY_DELAY ?? '60')) - setMetadata(account.metadata.SES_URL, ses) - setMetadata(account.metadata.SES_AUTH_TOKEN, sesAuthToken) + setMetadata(account.metadata.MAIL_URL, mailUrl) + setMetadata(account.metadata.MAIL_AUTH_TOKEN, mailAuthToken) setMetadata(account.metadata.FrontURL, frontURL) setMetadata(account.metadata.WsLivenessDays, wsLivenessDays) diff --git a/server/account/src/__tests__/utils.test.ts b/server/account/src/__tests__/utils.test.ts index 0058e455a6..928a2ae607 100644 --- a/server/account/src/__tests__/utils.test.ts +++ b/server/account/src/__tests__/utils.test.ts @@ -58,7 +58,7 @@ import { getPersonName, getInviteEmail, getFrontUrl, - getSesUrl, + getMailUrl, getSocialIdByKey, getWorkspaceInvite, loginOrSignUpWithProvider, @@ -654,9 +654,9 @@ describe('account utils', () => { return 30 case accountPlugin.metadata.OtpTimeToLiveSec: return 60 - case accountPlugin.metadata.SES_URL: + case accountPlugin.metadata.MAIL_URL: return sesUrl - case accountPlugin.metadata.SES_AUTH_TOKEN: + case accountPlugin.metadata.MAIL_AUTH_TOKEN: return sesAuth case accountPlugin.metadata.ProductName: return 'Test Product' @@ -833,9 +833,9 @@ describe('account utils', () => { mockFetch.mockResolvedValue({ ok: true }) ;(getMetadata as jest.Mock).mockImplementation((key) => { switch (key) { - case accountPlugin.metadata.SES_URL: + case accountPlugin.metadata.MAIL_URL: return 'https://ses.example.com' - case accountPlugin.metadata.SES_AUTH_TOKEN: + case accountPlugin.metadata.MAIL_AUTH_TOKEN: return 'test-auth-token' case accountPlugin.metadata.ProductName: return 'Test Product' @@ -871,14 +871,14 @@ describe('account utils', () => { ) }) - test('should throw error if SES_URL is missing', async () => { + test('should throw error if MAIL_URL is missing', async () => { ;(getMetadata as jest.Mock).mockReturnValue(undefined) await expect(sendEmailConfirmation(mockCtx, mockBranding, account, email)).rejects.toThrow( new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) ) - expect(mockCtx.error).toHaveBeenCalledWith('Please provide SES_URL to enable email confirmations.') + expect(mockCtx.error).toHaveBeenCalledWith('Please provide MAIL_URL to enable email confirmations.') }) test('should use branding front URL if available', async () => { @@ -999,9 +999,9 @@ describe('account utils', () => { beforeEach(() => { ;(getMetadata as jest.Mock).mockImplementation((key) => { switch (key) { - case accountPlugin.metadata.SES_URL: + case accountPlugin.metadata.MAIL_URL: return 'https://ses.example.com' - case accountPlugin.metadata.SES_AUTH_TOKEN: + case accountPlugin.metadata.MAIL_AUTH_TOKEN: return 'test-token' default: return undefined @@ -1832,9 +1832,9 @@ describe('account utils', () => { beforeEach(() => { ;(getMetadata as jest.Mock).mockImplementation((key) => { switch (key) { - case accountPlugin.metadata.SES_URL: + case accountPlugin.metadata.MAIL_URL: return 'https://ses.example.com' - case accountPlugin.metadata.SES_AUTH_TOKEN: + case accountPlugin.metadata.MAIL_AUTH_TOKEN: return 'test-token' default: return undefined @@ -1847,17 +1847,17 @@ describe('account utils', () => { }) test('should return SES URL and auth token when configured', () => { - const result = getSesUrl() + const result = getMailUrl() expect(result).toEqual({ - sesURL: 'https://ses.example.com', - sesAuth: 'test-token' + mailURL: 'https://ses.example.com', + mailAuth: 'test-token' }) }) test('should throw error when SES URL not configured', () => { ;(getMetadata as jest.Mock).mockReturnValue(undefined) - expect(() => getSesUrl()).toThrow('Please provide email service url') + expect(() => getMailUrl()).toThrow('Please provide email service url') }) }) diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index 5e1c2ba580..ae5fa6c27a 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -77,7 +77,7 @@ import { getPersonName, getRegions, getRolePower, - getSesUrl, + getMailUrl, getSocialIdByKey, getWorkspaceById, getWorkspaceInfoWithStatusById, @@ -216,14 +216,14 @@ export async function signUp ( throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) } - const sesURL = getMetadata(accountPlugin.metadata.SES_URL) - const forceConfirmation = sesURL !== undefined && sesURL !== '' + const mailURL = getMetadata(accountPlugin.metadata.MAIL_URL) + const forceConfirmation = mailURL !== undefined && mailURL !== '' if (forceConfirmation) { const normalizedEmail = cleanEmail(email) await sendEmailConfirmation(ctx, branding, account, normalizedEmail) } else { - ctx.warn('Please provide SES_URL to enable sign up email confirmations.') + ctx.warn('Please provide MAIL_URL to enable sign up email confirmations.') await confirmEmail(ctx, db, account, email) } @@ -760,7 +760,7 @@ export async function requestPasswordReset ( ) } - const { sesURL, sesAuth } = getSesUrl() + const { mailURL, mailAuth } = getMailUrl() const front = getFrontUrl(branding) const token = generateToken(account.uuid, undefined, { @@ -773,11 +773,11 @@ export async function requestPasswordReset ( const html = await translate(accountPlugin.string.RecoveryHTML, { link }, lang) const subject = await translate(accountPlugin.string.RecoverySubject, {}, lang) - await fetch(concatLink(sesURL, '/send'), { + await fetch(concatLink(mailURL, '/send'), { method: 'post', headers: { 'Content-Type': 'application/json', - ...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {}) + ...(mailAuth != null ? { Authorization: `Bearer ${mailAuth}` } : {}) }, body: JSON.stringify({ text, diff --git a/server/account/src/plugin.ts b/server/account/src/plugin.ts index 967971db04..4888bf682f 100644 --- a/server/account/src/plugin.ts +++ b/server/account/src/plugin.ts @@ -11,8 +11,8 @@ export const accountId = 'account' as Plugin export const accountPlugin = plugin(accountId, { metadata: { FrontURL: '' as Metadata, - SES_URL: '' as Metadata, - SES_AUTH_TOKEN: '' as Metadata, + MAIL_URL: '' as Metadata, + MAIL_AUTH_TOKEN: '' as Metadata, ProductName: '' as Metadata, Transactors: '' as Metadata, OtpTimeToLiveSec: '' as Metadata, diff --git a/server/account/src/utils.ts b/server/account/src/utils.ts index 6a3541e7a9..435b27f7ca 100644 --- a/server/account/src/utils.ts +++ b/server/account/src/utils.ts @@ -390,12 +390,12 @@ export async function sendOtpEmail ( otp: string, email: string ): Promise { - const sesURL = getMetadata(accountPlugin.metadata.SES_URL) - if (sesURL === undefined || sesURL === '') { + const mailURL = getMetadata(accountPlugin.metadata.MAIL_URL) + if (mailURL === undefined || mailURL === '') { ctx.error('Please provide email service url to enable email otp') return } - const sesAuth = getMetadata(accountPlugin.metadata.SES_AUTH_TOKEN) + const mailAuth = getMetadata(accountPlugin.metadata.MAIL_AUTH_TOKEN) const lang = branding?.language const app = branding?.title ?? getMetadata(accountPlugin.metadata.ProductName) @@ -405,11 +405,11 @@ export async function sendOtpEmail ( const subject = await translate(accountPlugin.string.OtpSubject, { code: otp, app }, lang) const to = email - await fetch(concatLink(sesURL, '/send'), { + await fetch(concatLink(mailURL, '/send'), { method: 'POST', headers: { 'Content-Type': 'application/json', - ...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {}) + ...(mailAuth != null ? { Authorization: `Bearer ${mailAuth}` } : {}) }, body: JSON.stringify({ text, @@ -756,13 +756,13 @@ export async function sendEmailConfirmation ( account: PersonUuid, email: string ): Promise { - const sesURL = getMetadata(accountPlugin.metadata.SES_URL) - if (sesURL === undefined || sesURL === '') { - ctx.error('Please provide SES_URL to enable email confirmations.') + const mailURL = getMetadata(accountPlugin.metadata.MAIL_URL) + if (mailURL === undefined || mailURL === '') { + ctx.error('Please provide MAIL_URL to enable email confirmations.') throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) } - const sesAuth = getMetadata(accountPlugin.metadata.SES_AUTH_TOKEN) + const mailAuth = getMetadata(accountPlugin.metadata.MAIL_AUTH_TOKEN) const front = branding?.front ?? getMetadata(accountPlugin.metadata.FrontURL) if (front === undefined || front === '') { @@ -782,11 +782,11 @@ export async function sendEmailConfirmation ( const html = await translate(accountPlugin.string.ConfirmationHTML, { name, link }, lang) const subject = await translate(accountPlugin.string.ConfirmationSubject, { name }, lang) - await fetch(concatLink(sesURL, '/send'), { + await fetch(concatLink(mailURL, '/send'), { method: 'post', headers: { 'Content-Type': 'application/json', - ...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {}) + ...(mailAuth != null ? { Authorization: `Bearer ${mailAuth}` } : {}) }, body: JSON.stringify({ text, @@ -893,15 +893,15 @@ export async function getEmailSocialId (db: AccountDB, email: string): Promise { const { text, html, subject, to } = info - const { sesURL, sesAuth } = getSesUrl() - await fetch(concatLink(sesURL, '/send'), { + const { mailURL, mailAuth } = getMailUrl() + await fetch(concatLink(mailURL, '/send'), { method: 'post', headers: { 'Content-Type': 'application/json', - ...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {}) + ...(mailAuth != null ? { Authorization: `Bearer ${mailAuth}` } : {}) }, body: JSON.stringify({ text, diff --git a/server/server/src/starter.ts b/server/server/src/starter.ts index 22b0cec3f9..de682be0e6 100644 --- a/server/server/src/starter.ts +++ b/server/server/src/starter.ts @@ -5,8 +5,8 @@ export interface ServerEnv { serverSecret: string frontUrl: string filesUrl: string | undefined - sesUrl: string | undefined - sesAuthToken: string | undefined + mailUrl: string | undefined + mailAuthToken: string | undefined webPushUrl: string | undefined accountsUrl: string serverPort: number @@ -45,8 +45,8 @@ export function serverConfigFromEnv (): ServerEnv { } const filesUrl = process.env.FILES_URL - const sesUrl = process.env.SES_URL - const sesAuthToken = process.env.SES_AUTH_TOKEN + const mailUrl = process.env.MAIL_URL + const mailAuthToken = process.env.MAIL_AUTH_TOKEN const webPushUrl = process.env.WEB_PUSH_URL const accountsUrl = process.env.ACCOUNTS_URL @@ -64,8 +64,8 @@ export function serverConfigFromEnv (): ServerEnv { serverSecret, frontUrl, filesUrl, - sesUrl, - sesAuthToken, + mailUrl, + mailAuthToken, webPushUrl, accountsUrl, serverPort,