import * as plugins from '../../plugins.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; import { requireOpsAuth } from '../helpers/auth.js'; import { logger } from '../../logger.js'; /** * CRUD handler for operator-managed SMTP submission accounts, plus the * legacy-route retirement lever. * * Auth: admin JWT or API token with `smtp-accounts:read` / `smtp-accounts:write` * scope; every write additionally requires an admin identity when a JWT is * used. Successful admin actions are audit-logged to the ops log. */ export class SmtpAccountHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private async requireAuth( request: { identity?: interfaces.data.IIdentity; apiToken?: string }, requiredScope: interfaces.data.TApiTokenScope, ): Promise { const auth = await requireOpsAuth(this.opsServerRef, request, { scope: requiredScope, requireAdminIdentity: requiredScope.endsWith(':write'), }); return auth.userId; } private get manager() { return this.opsServerRef.dcRouterRef.smtpAccountManager; } private failure(errorArg: unknown, fallbackMessageArg: string): interfaces.requests.ISmtpAccountActionResult { return { success: false, message: errorArg instanceof Error ? errorArg.message : fallbackMessageArg, code: 'SMTP_ACCOUNT_ACTION_FAILED', }; } private registerHandlers(): void { this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getSmtpAccounts', async (dataArg) => { await this.requireAuth(dataArg, 'smtp-accounts:read'); if (!this.manager?.isStarted()) return { accounts: [] }; return { accounts: await this.manager.listAccounts() }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'createSmtpAccount', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'smtp-accounts:write'); if (!this.manager?.isStarted()) { return { success: false, message: 'SmtpAccountManager not initialized', code: 'SMTP_ACCOUNT_MANAGER_UNAVAILABLE' }; } try { const result = await this.manager.createAccount({ username: dataArg.username, description: dataArg.description, senderScope: dataArg.senderScope, recipientScope: dataArg.recipientScope, mailPolicy: dataArg.mailPolicy, createdBy: userId, }); return { success: true, warnings: result.warnings.length > 0 ? result.warnings : undefined, account: result.account, password: result.password, }; } catch (err: unknown) { return this.failure(err, 'Failed to create SMTP account'); } }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updateSmtpAccount', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'smtp-accounts:write'); if (!this.manager?.isStarted()) { return { success: false, message: 'SmtpAccountManager not initialized', code: 'SMTP_ACCOUNT_MANAGER_UNAVAILABLE' }; } try { const result = await this.manager.updateAccount(dataArg.id, { description: dataArg.description, senderScope: dataArg.senderScope, recipientScope: dataArg.recipientScope, mailPolicy: dataArg.mailPolicy, }, userId); return { success: true, warnings: result.warnings.length > 0 ? result.warnings : undefined, account: result.account, }; } catch (err: unknown) { return this.failure(err, 'Failed to update SMTP account'); } }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'toggleSmtpAccount', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'smtp-accounts:write'); if (!this.manager?.isStarted()) { return { success: false, message: 'SmtpAccountManager not initialized', code: 'SMTP_ACCOUNT_MANAGER_UNAVAILABLE' }; } try { const result = await this.manager.toggleAccount(dataArg.id, dataArg.enabled, userId); return { success: true, account: result.account }; } catch (err: unknown) { return this.failure(err, 'Failed to toggle SMTP account'); } }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'rotateSmtpAccountPassword', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'smtp-accounts:write'); if (!this.manager?.isStarted()) { return { success: false, message: 'SmtpAccountManager not initialized', code: 'SMTP_ACCOUNT_MANAGER_UNAVAILABLE' }; } try { const result = await this.manager.rotatePassword(dataArg.id, userId); return { success: true, account: result.account, password: result.password }; } catch (err: unknown) { return this.failure(err, 'Failed to rotate SMTP account password'); } }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deleteSmtpAccount', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'smtp-accounts:write'); if (!this.manager?.isStarted()) { return { success: false, message: 'SmtpAccountManager not initialized', code: 'SMTP_ACCOUNT_MANAGER_UNAVAILABLE' }; } try { await this.manager.deleteAccount(dataArg.id, userId); return { success: true }; } catch (err: unknown) { return this.failure(err, 'Failed to delete SMTP account'); } }, ), ); // Internal-relay retirement lever: removes one persisted legacy route by // exact name through the standard settings restart path. Guarded in // EmailSettingsManager (unknown names and generated managed names fail). this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'removeLegacyEmailRoute', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'smtp-accounts:write'); const routeName = (dataArg.routeName || '').trim(); logger.log('info', `removeLegacyEmailRoute invoked by ${userId} for route '${routeName}'`); if (!routeName) { return { success: false, message: 'routeName is required', code: 'SMTP_ACCOUNT_ACTION_FAILED' }; } if (!this.opsServerRef.dcRouterRef.emailSettingsManager) { return { success: false, message: 'EmailSettingsManager not initialized', code: 'EMAIL_SETTINGS_MANAGER_UNAVAILABLE' }; } try { await this.opsServerRef.dcRouterRef.updateEmailServerSettings( { removeRouteNames: [routeName] }, userId, ); logger.log('info', `Legacy email route '${routeName}' removed from persisted settings by ${userId}`); return { success: true, message: `Route '${routeName}' removed` }; } catch (err: unknown) { return this.failure(err, 'Failed to remove legacy email route'); } }, ), ); } }