From 7bd94884f4c693994503997b8ed58f7fe50411a0 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Sun, 29 Jun 2025 18:47:44 +0000 Subject: [PATCH] update --- package.json | 2 +- pnpm-lock.yaml | 10 +-- ts/classes.dcrouter.ts | 21 +++++- .../routing/classes.unified.email.server.ts | 2 +- ts/opsserver/handlers/config.handler.ts | 13 ++++ ts_web/elements/ops-view-emails.ts | 66 +++++++++++++++---- 6 files changed, 93 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 9e7fb46..1338eea 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@api.global/typedserver": "^3.0.74", "@api.global/typedsocket": "^3.0.0", "@apiclient.xyz/cloudflare": "^6.4.1", - "@design.estate/dees-catalog": "^1.10.2", + "@design.estate/dees-catalog": "^1.10.7", "@design.estate/dees-element": "^2.0.45", "@push.rocks/projectinfo": "^5.0.1", "@push.rocks/qenv": "^6.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94cdea5..e7bee98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^6.4.1 version: 6.4.1 '@design.estate/dees-catalog': - specifier: ^1.10.2 - version: 1.10.2(@tiptap/pm@2.23.0) + specifier: ^1.10.7 + version: 1.10.7(@tiptap/pm@2.23.0) '@design.estate/dees-element': specifier: ^2.0.45 version: 2.0.45 @@ -344,8 +344,8 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - '@design.estate/dees-catalog@1.10.2': - resolution: {integrity: sha512-hLdXbRUWpyYspZFhmp/S/GtOnhMPfa03ruMOX3dhkzTKoVymp2WxSjUID0zQlv+Y/P1iNMnwIfhzGkJsInhCTQ==} + '@design.estate/dees-catalog@1.10.7': + resolution: {integrity: sha512-q5ukOfTzVaOSocrNQIOCc4sugw3flPW3RzvCfEISmRh0ziq6oadRcPcTgg8cYTOVT58C/oIWZZgsAz22W3RyZQ==} '@design.estate/dees-comms@1.0.27': resolution: {integrity: sha512-GvzTUwkV442LD60T08iqSoqvhA02Mou5lFvvqBPc4yBUiU7cZISqBx+76xvMgMIEI9Dx9JfTl4/2nW8MoVAanw==} @@ -5500,7 +5500,7 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 - '@design.estate/dees-catalog@1.10.2(@tiptap/pm@2.23.0)': + '@design.estate/dees-catalog@1.10.7(@tiptap/pm@2.23.0)': dependencies: '@design.estate/dees-domtools': 2.3.3 '@design.estate/dees-element': 2.0.45 diff --git a/ts/classes.dcrouter.ts b/ts/classes.dcrouter.ts index 7e54f79..622abb8 100644 --- a/ts/classes.dcrouter.ts +++ b/ts/classes.dcrouter.ts @@ -5,7 +5,7 @@ import * as paths from './paths.js'; // Import the email server and its configuration import { UnifiedEmailServer, type IUnifiedEmailServerOptions } from './mail/routing/classes.unified.email.server.js'; -import type { IEmailRoute } from './mail/routing/interfaces.js'; +import type { IEmailRoute, IEmailDomainConfig } from './mail/routing/interfaces.js'; import { logger } from './logger.js'; // Import the email configuration helpers directly from mail/delivery import { configureEmailStorage, configureEmailServer } from './mail/delivery/index.js'; @@ -640,9 +640,28 @@ export class DcRouter { 465: 10465 // SMTPS }; + // Transform domains if they are provided as strings + let transformedDomains = this.options.emailConfig.domains; + if (transformedDomains && transformedDomains.length > 0) { + // Check if domains are strings (for backward compatibility) + if (typeof transformedDomains[0] === 'string') { + transformedDomains = (transformedDomains as any).map((domain: string) => ({ + domain, + dnsMode: 'external-dns' as const, + dkim: { + selector: 'default', + keySize: 2048, + rotateKeys: false, + rotationInterval: 90 + } + })); + } + } + // Create config with mapped ports const emailConfig: IUnifiedEmailServerOptions = { ...this.options.emailConfig, + domains: transformedDomains, ports: this.options.emailConfig.ports.map(port => portMapping[port] || port + 10000), hostname: 'localhost' // Listen on localhost for SmartProxy forwarding }; diff --git a/ts/mail/routing/classes.unified.email.server.ts b/ts/mail/routing/classes.unified.email.server.ts index 287e489..c6158c7 100644 --- a/ts/mail/routing/classes.unified.email.server.ts +++ b/ts/mail/routing/classes.unified.email.server.ts @@ -158,7 +158,7 @@ export class UnifiedEmailServer extends EventEmitter { private dcRouter: DcRouter; private options: IUnifiedEmailServerOptions; private emailRouter: EmailRouter; - private domainRegistry: DomainRegistry; + public domainRegistry: DomainRegistry; private servers: any[] = []; private stats: IServerStats; diff --git a/ts/opsserver/handlers/config.handler.ts b/ts/opsserver/handlers/config.handler.ts index 3ac93b9..06f12c3 100644 --- a/ts/opsserver/handlers/config.handler.ts +++ b/ts/opsserver/handlers/config.handler.ts @@ -65,6 +65,7 @@ export class ConfigHandler { perHour: number; perDay: number; }; + domains?: string[]; }; dns: { enabled: boolean; @@ -88,6 +89,17 @@ export class ConfigHandler { }> { const dcRouter = this.opsServerRef.dcRouterRef; + // Get email domains if email server is configured + let emailDomains: string[] = []; + if (dcRouter.emailServer && dcRouter.emailServer.domainRegistry) { + emailDomains = dcRouter.emailServer.domainRegistry.getAllDomains(); + } else if (dcRouter.options.emailConfig?.domains) { + // Fallback: get domains from email config options + emailDomains = dcRouter.options.emailConfig.domains.map(d => + typeof d === 'string' ? d : d.domain + ); + } + return { email: { enabled: !!dcRouter.emailServer, @@ -98,6 +110,7 @@ export class ConfigHandler { perHour: 100, perDay: 1000, }, + domains: emailDomains, }, dns: { enabled: !!dcRouter.dnsServer, diff --git a/ts_web/elements/ops-view-emails.ts b/ts_web/elements/ops-view-emails.ts index ee8d271..53c78fa 100644 --- a/ts_web/elements/ops-view-emails.ts +++ b/ts_web/elements/ops-view-emails.ts @@ -50,9 +50,13 @@ export class OpsViewEmails extends DeesElement { @state() private searchTerm = ''; + @state() + private emailDomains: string[] = []; + constructor() { super(); this.loadEmails(); + this.loadEmailDomains(); } public static styles = [ @@ -436,6 +440,11 @@ export class OpsViewEmails extends DeesElement { private async openComposeModal(replyTo?: IEmail, replyAll = false, forward = false) { const { DeesModal } = await import('@design.estate/dees-catalog'); + // Ensure domains are loaded before opening modal + if (this.emailDomains.length === 0) { + await this.loadEmailDomains(); + } + await DeesModal.createAndShow({ heading: forward ? 'Forward Email' : replyTo ? 'Reply to Email' : 'New Email', width: 'large', @@ -447,18 +456,27 @@ export class OpsViewEmails extends DeesElement { const modals = document.querySelectorAll('dees-modal'); modals.forEach(m => (m as any).destroy?.()); }}> - ' }, - { key: 'noreply@dcrouter.local', value: 'No Reply ' }, - { key: 'support@dcrouter.local', value: 'Support ' }, - { key: 'alerts@dcrouter.local', value: 'Alerts ' } - ]} - .selectedKey=${'admin@dcrouter.local'} - required - > +
+ + @ + 0 + ? this.emailDomains.map(domain => ({ key: domain, value: domain })) + : [{ key: 'dcrouter.local', value: 'dcrouter.local' }]} + .selectedKey=${this.emailDomains[0] || 'dcrouter.local'} + required + style="flex: 1;" + > +
0) { + this.emailDomains = config.email.domains; + } else { + // Fallback to default domains if none configured + this.emailDomains = ['dcrouter.local']; + } + } catch (error) { + console.error('Failed to load email domains:', error); + // Fallback to default domain on error + this.emailDomains = ['dcrouter.local']; + } + } + private async refreshEmails() { this.isLoading = true; await this.loadEmails(); @@ -579,9 +616,12 @@ export class OpsViewEmails extends DeesElement { console.log('Sending email:', formData); // Add to sent folder (mock) + // Combine username and domain + const fromEmail = `${formData.fromUsername || 'admin'}@${formData.fromDomain || this.emailDomains[0] || 'dcrouter.local'}`; + const newEmail: IEmail = { id: `email-${Date.now()}`, - from: formData.from || 'admin@dcrouter.local', + from: fromEmail, to: formData.to || [], cc: formData.cc || [], bcc: formData.bcc || [],