@serve.zone/dcrouter 13.7.1 → 13.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist_serve/bundle.js +1763 -1519
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/acme/index.d.ts +1 -0
  4. package/dist_ts/acme/index.js +2 -0
  5. package/dist_ts/acme/manager.acme-config.d.ts +48 -0
  6. package/dist_ts/acme/manager.acme-config.js +156 -0
  7. package/dist_ts/classes.dcrouter.d.ts +2 -0
  8. package/dist_ts/classes.dcrouter.js +60 -21
  9. package/dist_ts/db/documents/classes.acme-config.doc.d.ts +22 -0
  10. package/dist_ts/db/documents/classes.acme-config.doc.js +121 -0
  11. package/dist_ts/db/documents/index.d.ts +1 -0
  12. package/dist_ts/db/documents/index.js +3 -1
  13. package/dist_ts/dns/manager.dns.d.ts +17 -15
  14. package/dist_ts/dns/manager.dns.js +33 -27
  15. package/dist_ts/dns/providers/factory.js +10 -1
  16. package/dist_ts/opsserver/classes.opsserver.d.ts +1 -0
  17. package/dist_ts/opsserver/classes.opsserver.js +3 -1
  18. package/dist_ts/opsserver/handlers/acme-config.handler.d.ts +16 -0
  19. package/dist_ts/opsserver/handlers/acme-config.handler.js +77 -0
  20. package/dist_ts/opsserver/handlers/dns-provider.handler.js +42 -5
  21. package/dist_ts/opsserver/handlers/domain.handler.js +3 -3
  22. package/dist_ts/opsserver/handlers/index.d.ts +1 -0
  23. package/dist_ts/opsserver/handlers/index.js +2 -1
  24. package/dist_ts_interfaces/data/acme-config.d.ts +25 -0
  25. package/dist_ts_interfaces/data/acme-config.js +2 -0
  26. package/dist_ts_interfaces/data/dns-provider.d.ts +28 -4
  27. package/dist_ts_interfaces/data/dns-provider.js +15 -1
  28. package/dist_ts_interfaces/data/dns-record.d.ts +9 -7
  29. package/dist_ts_interfaces/data/domain.d.ts +8 -7
  30. package/dist_ts_interfaces/data/index.d.ts +1 -0
  31. package/dist_ts_interfaces/data/index.js +2 -1
  32. package/dist_ts_interfaces/data/route-management.d.ts +1 -1
  33. package/dist_ts_interfaces/requests/acme-config.d.ts +42 -0
  34. package/dist_ts_interfaces/requests/acme-config.js +2 -0
  35. package/dist_ts_interfaces/requests/dns-records.d.ts +1 -1
  36. package/dist_ts_interfaces/requests/domains.d.ts +3 -3
  37. package/dist_ts_interfaces/requests/index.d.ts +1 -0
  38. package/dist_ts_interfaces/requests/index.js +2 -1
  39. package/dist_ts_migrations/index.js +17 -1
  40. package/dist_ts_web/00_commitinfo_data.js +1 -1
  41. package/dist_ts_web/appstate.d.ts +16 -1
  42. package/dist_ts_web/appstate.js +61 -2
  43. package/dist_ts_web/elements/domains/dns-provider-form.d.ts +4 -2
  44. package/dist_ts_web/elements/domains/dns-provider-form.js +11 -5
  45. package/dist_ts_web/elements/domains/ops-view-certificates.d.ts +3 -0
  46. package/dist_ts_web/elements/domains/ops-view-certificates.js +208 -4
  47. package/dist_ts_web/elements/domains/ops-view-dns.js +3 -3
  48. package/dist_ts_web/elements/domains/ops-view-domains.d.ts +1 -1
  49. package/dist_ts_web/elements/domains/ops-view-domains.js +10 -10
  50. package/dist_ts_web/elements/domains/ops-view-providers.js +19 -5
  51. package/package.json +3 -3
  52. package/ts/00_commitinfo_data.ts +1 -1
  53. package/ts/acme/index.ts +1 -0
  54. package/ts/acme/manager.acme-config.ts +182 -0
  55. package/ts/classes.dcrouter.ts +74 -26
  56. package/ts/db/documents/classes.acme-config.doc.ts +49 -0
  57. package/ts/db/documents/index.ts +3 -0
  58. package/ts/dns/manager.dns.ts +38 -27
  59. package/ts/dns/providers/factory.ts +11 -0
  60. package/ts/opsserver/classes.opsserver.ts +2 -0
  61. package/ts/opsserver/handlers/acme-config.handler.ts +94 -0
  62. package/ts/opsserver/handlers/dns-provider.handler.ts +41 -3
  63. package/ts/opsserver/handlers/domain.handler.ts +2 -2
  64. package/ts/opsserver/handlers/index.ts +2 -1
  65. package/ts_web/00_commitinfo_data.ts +1 -1
  66. package/ts_web/appstate.ts +89 -1
  67. package/ts_web/elements/domains/dns-provider-form.ts +12 -4
  68. package/ts_web/elements/domains/ops-view-certificates.ts +205 -2
  69. package/ts_web/elements/domains/ops-view-dns.ts +2 -2
  70. package/ts_web/elements/domains/ops-view-domains.ts +9 -9
  71. package/ts_web/elements/domains/ops-view-providers.ts +18 -4
