diff --git a/changelog.md b/changelog.md index 2d410ac..e9b43c5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,16 @@ # Changelog +## 2025-10-24 - 1.2.1 - fix(mail/delivery) +Centralize runtime/plugin imports and switch modules to use plugins exports; unify EventEmitter usage; update Deno dependencies and small path/server refactors + +- Centralized Node and third-party imports in ts/plugins.ts and re-exported commonly used utilities (net, tls, dns, fs, smartfile, smartdns, smartmail, mailauth, uuid, ip, LRUCache, etc). +- Replaced direct EventEmitter / Node built-in imports with plugins.EventEmitter across delivery, smtpclient, routing and the unified email server to standardize runtime integration. +- Updated deno.json dependency map: added @push.rocks/smartfile, @push.rocks/smartdns, @tsclass/tsclass and ip; reordered lru-cache entry. +- Stopped exporting ./dns/index.ts from ts/index.ts (DNS is available via mail/routing) to avoid duplicate exports. +- Added keysDir alias and dnsRecordsDir in ts/paths.ts and small path-related fixes. +- Added a placeholder SmtpServer and other minor delivery/smtpserver refactors and sanitizations. +- Added a local .claude/settings.local.json for development permissions (local-only configuration). + ## 2025-10-24 - 1.2.0 - feat(plugins) Add smartmail, mailauth and uuid to Deno dependencies and export them from plugins; include local dev permissions file diff --git a/deno.json b/deno.json index 251bde7..84a11a1 100644 --- a/deno.json +++ b/deno.json @@ -41,9 +41,13 @@ "@std/crypto": "jsr:@std/crypto@^1.0.0", "@std/assert": "jsr:@std/assert@^1.0.0", "@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@latest", - "lru-cache": "npm:lru-cache@^11.0.0", + "@push.rocks/smartfile": "npm:@push.rocks/smartfile@latest", + "@push.rocks/smartdns": "npm:@push.rocks/smartdns@latest", "@push.rocks/smartmail": "npm:@push.rocks/smartmail@^2.0.0", + "@tsclass/tsclass": "npm:@tsclass/tsclass@latest", + "lru-cache": "npm:lru-cache@^11.0.0", "mailauth": "npm:mailauth@^4.0.0", - "uuid": "npm:uuid@^9.0.0" + "uuid": "npm:uuid@^9.0.0", + "ip": "npm:ip@^2.0.0" } } diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 0a7ca44..c4d26ae 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/mailer', - version: '1.2.0', + version: '1.2.1', description: 'Enterprise mail server with SMTP, HTTP API, and DNS management - built for serve.zone infrastructure' } diff --git a/ts/index.ts b/ts/index.ts index 5818ce7..3885188 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -8,5 +8,6 @@ export * from './mail/core/index.ts'; export * from './mail/delivery/index.ts'; export * from './mail/routing/index.ts'; export * from './api/index.ts'; -export * from './dns/index.ts'; export * from './config/index.ts'; + +// DNS exports are included in mail/routing, so we skip './dns/index.ts' to avoid duplication diff --git a/ts/mail/delivery/classes.delivery.queue.ts b/ts/mail/delivery/classes.delivery.queue.ts index 3e5273d..e3440dd 100644 --- a/ts/mail/delivery/classes.delivery.queue.ts +++ b/ts/mail/delivery/classes.delivery.queue.ts @@ -1,7 +1,4 @@ import * as plugins from '../../plugins.ts'; -import { EventEmitter } from 'node:events'; -import * as fs from 'node:fs'; -import * as path from 'node:path'; import { logger } from '../../logger.ts'; import { type EmailProcessingMode } from '../routing/classes.email.config.ts'; import type { IEmailRoute } from '../routing/interfaces.ts'; @@ -74,7 +71,7 @@ export interface IQueueStats { /** * A unified queue for all email modes */ -export class UnifiedDeliveryQueue extends EventEmitter { +export class UnifiedDeliveryQueue extends plugins.EventEmitter { private options: Required; private queue: Map = new Map(); private checkTimer?: NodeJS.Timeout; diff --git a/ts/mail/delivery/classes.delivery.system.ts b/ts/mail/delivery/classes.delivery.system.ts index 115e56d..af4cc56 100644 --- a/ts/mail/delivery/classes.delivery.system.ts +++ b/ts/mail/delivery/classes.delivery.system.ts @@ -1,7 +1,4 @@ import * as plugins from '../../plugins.ts'; -import { EventEmitter } from 'node:events'; -import * as net from 'node:net'; -import * as tls from 'node:tls'; import { logger } from '../../logger.ts'; import { SecurityLogger, @@ -100,7 +97,7 @@ export interface IDeliveryStats { /** * Handles delivery for all email processing modes */ -export class MultiModeDeliverySystem extends EventEmitter { +export class MultiModeDeliverySystem extends plugins.EventEmitter { private queue: UnifiedDeliveryQueue; private options: Required; private stats: IDeliveryStats; diff --git a/ts/mail/delivery/classes.unified.rate.limiter.ts b/ts/mail/delivery/classes.unified.rate.limiter.ts index ad05d51..9f27a41 100644 --- a/ts/mail/delivery/classes.unified.rate.limiter.ts +++ b/ts/mail/delivery/classes.unified.rate.limiter.ts @@ -1,5 +1,4 @@ import * as plugins from '../../plugins.ts'; -import { EventEmitter } from 'node:events'; import { logger } from '../../logger.ts'; import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.ts'; @@ -84,7 +83,7 @@ export interface IRateLimitResult { /** * Unified rate limiter for all email processing modes */ -export class UnifiedRateLimiter extends EventEmitter { +export class UnifiedRateLimiter extends plugins.EventEmitter { private config: IHierarchicalRateLimits; private counters: Map = new Map(); private patternCounters: Map = new Map(); diff --git a/ts/mail/delivery/placeholder.ts b/ts/mail/delivery/placeholder.ts index 61d0529..0d6864e 100644 --- a/ts/mail/delivery/placeholder.ts +++ b/ts/mail/delivery/placeholder.ts @@ -6,3 +6,9 @@ export class DeliveryPlaceholder { // Placeholder for delivery functionality } + +export class SmtpServer { + // Placeholder SMTP server + async start() {} + async stop() {} +} diff --git a/ts/mail/delivery/smtpclient/command-handler.ts b/ts/mail/delivery/smtpclient/command-handler.ts index 16ad698..379230e 100644 --- a/ts/mail/delivery/smtpclient/command-handler.ts +++ b/ts/mail/delivery/smtpclient/command-handler.ts @@ -3,7 +3,7 @@ * SMTP command sending and response parsing */ -import { EventEmitter } from 'node:events'; +import * as plugins from '../../../plugins.ts'; import { SMTP_COMMANDS, SMTP_CODES, LINE_ENDINGS } from './constants.ts'; import type { ISmtpConnection, @@ -19,7 +19,7 @@ import { } from './utils/helpers.ts'; import { logCommand, logDebug } from './utils/logging.ts'; -export class CommandHandler extends EventEmitter { +export class CommandHandler extends plugins.EventEmitter { private options: ISmtpClientOptions; private responseBuffer: string = ''; private pendingCommand: { resolve: Function; reject: Function; command: string } | null = null; diff --git a/ts/mail/delivery/smtpclient/connection-manager.ts b/ts/mail/delivery/smtpclient/connection-manager.ts index 78163a1..42d8d18 100644 --- a/ts/mail/delivery/smtpclient/connection-manager.ts +++ b/ts/mail/delivery/smtpclient/connection-manager.ts @@ -3,20 +3,18 @@ * Connection pooling and lifecycle management */ -import * as net from 'node:net'; -import * as tls from 'node:tls'; -import { EventEmitter } from 'node:events'; +import * as plugins from '../../../plugins.ts'; import { DEFAULTS, CONNECTION_STATES } from './constants.ts'; -import type { - ISmtpClientOptions, - ISmtpConnection, +import type { + ISmtpClientOptions, + ISmtpConnection, IConnectionPoolStatus, - ConnectionState + ConnectionState } from './interfaces.ts'; import { logConnection, logDebug } from './utils/logging.ts'; import { generateConnectionId } from './utils/helpers.ts'; -export class ConnectionManager extends EventEmitter { +export class ConnectionManager extends plugins.EventEmitter { private options: ISmtpClientOptions; private connections: Map = new Map(); private pendingConnections: Set = new Set(); diff --git a/ts/mail/delivery/smtpclient/smtp-client.ts b/ts/mail/delivery/smtpclient/smtp-client.ts index e3f3844..2810c0f 100644 --- a/ts/mail/delivery/smtpclient/smtp-client.ts +++ b/ts/mail/delivery/smtpclient/smtp-client.ts @@ -3,7 +3,7 @@ * Main client class with delegation to handlers */ -import { EventEmitter } from 'node:events'; +import * as plugins from '../../../plugins.ts'; import type { Email } from '../../core/classes.email.ts'; import type { ISmtpClientOptions, @@ -30,7 +30,7 @@ interface ISmtpClientDependencies { errorHandler: SmtpErrorHandler; } -export class SmtpClient extends EventEmitter { +export class SmtpClient extends plugins.EventEmitter { private options: ISmtpClientOptions; private connectionManager: ConnectionManager; private commandHandler: CommandHandler; diff --git a/ts/mail/delivery/smtpserver/certificate-utils.ts b/ts/mail/delivery/smtpserver/certificate-utils.ts index 396ab27..0c3e7ef 100644 --- a/ts/mail/delivery/smtpserver/certificate-utils.ts +++ b/ts/mail/delivery/smtpserver/certificate-utils.ts @@ -3,17 +3,16 @@ * Provides utilities for managing TLS certificates */ -import * as fs from 'fs'; -import * as tls from 'tls'; +import * as plugins from '../../../plugins.ts'; import { SmtpLogger } from './utils/logging.ts'; /** * Certificate data */ export interface ICertificateData { - key: Buffer; - cert: Buffer; - ca?: Buffer; + key: plugins.Buffer; + cert: plugins.Buffer; + ca?: plugins.Buffer; } /** @@ -155,7 +154,7 @@ export function loadCertificatesFromString(options: { const caBuffer = caStr ? Buffer.from(caStr, 'utf8') : undefined; // Test the certificates first - const secureContext = tls.createSecureContext({ + const secureContext = plugins.tls.createSecureContext({ key: keyBuffer, cert: certBuffer, ca: caBuffer @@ -206,7 +205,7 @@ export function loadCertificatesFromString(options: { // Validate the certificates by attempting to create a secure context try { - const secureContext = tls.createSecureContext({ + const secureContext = plugins.tls.createSecureContext({ key: keyBuffer, cert: certBuffer, ca: caBuffer @@ -253,9 +252,9 @@ export function loadCertificatesFromFiles(options: { }): ICertificateData { try { // Read files directly as Buffers - const key = fs.readFileSync(options.keyPath); - const cert = fs.readFileSync(options.certPath); - const ca = options.caPath ? fs.readFileSync(options.caPath) : undefined; + const key = plugins.fs.readFileSync(options.keyPath); + const cert = plugins.fs.readFileSync(options.certPath); + const ca = options.caPath ? plugins.fs.readFileSync(options.caPath) : undefined; // Log for debugging SmtpLogger.debug('Certificate file properties', { @@ -266,7 +265,7 @@ export function loadCertificatesFromFiles(options: { // Validate the certificates by attempting to create a secure context try { - const secureContext = tls.createSecureContext({ + const secureContext = plugins.tls.createSecureContext({ key, cert, ca @@ -364,8 +363,8 @@ ORWZbz+8rBL0JIeA7eFxEA== export function createTlsOptions( certificates: ICertificateData, isServer: boolean = true -): tls.TlsOptions { - const options: tls.TlsOptions = { +): plugins.tls.TlsOptions { + const options: plugins.tls.TlsOptions = { key: certificates.key, cert: certificates.cert, ca: certificates.ca, diff --git a/ts/mail/delivery/smtpserver/data-handler.ts b/ts/mail/delivery/smtpserver/data-handler.ts index 74ece6f..0add00b 100644 --- a/ts/mail/delivery/smtpserver/data-handler.ts +++ b/ts/mail/delivery/smtpserver/data-handler.ts @@ -4,8 +4,6 @@ */ import * as plugins from '../../../plugins.ts'; -import * as fs from 'fs'; -import * as path from 'path'; import { SmtpState } from './interfaces.ts'; import type { ISmtpSession, ISmtpTransactionResult } from './interfaces.ts'; import type { IDataHandler, ISmtpServer } from './interfaces.ts'; diff --git a/ts/mail/routing/classes.email.router.ts b/ts/mail/routing/classes.email.router.ts index 1d8569e..2315d67 100644 --- a/ts/mail/routing/classes.email.router.ts +++ b/ts/mail/routing/classes.email.router.ts @@ -1,12 +1,11 @@ import * as plugins from '../../plugins.ts'; -import { EventEmitter } from 'node:events'; import type { IEmailRoute, IEmailMatch, IEmailAction, IEmailContext } from './interfaces.ts'; import type { Email } from '../core/classes.email.ts'; /** * Email router that evaluates routes and determines actions */ -export class EmailRouter extends EventEmitter { +export class EmailRouter extends plugins.EventEmitter { private routes: IEmailRoute[]; private patternCache: Map = new Map(); private storageManager?: any; // StorageManager instance diff --git a/ts/mail/routing/classes.unified.email.server.ts b/ts/mail/routing/classes.unified.email.server.ts index 7306d49..175ceed 100644 --- a/ts/mail/routing/classes.unified.email.server.ts +++ b/ts/mail/routing/classes.unified.email.server.ts @@ -1,6 +1,5 @@ import * as plugins from '../../plugins.ts'; import * as paths from '../../paths.ts'; -import { EventEmitter } from 'events'; import { logger } from '../../logger.ts'; import { SecurityLogger, @@ -154,7 +153,7 @@ export interface IServerStats { /** * Unified email server that handles all email traffic with pattern-based routing */ -export class UnifiedEmailServer extends EventEmitter { +export class UnifiedEmailServer extends plugins.EventEmitter { private dcRouter: DcRouter; private options: IUnifiedEmailServerOptions; private emailRouter: EmailRouter; diff --git a/ts/paths.ts b/ts/paths.ts index bb34233..bdc7163 100644 --- a/ts/paths.ts +++ b/ts/paths.ts @@ -19,3 +19,9 @@ export const logsDir = plugins.path.join(configDir, 'logs'); // DKIM keys directory export const dkimKeysDir = plugins.path.join(configDir, 'dkim-keys'); + +// Keys directory (alias for compatibility) +export const keysDir = dkimKeysDir; + +// DNS records directory +export const dnsRecordsDir = plugins.path.join(configDir, 'dns-records'); diff --git a/ts/plugins.ts b/ts/plugins.ts index 98f3742..7bf3884 100644 --- a/ts/plugins.ts +++ b/ts/plugins.ts @@ -10,19 +10,7 @@ export * as cli from '@std/cli'; export { serveDir } from '@std/http/file-server'; export * as crypto from '@std/crypto'; -// Cloudflare API client -import * as cloudflareImport from '@apiclient.xyz/cloudflare'; -export const cloudflare = cloudflareImport; - -// Email libraries -import * as smartmail from '@push.rocks/smartmail'; -import * as mailauth from 'mailauth'; -import * as uuid from 'uuid'; - -export { smartmail, mailauth, uuid }; - -// Node.js compatibility - needed for SMTP and email processing -// We import these as npm: specifiers for Node.js modules that don't have Deno equivalents +// Node.js built-in modules (needed for SMTP and email processing) import { EventEmitter } from 'node:events'; import * as net from 'node:net'; import * as tls from 'node:tls'; @@ -31,9 +19,32 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as process from 'node:process'; import * as buffer from 'node:buffer'; +import * as util from 'node:util'; -export { EventEmitter }; -export { net, tls, dns, fs, os, process, buffer }; - -// Re-export Buffer for convenience +export { EventEmitter, net, tls, dns, fs, os, process, buffer, util }; export const Buffer = buffer.Buffer; + +// Cloudflare API client +import * as cloudflareImport from '@apiclient.xyz/cloudflare'; +export const cloudflare = cloudflareImport; + +// @push.rocks packages +import * as smartfile from '@push.rocks/smartfile'; +import * as smartdns from '@push.rocks/smartdns'; +import * as smartmail from '@push.rocks/smartmail'; + +export { smartfile, smartdns, smartmail }; + +// @tsclass packages +import * as tsclass from '@tsclass/tsclass'; + +export { tsclass }; + +// Third-party libraries +import * as mailauth from 'mailauth'; +import { dkimSign } from 'mailauth/lib/dkim/sign.js'; +import * as uuid from 'uuid'; +import * as ip from 'ip'; +import { LRUCache } from 'lru-cache'; + +export { mailauth, dkimSign, uuid, ip, LRUCache };