diff --git a/changelog.md b/changelog.md index bf4a8b7..7b6c74b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2026-02-16 - 6.3.0 - feat(dcrouter) +add configurable baseDir and centralized path resolution; use resolved data paths for storage, cache and DNS + +- Introduce IDcRouterOptions.baseDir to allow configuring base directory for dcrouter data (defaults to ~/.serve.zone/dcrouter). +- Add DcRouter.resolvedPaths and resolvePaths(baseDir) in ts/paths.ts to centralize computation of dcrouterHomeDir, dataDir, defaultTsmDbPath, defaultStoragePath and dnsRecordsDir. +- Use resolvedPaths throughout DcRouter: default filesystem storage fsPath, CacheDb storagePath, and DNS records loading now reference resolved paths. +- Replace ensureDirectories() behavior with ensureDataDirectories(resolvedPaths) to only create data-related directories; keep legacy ensureDirectories wrapper delegating to the new function. +- Simplify paths module by removing unused legacy path constants and adding a focused API for path resolution and directory creation. +- Remove an unused import (paths) in contentscanner, cleaning up imports. + ## 2026-02-16 - 6.2.4 - fix(deps) bump @push.rocks/smartproxy to ^25.5.0 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index e93c04c..7a9b16a 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '6.2.4', + version: '6.3.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts/classes.dcrouter.ts b/ts/classes.dcrouter.ts index 3463702..587ddc2 100644 --- a/ts/classes.dcrouter.ts +++ b/ts/classes.dcrouter.ts @@ -23,9 +23,12 @@ import { MetricsManager } from './monitoring/index.js'; import { RadiusServer, type IRadiusServerConfig } from './radius/index.js'; export interface IDcRouterOptions { - /** + /** Base directory for all dcrouter data. Defaults to ~/.serve.zone/dcrouter */ + baseDir?: string; + + /** * Direct SmartProxy configuration - gives full control over HTTP/HTTPS and TCP/SNI traffic - * This is the preferred way to configure HTTP/HTTPS and general TCP/SNI traffic + * This is the preferred way to configure HTTP/HTTPS and general TCP/SNI traffic */ smartProxyConfig?: plugins.smartproxy.ISmartProxyOptions; @@ -170,6 +173,7 @@ export interface PortProxyRuleContext { export class DcRouter { public options: IDcRouterOptions; + public resolvedPaths: ReturnType; // Core services public smartProxy?: plugins.smartproxy.SmartProxy; @@ -210,10 +214,13 @@ export class DcRouter { ...optionsArg }; + // Resolve all data paths from baseDir + this.resolvedPaths = paths.resolvePaths(this.options.baseDir); + // Default storage to filesystem if not configured if (!this.options.storage) { this.options.storage = { - fsPath: plugins.path.join(paths.dcrouterHomeDir, 'storage'), + fsPath: this.resolvedPaths.defaultStoragePath, }; } @@ -372,7 +379,7 @@ export class DcRouter { // Initialize CacheDb singleton this.cacheDb = CacheDb.getInstance({ - storagePath: cacheConfig.storagePath || paths.defaultTsmDbPath, + storagePath: cacheConfig.storagePath || this.resolvedPaths.defaultTsmDbPath, dbName: cacheConfig.dbName || 'dcrouter', debug: false, }); @@ -1306,7 +1313,7 @@ export class DcRouter { try { // Ensure paths are imported - const dnsDir = paths.dnsRecordsDir; + const dnsDir = this.resolvedPaths.dnsRecordsDir; // Check if directory exists if (!plugins.fs.existsSync(dnsDir)) { @@ -1370,7 +1377,7 @@ export class DcRouter { } // Ensure necessary directories exist - paths.ensureDirectories(); + paths.ensureDataDirectories(this.resolvedPaths); // Generate DKIM keys for each email domain for (const domainConfig of this.options.emailConfig.domains) { diff --git a/ts/paths.ts b/ts/paths.ts index 5c9eb40..a739c19 100644 --- a/ts/paths.ts +++ b/ts/paths.ts @@ -1,7 +1,6 @@ import * as plugins from './plugins.js'; -// Base directories -export const baseDir = process.cwd(); +// Code/asset paths (not affected by baseDir) export const packageDir = plugins.path.join( plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../' @@ -20,35 +19,37 @@ export const dataDir = process.env.DATA_DIR // Default TsmDB path for CacheDb export const defaultTsmDbPath = plugins.path.join(dcrouterHomeDir, 'tsmdb'); -// MTA directories -export const keysDir = plugins.path.join(dataDir, 'keys'); +// DNS records directory (only surviving MTA directory reference) export const dnsRecordsDir = plugins.path.join(dataDir, 'dns'); -export const sentEmailsDir = plugins.path.join(dataDir, 'emails', 'sent'); -export const receivedEmailsDir = plugins.path.join(dataDir, 'emails', 'received'); -export const failedEmailsDir = plugins.path.join(dataDir, 'emails', 'failed'); // For failed emails -export const logsDir = plugins.path.join(dataDir, 'logs'); // For logs -// Email template directories -export const emailTemplatesDir = plugins.path.join(dataDir, 'templates', 'email'); -export const MtaAttachmentsDir = plugins.path.join(dataDir, 'attachments'); // For email attachments +/** + * Resolve all data paths from a given baseDir. + * When no baseDir is provided, falls back to ~/.serve.zone/dcrouter. + * Specific overrides (e.g. DATA_DIR env) take precedence. + */ +export function resolvePaths(baseDir?: string) { + const root = baseDir ?? plugins.path.join(plugins.os.homedir(), '.serve.zone', 'dcrouter'); + const resolvedDataDir = process.env.DATA_DIR ?? plugins.path.join(root, 'data'); + return { + dcrouterHomeDir: root, + dataDir: resolvedDataDir, + defaultTsmDbPath: plugins.path.join(root, 'tsmdb'), + defaultStoragePath: plugins.path.join(root, 'storage'), + dnsRecordsDir: plugins.path.join(resolvedDataDir, 'dns'), + }; +} -// Configuration path -export const configPath = process.env.CONFIG_PATH - ? process.env.CONFIG_PATH - : plugins.path.join(baseDir, 'config.json'); +/** + * Ensure only the data directories that are actually used exist. + */ +export function ensureDataDirectories(resolvedPaths: ReturnType) { + plugins.fsUtils.ensureDirSync(resolvedPaths.dataDir); + plugins.fsUtils.ensureDirSync(resolvedPaths.dnsRecordsDir); +} -// Create directories if they don't exist +/** + * Legacy wrapper — delegates to ensureDataDirectories with module-level defaults. + */ export function ensureDirectories() { - // Ensure data directories - plugins.fsUtils.ensureDirSync(dataDir); - plugins.fsUtils.ensureDirSync(keysDir); - plugins.fsUtils.ensureDirSync(dnsRecordsDir); - plugins.fsUtils.ensureDirSync(sentEmailsDir); - plugins.fsUtils.ensureDirSync(receivedEmailsDir); - plugins.fsUtils.ensureDirSync(failedEmailsDir); - plugins.fsUtils.ensureDirSync(logsDir); - - // Ensure email template directories - plugins.fsUtils.ensureDirSync(emailTemplatesDir); - plugins.fsUtils.ensureDirSync(MtaAttachmentsDir); -} \ No newline at end of file + ensureDataDirectories(resolvePaths()); +} diff --git a/ts/security/classes.contentscanner.ts b/ts/security/classes.contentscanner.ts index faa6108..813d56b 100644 --- a/ts/security/classes.contentscanner.ts +++ b/ts/security/classes.contentscanner.ts @@ -1,5 +1,4 @@ import * as plugins from '../plugins.js'; -import * as paths from '../paths.js'; import { logger } from '../logger.js'; import { Email, type Core } from '@push.rocks/smartmta'; type IAttachment = Core.IAttachment; diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index e93c04c..7a9b16a 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '6.2.4', + version: '6.3.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' }