@@ -105,6 +105,11 @@ let OpsViewProviders = (() => {
105
105
  background: ${cssManager.bdTheme('#f3f4f6', '#1f2937')};
106
106
  color: ${cssManager.bdTheme('#4b5563', '#9ca3af')};
107
107
  }
108
+
109
+ .statusBadge.builtin {
110
+ background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
111
+ color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
112
+ }
108
113
  `,
109
114
  ];
110
115
  render() {
@@ -114,15 +119,21 @@ let OpsViewProviders = (() => {
114
119
  <div class="providersContainer">
115
120
  <dees-table
116
121
  .heading1=${'Providers'}
117
- .heading2=${'External DNS provider accounts'}
122
+ .heading2=${'Built-in dcrouter + external DNS provider accounts'}
118
123
  .data=${providers}
119
124
  .showColumnFilters=${true}
120
125
  .displayFunction=${(p) => ({
121
126
  Name: p.name,
122
127
  Type: this.providerTypeLabel(p.type),
123
- Status: this.renderStatusBadge(p.status),
124
- 'Last Tested': p.lastTestedAt ? new Date(p.lastTestedAt).toLocaleString() : 'never',
125
- Error: p.lastError || '-',
128
+ Status: p.builtIn
129
+ ? html `<span class="statusBadge builtin">built-in</span>`
130
+ : this.renderStatusBadge(p.status),
131
+ 'Last Tested': p.builtIn
132
+ ? '—'
133
+ : p.lastTestedAt
134
+ ? new Date(p.lastTestedAt).toLocaleString()
135
+ : 'never',
136
+ Error: p.builtIn ? '—' : p.lastError || '-',
126
137
  })}
127
138
  .dataActions=${[
128
139
  {
@@ -145,6 +156,7 @@ let OpsViewProviders = (() => {
145
156
  name: 'Test Connection',
146
157
  iconName: 'lucide:plug',
147
158
  type: ['inRow', 'contextmenu'],
159
+ actionRelevancyCheckFunc: (p) => !p.builtIn,
148
160
  actionFunc: async (actionData) => {
149
161
  const provider = actionData.item;
150
162
  await this.testProvider(provider);
@@ -154,6 +166,7 @@ let OpsViewProviders = (() => {
154
166
  name: 'Edit',
155
167
  iconName: 'lucide:pencil',
156
168
  type: ['inRow', 'contextmenu'],
169
+ actionRelevancyCheckFunc: (p) => !p.builtIn,
157
170
  actionFunc: async (actionData) => {
158
171
  const provider = actionData.item;
159
172
  await this.showEditDialog(provider);
@@ -163,6 +176,7 @@ let OpsViewProviders = (() => {
163
176
  name: 'Delete',
164
177
  iconName: 'lucide:trash2',
165
178
  type: ['inRow', 'contextmenu'],
179
+ actionRelevancyCheckFunc: (p) => !p.builtIn,
166
180
  actionFunc: async (actionData) => {
167
181
  const provider = actionData.item;
168
182
  await this.deleteProvider(provider);
@@ -313,4 +327,4 @@ let OpsViewProviders = (() => {
313
327
  return OpsViewProviders = _classThis;
314
328
  })();
315
329
  export { OpsViewProviders };
316
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctcHJvdmlkZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL2RvbWFpbnMvb3BzLXZpZXctcHJvdmlkZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEVBQ0wsV0FBVyxFQUNYLElBQUksRUFDSixhQUFhLEVBRWIsR0FBRyxFQUNILEtBQUssRUFDTCxVQUFVLEdBQ1gsTUFBTSw2QkFBNkIsQ0FBQztBQUNyQyxPQUFPLEtBQUssUUFBUSxNQUFNLG1CQUFtQixDQUFDO0FBQzlDLE9BQU8sS0FBSyxVQUFVLE1BQU0sc0NBQXNDLENBQUM7QUFDbkUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQy9DLE9BQU8sd0JBQXdCLENBQUM7SUFVbkIsZ0JBQWdCOzRCQUQ1QixhQUFhLENBQUMsb0JBQW9CLENBQUM7Ozs7c0JBQ0UsV0FBVzs7OztnQ0FBbkIsU0FBUSxXQUFXOzs7O3dDQUM5QyxLQUFLLEVBQUU7WUFDUix5TEFBUyxZQUFZLDZCQUFaLFlBQVksbUdBQWlFO1lBRnhGLDZLQXFRQzs7OztRQW5RQyxxRkFBZ0QsUUFBUSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRyxFQUFDO1FBQXRGLElBQVMsWUFBWSxrREFBaUU7UUFBdEYsSUFBUyxZQUFZLHdEQUFpRTtRQUV0RjtZQUNFLEtBQUssRUFBRSxDQUFDOztZQUNSLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtnQkFDcEUsSUFBSSxDQUFDLFlBQVksR0FBRyxRQUFRLENBQUM7WUFDL0IsQ0FBQyxDQUFDLENBQUM7WUFDSCxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUNoQztRQUVELEtB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
330
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctcHJvdmlkZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL2RvbWFpbnMvb3BzLXZpZXctcHJvdmlkZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEVBQ0wsV0FBVyxFQUNYLElBQUksRUFDSixhQUFhLEVBRWIsR0FBRyxFQUNILEtBQUssRUFDTCxVQUFVLEdBQ1gsTUFBTSw2QkFBNkIsQ0FBQztBQUNyQyxPQUFPLEtBQUssUUFBUSxNQUFNLG1CQUFtQixDQUFDO0FBQzlDLE9BQU8sS0FBSyxVQUFVLE1BQU0sc0NBQXNDLENBQUM7QUFDbkUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQy9DLE9BQU8sd0JBQXdCLENBQUM7SUFVbkIsZ0JBQWdCOzRCQUQ1QixhQUFhLENBQUMsb0JBQW9CLENBQUM7Ozs7c0JBQ0UsV0FBVzs7OztnQ0FBbkIsU0FBUSxXQUFXOzs7O3dDQUM5QyxLQUFLLEVBQUU7WUFDUix5TEFBUyxZQUFZLDZCQUFaLFlBQVksbUdBQWlFO1lBRnhGLDZLQW1SQzs7OztRQWpSQyxxRkFBZ0QsUUFBUSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRyxFQUFDO1FBQXRGLElBQVMsWUFBWSxrREFBaUU7UUFBdEYsSUFBUyxZQUFZLHdEQUFpRTtRQUV0RjtZQUNFLEtBQUssRUFBRSxDQUFDOztZQUNSLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtnQkFDcEUsSUFBSSxDQUFDLFlBQVksR0FBRyxRQUFRLENBQUM7WUFDL0IsQ0FBQyxDQUFDLENBQUM7WUFDSCxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUNoQztRQUVELEtB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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "13.7.1",
4
+ "version": "13.9.0",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "exports": {
@@ -35,7 +35,7 @@
35
35
  "@api.global/typedserver": "^8.4.6",
36
36
  "@api.global/typedsocket": "^4.1.2",
37
37
  "@apiclient.xyz/cloudflare": "^7.1.0",
38
- "@design.estate/dees-catalog": "^3.69.1",
38
+ "@design.estate/dees-catalog": "^3.70.0",
39
39
  "@design.estate/dees-element": "^2.2.4",
40
40
  "@push.rocks/lik": "^6.4.0",
41
41
  "@push.rocks/projectinfo": "^5.1.0",
@@ -49,7 +49,7 @@
49
49
  "@push.rocks/smartjwt": "^2.2.1",
50
50
  "@push.rocks/smartlog": "^3.2.2",
51
51
  "@push.rocks/smartmetrics": "^3.0.3",
52
- "@push.rocks/smartmigration": "1.1.1",
52
+ "@push.rocks/smartmigration": "1.2.0",
53
53
  "@push.rocks/smartmta": "^5.3.1",
54
54
  "@push.rocks/smartnetwork": "^4.5.2",
55
55
  "@push.rocks/smartpath": "^6.0.0",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '13.7.1',
6
+ version: '13.9.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -0,0 +1 @@
1
+ export * from './manager.acme-config.js';
@@ -0,0 +1,182 @@
1
+ import { logger } from '../logger.js';
2
+ import { AcmeConfigDoc } from '../db/documents/index.js';
3
+ import type { IDcRouterOptions } from '../classes.dcrouter.js';
4
+ import type { IAcmeConfig } from '../../ts_interfaces/data/acme-config.js';
5
+
6
+ /**
7
+ * AcmeConfigManager — owns the singleton ACME configuration in the DB.
8
+ *
9
+ * Lifecycle:
10
+ * - `start()` — loads from the DB; if empty, seeds from legacy constructor
11
+ * fields (`tls.contactEmail`, `smartProxyConfig.acme.*`) on first boot.
12
+ * - `getConfig()` — returns the in-memory cached `IAcmeConfig` (or null)
13
+ * - `updateConfig(args, updatedBy)` — upserts and refreshes the cache
14
+ *
15
+ * Reload semantics: updates take effect on the next dcrouter restart because
16
+ * `SmartAcme` is instantiated once in `setupSmartProxy()`. `renewThresholdDays`
17
+ * applies immediately to the next renewal check. See
18
+ * `ts_web/elements/domains/ops-view-certificates.ts` for the UI warning.
19
+ */
20
+ export class AcmeConfigManager {
21
+ private cached: IAcmeConfig | null = null;
22
+
23
+ constructor(private options: IDcRouterOptions) {}
24
+
25
+ public async start(): Promise<void> {
26
+ logger.log('info', 'AcmeConfigManager: starting');
27
+ let doc = await AcmeConfigDoc.load();
28
+
29
+ if (!doc) {
30
+ // First-boot path: seed from legacy constructor fields if present.
31
+ const seed = this.deriveSeedFromOptions();
32
+ if (seed) {
33
+ doc = await this.createSeedDoc(seed);
34
+ logger.log(
35
+ 'info',
36
+ `AcmeConfigManager: seeded from constructor legacy fields (accountEmail=${seed.accountEmail}, useProduction=${seed.useProduction})`,
37
+ );
38
+ } else {
39
+ logger.log(
40
+ 'info',
41
+ 'AcmeConfigManager: no AcmeConfig in DB and no legacy constructor fields — ACME disabled until configured via Domains > Certificates > Settings.',
42
+ );
43
+ }
44
+ } else if (this.deriveSeedFromOptions()) {
45
+ logger.log(
46
+ 'warn',
47
+ 'AcmeConfigManager: ignoring constructor tls.contactEmail / smartProxyConfig.acme — DB already has AcmeConfigDoc. Manage via Domains > Certificates > Settings.',
48
+ );
49
+ }
50
+
51
+ this.cached = doc ? this.toPlain(doc) : null;
52
+ if (this.cached) {
53
+ logger.log(
54
+ 'info',
55
+ `AcmeConfigManager: loaded ACME config (accountEmail=${this.cached.accountEmail}, enabled=${this.cached.enabled}, useProduction=${this.cached.useProduction})`,
56
+ );
57
+ }
58
+ }
59
+
60
+ public async stop(): Promise<void> {
61
+ this.cached = null;
62
+ }
63
+
64
+ /**
65
+ * Returns the current ACME config, or null if not configured.
66
+ * In-memory — does not hit the DB.
67
+ */
68
+ public getConfig(): IAcmeConfig | null {
69
+ return this.cached;
70
+ }
71
+
72
+ /**
73
+ * True if there is an enabled ACME config. Used by `setupSmartProxy()` to
74
+ * decide whether to instantiate SmartAcme.
75
+ */
76
+ public hasEnabledConfig(): boolean {
77
+ return this.cached !== null && this.cached.enabled;
78
+ }
79
+
80
+ /**
81
+ * Upsert the ACME config. All fields are optional; missing fields are
82
+ * preserved from the existing row (or defaulted if there is no row yet).
83
+ */
84
+ public async updateConfig(
85
+ args: Partial<Omit<IAcmeConfig, 'updatedAt' | 'updatedBy'>>,
86
+ updatedBy: string,
87
+ ): Promise<IAcmeConfig> {
88
+ let doc = await AcmeConfigDoc.load();
89
+ const now = Date.now();
90
+
91
+ if (!doc) {
92
+ doc = new AcmeConfigDoc();
93
+ doc.configId = 'acme-config';
94
+ doc.accountEmail = args.accountEmail ?? '';
95
+ doc.enabled = args.enabled ?? true;
96
+ doc.useProduction = args.useProduction ?? true;
97
+ doc.autoRenew = args.autoRenew ?? true;
98
+ doc.renewThresholdDays = args.renewThresholdDays ?? 30;
99
+ } else {
100
+ if (args.accountEmail !== undefined) doc.accountEmail = args.accountEmail;
101
+ if (args.enabled !== undefined) doc.enabled = args.enabled;
102
+ if (args.useProduction !== undefined) doc.useProduction = args.useProduction;
103
+ if (args.autoRenew !== undefined) doc.autoRenew = args.autoRenew;
104
+ if (args.renewThresholdDays !== undefined) doc.renewThresholdDays = args.renewThresholdDays;
105
+ }
106
+
107
+ doc.updatedAt = now;
108
+ doc.updatedBy = updatedBy;
109
+ await doc.save();
110
+
111
+ this.cached = this.toPlain(doc);
112
+ return this.cached;
113
+ }
114
+
115
+ // ==========================================================================
116
+ // Internal helpers
117
+ // ==========================================================================
118
+
119
+ /**
120
+ * Build a seed object from the legacy constructor fields. Returns null
121
+ * if the user has not provided any of them.
122
+ *
123
+ * Supports BOTH `tls.contactEmail` (short form) and `smartProxyConfig.acme`
124
+ * (full form). `smartProxyConfig.acme` wins when both are present.
125
+ */
126
+ private deriveSeedFromOptions(): Omit<IAcmeConfig, 'updatedAt' | 'updatedBy'> | null {
127
+ const acme = this.options.smartProxyConfig?.acme;
128
+ const tls = this.options.tls;
129
+
130
+ // Prefer the explicit smartProxyConfig.acme block if present.
131
+ if (acme?.accountEmail) {
132
+ return {
133
+ accountEmail: acme.accountEmail,
134
+ enabled: acme.enabled !== false,
135
+ useProduction: acme.useProduction !== false,
136
+ autoRenew: acme.autoRenew !== false,
137
+ renewThresholdDays: acme.renewThresholdDays ?? 30,
138
+ };
139
+ }
140
+
141
+ // Fall back to the short tls.contactEmail form.
142
+ if (tls?.contactEmail) {
143
+ return {
144
+ accountEmail: tls.contactEmail,
145
+ enabled: true,
146
+ useProduction: true,
147
+ autoRenew: true,
148
+ renewThresholdDays: 30,
149
+ };
150
+ }
151
+
152
+ return null;
153
+ }
154
+
155
+ private async createSeedDoc(
156
+ seed: Omit<IAcmeConfig, 'updatedAt' | 'updatedBy'>,
157
+ ): Promise<AcmeConfigDoc> {
158
+ const doc = new AcmeConfigDoc();
159
+ doc.configId = 'acme-config';
160
+ doc.accountEmail = seed.accountEmail;
161
+ doc.enabled = seed.enabled;
162
+ doc.useProduction = seed.useProduction;
163
+ doc.autoRenew = seed.autoRenew;
164
+ doc.renewThresholdDays = seed.renewThresholdDays;
165
+ doc.updatedAt = Date.now();
166
+ doc.updatedBy = 'seed';
167
+ await doc.save();
168
+ return doc;
169
+ }
170
+
171
+ private toPlain(doc: AcmeConfigDoc): IAcmeConfig {
172
+ return {
173
+ accountEmail: doc.accountEmail,
174
+ enabled: doc.enabled,
175
+ useProduction: doc.useProduction,
176
+ autoRenew: doc.autoRenew,
177
+ renewThresholdDays: doc.renewThresholdDays,
178
+ updatedAt: doc.updatedAt,
179
+ updatedBy: doc.updatedBy,
180
+ };
181
+ }
182
+ }
@@ -28,6 +28,7 @@ import { RouteConfigManager, ApiTokenManager, ReferenceResolver, DbSeeder, Targe
28
28
  import { SecurityLogger, ContentScanner, IPReputationChecker } from './security/index.js';
29
29
  import { type IHttp3Config, augmentRoutesWithHttp3 } from './http3/index.js';
30
30
  import { DnsManager } from './dns/manager.dns.js';
31
+ import { AcmeConfigManager } from './acme/manager.acme-config.js';
31
32
 
32
33
  export interface IDcRouterOptions {
33
34
  /** Base directory for all dcrouter data. Defaults to ~/.serve.zone/dcrouter */
@@ -276,6 +277,9 @@ export class DcRouter {
276
277
  // Domain / DNS management (DB-backed providers, domains, records)
277
278
  public dnsManager?: DnsManager;
278
279
 
280
+ // ACME configuration (DB-backed singleton, replaces tls.contactEmail)
281
+ public acmeConfigManager?: AcmeConfigManager;
282
+
279
283
  // Auto-discovered public IP (populated by generateAuthoritativeRecords)
280
284
  public detectedPublicIp: string | null = null;
281
285
 
@@ -412,11 +416,35 @@ export class DcRouter {
412
416
  );
413
417
  }
414
418
 
415
- // SmartProxy: critical, depends on DcRouterDb + DnsManager (if enabled)
419
+ // AcmeConfigManager: optional, depends on DcRouterDb owns the singleton
420
+ // ACME configuration (accountEmail, useProduction, etc.). Must run before
421
+ // SmartProxy so setupSmartProxy() can read the ACME config from the DB.
422
+ // On first boot, seeds from legacy `tls.contactEmail` / `smartProxyConfig.acme`.
423
+ if (this.options.dbConfig?.enabled !== false) {
424
+ this.serviceManager.addService(
425
+ new plugins.taskbuffer.Service('AcmeConfigManager')
426
+ .optional()
427
+ .dependsOn('DcRouterDb')
428
+ .withStart(async () => {
429
+ this.acmeConfigManager = new AcmeConfigManager(this.options);
430
+ await this.acmeConfigManager.start();
431
+ })
432
+ .withStop(async () => {
433
+ if (this.acmeConfigManager) {
434
+ await this.acmeConfigManager.stop();
435
+ this.acmeConfigManager = undefined;
436
+ }
437
+ })
438
+ .withRetry({ maxRetries: 1, baseDelayMs: 500 }),
439
+ );
440
+ }
441
+
442
+ // SmartProxy: critical, depends on DcRouterDb + DnsManager + AcmeConfigManager (if enabled)
416
443
  const smartProxyDeps: string[] = [];
417
444
  if (this.options.dbConfig?.enabled !== false) {
418
445
  smartProxyDeps.push('DcRouterDb');
419
446
  smartProxyDeps.push('DnsManager');
447
+ smartProxyDeps.push('AcmeConfigManager');
420
448
  }
421
449
  this.serviceManager.addService(
422
450
  new plugins.taskbuffer.Service('SmartProxy')
@@ -837,45 +865,62 @@ export class DcRouter {
837
865
  }
838
866
 
839
867
  let routes: plugins.smartproxy.IRouteConfig[] = [];
840
- let acmeConfig: plugins.smartproxy.IAcmeOptions | undefined;
841
-
842
- // If user provides full SmartProxy config, use it directly
868
+
869
+ // If user provides full SmartProxy config, use its routes.
870
+ // NOTE: `smartProxyConfig.acme` is now seed-only consumed by
871
+ // AcmeConfigManager on first boot. The live ACME config always comes
872
+ // from the DB via `this.acmeConfigManager.getConfig()`.
843
873
  if (this.options.smartProxyConfig) {
844
874
  routes = this.options.smartProxyConfig.routes || [];
845
- acmeConfig = this.options.smartProxyConfig.acme;
846
- logger.log('info', `Found ${routes.length} routes in config, ACME config present: ${!!acmeConfig}`);
875
+ logger.log('info', `Found ${routes.length} routes in config`);
847
876
  }
848
-
877
+
849
878
  // If email config exists, automatically add email routes
850
879
  if (this.options.emailConfig) {
851
880
  const emailRoutes = this.generateEmailRoutes(this.options.emailConfig);
852
881
  logger.log('debug', 'Email routes generated', { routes: JSON.stringify(emailRoutes) });
853
882
  routes = [...routes, ...emailRoutes]; // Enable email routing through SmartProxy
854
883
  }
855
-
884
+
856
885
  // If DNS is configured, add DNS routes
857
886
  if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0) {
858
887
  const dnsRoutes = this.generateDnsRoutes();
859
888
  logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(dnsRoutes) });
860
889
  routes = [...routes, ...dnsRoutes];
861
890
  }
862
-
863
- // Merge TLS/ACME configuration if provided at root level
864
- if (this.options.tls && !acmeConfig) {
865
- acmeConfig = {
866
- accountEmail: this.options.tls.contactEmail,
867
- enabled: true,
868
- useProduction: true,
869
- autoRenew: true,
870
- renewThresholdDays: 30
871
- };
891
+
892
+ // Build the ACME options for SmartProxy from the DB-backed AcmeConfigManager.
893
+ // If no config exists or it's disabled, SmartProxy's own ACME is turned off
894
+ // and dcrouter's SmartAcme / certProvisionFunction are not wired.
895
+ const dbAcme = this.acmeConfigManager?.getConfig();
896
+ const acmeConfig: plugins.smartproxy.IAcmeOptions | undefined =
897
+ dbAcme && dbAcme.enabled
898
+ ? {
899
+ accountEmail: dbAcme.accountEmail,
900
+ enabled: true,
901
+ useProduction: dbAcme.useProduction,
902
+ autoRenew: dbAcme.autoRenew,
903
+ renewThresholdDays: dbAcme.renewThresholdDays,
904
+ }
905
+ : undefined;
906
+ if (acmeConfig) {
907
+ logger.log(
908
+ 'info',
909
+ `ACME config: accountEmail=${acmeConfig.accountEmail}, useProduction=${acmeConfig.useProduction}, autoRenew=${acmeConfig.autoRenew}`,
910
+ );
911
+ } else {
912
+ logger.log('info', 'ACME config: disabled or not yet configured in DB');
872
913
  }
873
-
874
- // Configure DNS-01 challenge if any DnsProviderDoc exists in the DB.
875
- // The DnsManager dispatches each challenge to the right provider client
876
- // based on the FQDN being certificated.
914
+
915
+ // Configure DNS-01 challenge if any DnsProviderDoc exists in the DB AND
916
+ // ACME is enabled. The DnsManager dispatches each challenge to the right
917
+ // provider client based on the FQDN being certificated.
877
918
  let challengeHandlers: any[] = [];
878
- if (this.dnsManager && (await this.dnsManager.hasAcmeCapableProvider())) {
919
+ if (
920
+ acmeConfig &&
921
+ this.dnsManager &&
922
+ (await this.dnsManager.hasAcmeCapableProvider())
923
+ ) {
879
924
  logger.log('info', 'Configuring DNS-01 challenge for ACME via DnsManager (DB providers)');
880
925
  const convenientDnsProvider = this.dnsManager.buildAcmeConvenientDnsProvider();
881
926
  const dns01Handler = new plugins.smartacme.handlers.Dns01Handler(convenientDnsProvider);
@@ -977,10 +1022,12 @@ export class DcRouter {
977
1022
  logger.log('error', 'Error stopping old SmartAcme', { error: String(err) })
978
1023
  );
979
1024
  }
1025
+ // Safe non-null: challengeHandlers.length > 0 implies both dnsManager
1026
+ // and acmeConfig exist (enforced above).
980
1027
  this.smartAcme = new plugins.smartacme.SmartAcme({
981
- accountEmail: acmeConfig?.accountEmail || this.options.tls?.contactEmail || 'admin@example.com',
1028
+ accountEmail: dbAcme!.accountEmail,
982
1029
  certManager: new StorageBackedCertManager(),
983
- environment: 'production',
1030
+ environment: dbAcme!.useProduction ? 'production' : 'integration',
984
1031
  challengeHandlers: challengeHandlers,
985
1032
  challengePriority: ['dns-01'],
986
1033
  });
@@ -1745,7 +1792,8 @@ export class DcRouter {
1745
1792
  logger.log('info', `Registered ${allRecords.length} DNS records (${authoritativeRecords.length} authoritative, ${emailDnsRecords.length} email, ${dkimRecords.length} DKIM, ${this.options.dnsRecords?.length || 0} user-defined)`);
1746
1793
  }
1747
1794
 
1748
- // Hand the DnsServer to DnsManager so DB-backed manual records get registered too.
1795
+ // Hand the DnsServer to DnsManager so DB-backed local records on
1796
+ // dcrouter-hosted domains get registered too.
1749
1797
  if (this.dnsManager && this.dnsServer) {
1750
1798
  await this.dnsManager.attachDnsServer(this.dnsServer);
1751
1799
  }
@@ -0,0 +1,49 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { DcRouterDb } from '../classes.dcrouter-db.js';
3
+
4
+ const getDb = () => DcRouterDb.getInstance().getDb();
5
+
6
+ /**
7
+ * Singleton ACME configuration document. One row per dcrouter instance,
8
+ * keyed on the fixed `configId = 'acme-config'` following the
9
+ * `VpnServerKeysDoc` pattern.
10
+ *
11
+ * Replaces the legacy `tls.contactEmail` and `smartProxyConfig.acme.*`
12
+ * constructor fields. Managed via the OpsServer UI at
13
+ * **Domains > Certificates > Settings**.
14
+ */
15
+ @plugins.smartdata.Collection(() => getDb())
16
+ export class AcmeConfigDoc extends plugins.smartdata.SmartDataDbDoc<AcmeConfigDoc, AcmeConfigDoc> {
17
+ @plugins.smartdata.unI()
18
+ @plugins.smartdata.svDb()
19
+ public configId: string = 'acme-config';
20
+
21
+ @plugins.smartdata.svDb()
22
+ public accountEmail: string = '';
23
+
24
+ @plugins.smartdata.svDb()
25
+ public enabled: boolean = true;
26
+
27
+ @plugins.smartdata.svDb()
28
+ public useProduction: boolean = true;
29
+
30
+ @plugins.smartdata.svDb()
31
+ public autoRenew: boolean = true;
32
+
33
+ @plugins.smartdata.svDb()
34
+ public renewThresholdDays: number = 30;
35
+
36
+ @plugins.smartdata.svDb()
37
+ public updatedAt: number = 0;
38
+
39
+ @plugins.smartdata.svDb()
40
+ public updatedBy: string = '';
41
+
42
+ constructor() {
43
+ super();
44
+ }
45
+
46
+ public static async load(): Promise<AcmeConfigDoc | null> {
47
+ return await AcmeConfigDoc.getInstance({ configId: 'acme-config' });
48
+ }
49
+ }
@@ -30,3 +30,6 @@ export * from './classes.accounting-session.doc.js';
30
30
  export * from './classes.dns-provider.doc.js';
31
31
  export * from './classes.domain.doc.js';
32
32
  export * from './classes.dns-record.doc.js';
33
+
34
+ // ACME configuration (singleton)
35
+ export * from './classes.acme-config.doc.js';