@serve.zone/dcrouter 8.1.0 → 9.1.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 (34) hide show
  1. package/dist_serve/bundle.js +1417 -992
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/monitoring/classes.metricsmanager.d.ts +169 -0
  4. package/dist_ts/monitoring/classes.metricsmanager.js +591 -0
  5. package/dist_ts/monitoring/index.d.ts +1 -0
  6. package/dist_ts/monitoring/index.js +2 -0
  7. package/dist_ts/opsserver/handlers/certificate.handler.d.ts +34 -0
  8. package/dist_ts/opsserver/handlers/certificate.handler.js +419 -0
  9. package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +32 -0
  10. package/dist_ts/opsserver/handlers/email-ops.handler.js +226 -0
  11. package/dist_ts/opsserver/handlers/radius.handler.d.ts +8 -0
  12. package/dist_ts/opsserver/handlers/radius.handler.js +296 -0
  13. package/dist_ts/opsserver/handlers/remoteingress.handler.d.ts +8 -0
  14. package/dist_ts/opsserver/handlers/remoteingress.handler.js +154 -0
  15. package/dist_ts/opsserver/handlers/security.handler.d.ts +11 -0
  16. package/dist_ts/opsserver/handlers/security.handler.js +232 -0
  17. package/dist_ts/opsserver/handlers/stats.handler.d.ts +13 -0
  18. package/dist_ts/opsserver/handlers/stats.handler.js +400 -0
  19. package/dist_ts/security/classes.securitylogger.d.ts +140 -0
  20. package/dist_ts_interfaces/requests/config.d.ts +77 -1
  21. package/dist_ts_web/00_commitinfo_data.js +1 -1
  22. package/dist_ts_web/appstate.d.ts +1 -1
  23. package/dist_ts_web/elements/ops-dashboard.js +15 -5
  24. package/dist_ts_web/elements/ops-view-apitokens.js +8 -4
  25. package/dist_ts_web/elements/ops-view-config.d.ts +10 -8
  26. package/dist_ts_web/elements/ops-view-config.js +215 -297
  27. package/package.json +2 -2
  28. package/ts/00_commitinfo_data.ts +1 -1
  29. package/ts/opsserver/handlers/config.handler.ts +154 -72
  30. package/ts_web/00_commitinfo_data.ts +1 -1
  31. package/ts_web/appstate.ts +1 -1
  32. package/ts_web/elements/ops-dashboard.ts +14 -4
  33. package/ts_web/elements/ops-view-apitokens.ts +7 -3
  34. package/ts_web/elements/ops-view-config.ts +237 -299
@@ -1,4 +1,5 @@
1
1
  import * as plugins from '../../plugins.js';
2
+ import * as paths from '../../paths.js';
2
3
  import type { OpsServer } from '../classes.opsserver.js';
3
4
  import * as interfaces from '../../../ts_interfaces/index.js';
4
5
 
@@ -17,7 +18,7 @@ export class ConfigHandler {
17
18
  new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetConfiguration>(
18
19
  'getConfiguration',
19
20
  async (dataArg, toolsArg) => {
20
- const config = await this.getConfiguration(dataArg.section);
21
+ const config = await this.getConfiguration();
21
22
  return {
22
23
  config,
23
24
  section: dataArg.section,
@@ -26,83 +27,164 @@ export class ConfigHandler {
26
27
  )
27
28
  );
28
29
  }
29
-
30
- private async getConfiguration(section?: string): Promise<{
31
- email: {
32
- enabled: boolean;
33
- ports: number[];
34
- maxMessageSize: number;
35
- rateLimits: {
36
- perMinute: number;
37
- perHour: number;
38
- perDay: number;
39
- };
40
- domains?: string[];
41
- };
42
- dns: {
43
- enabled: boolean;
44
- port: number;
45
- nameservers: string[];
46
- caching: boolean;
47
- ttl: number;
48
- };
49
- proxy: {
50
- enabled: boolean;
51
- httpPort: number;
52
- httpsPort: number;
53
- maxConnections: number;
30
+
31
+ private async getConfiguration(): Promise<interfaces.requests.IConfigData> {
32
+ const dcRouter = this.opsServerRef.dcRouterRef;
33
+ const opts = dcRouter.options;
34
+ const resolvedPaths = dcRouter.resolvedPaths;
35
+
36
+ // --- System ---
37
+ const storageBackend: 'filesystem' | 'custom' | 'memory' = opts.storage?.readFunction
38
+ ? 'custom'
39
+ : opts.storage?.fsPath
40
+ ? 'filesystem'
41
+ : 'memory';
42
+
43
+ const system: interfaces.requests.IConfigData['system'] = {
44
+ baseDir: resolvedPaths.dcrouterHomeDir,
45
+ dataDir: resolvedPaths.dataDir,
46
+ publicIp: opts.publicIp || null,
47
+ proxyIps: opts.proxyIps || [],
48
+ uptime: Math.floor(process.uptime()),
49
+ storageBackend,
50
+ storagePath: opts.storage?.fsPath || null,
54
51
  };
55
- security: {
56
- blockList: string[];
57
- rateLimit: boolean;
58
- spamDetection: boolean;
59
- tlsRequired: boolean;
52
+
53
+ // --- SmartProxy ---
54
+ let acmeInfo: interfaces.requests.IConfigData['smartProxy']['acme'] = null;
55
+ if (opts.smartProxyConfig?.acme) {
56
+ const acme = opts.smartProxyConfig.acme;
57
+ acmeInfo = {
58
+ enabled: acme.enabled !== false,
59
+ accountEmail: acme.accountEmail || '',
60
+ useProduction: acme.useProduction !== false,
61
+ autoRenew: acme.autoRenew !== false,
62
+ renewThresholdDays: acme.renewThresholdDays || 30,
63
+ };
64
+ }
65
+
66
+ let routeCount = 0;
67
+ if (dcRouter.routeConfigManager) {
68
+ try {
69
+ const merged = await dcRouter.routeConfigManager.getMergedRoutes();
70
+ routeCount = merged.routes.length;
71
+ } catch {
72
+ routeCount = opts.smartProxyConfig?.routes?.length || 0;
73
+ }
74
+ } else if (opts.smartProxyConfig?.routes) {
75
+ routeCount = opts.smartProxyConfig.routes.length;
76
+ }
77
+
78
+ const smartProxy: interfaces.requests.IConfigData['smartProxy'] = {
79
+ enabled: !!dcRouter.smartProxy,
80
+ routeCount,
81
+ acme: acmeInfo,
60
82
  };
61
- }> {
62
- const dcRouter = this.opsServerRef.dcRouterRef;
63
-
64
- // Get email domains if email server is configured
83
+
84
+ // --- Email ---
65
85
  let emailDomains: string[] = [];
66
- if (dcRouter.emailServer && dcRouter.emailServer.domainRegistry) {
67
- emailDomains = dcRouter.emailServer.domainRegistry.getAllDomains();
68
- } else if (dcRouter.options.emailConfig?.domains) {
69
- // Fallback: get domains from email config options
70
- emailDomains = dcRouter.options.emailConfig.domains.map(d =>
86
+ if (dcRouter.emailServer && (dcRouter.emailServer as any).domainRegistry) {
87
+ emailDomains = (dcRouter.emailServer as any).domainRegistry.getAllDomains();
88
+ } else if (opts.emailConfig?.domains) {
89
+ emailDomains = opts.emailConfig.domains.map((d: any) =>
71
90
  typeof d === 'string' ? d : d.domain
72
91
  );
73
92
  }
74
-
93
+
94
+ let portMapping: Record<string, number> | null = null;
95
+ if (opts.emailPortConfig?.portMapping) {
96
+ portMapping = {};
97
+ for (const [ext, int] of Object.entries(opts.emailPortConfig.portMapping)) {
98
+ portMapping[String(ext)] = int as number;
99
+ }
100
+ }
101
+
102
+ const email: interfaces.requests.IConfigData['email'] = {
103
+ enabled: !!dcRouter.emailServer,
104
+ ports: opts.emailConfig?.ports || [],
105
+ portMapping,
106
+ hostname: opts.emailConfig?.hostname || null,
107
+ domains: emailDomains,
108
+ emailRouteCount: opts.emailConfig?.routes?.length || 0,
109
+ receivedEmailsPath: opts.emailPortConfig?.receivedEmailsPath || null,
110
+ };
111
+
112
+ // --- DNS ---
113
+ const dnsRecords = (opts.dnsRecords || []).map(r => ({
114
+ name: r.name,
115
+ type: r.type,
116
+ value: r.value,
117
+ ttl: r.ttl,
118
+ }));
119
+
120
+ const dns: interfaces.requests.IConfigData['dns'] = {
121
+ enabled: !!dcRouter.dnsServer,
122
+ port: 53,
123
+ nsDomains: opts.dnsNsDomains || [],
124
+ scopes: opts.dnsScopes || [],
125
+ recordCount: dnsRecords.length,
126
+ records: dnsRecords,
127
+ dnsChallenge: !!opts.dnsChallenge?.cloudflareApiKey,
128
+ };
129
+
130
+ // --- TLS ---
131
+ let tlsSource: 'acme' | 'static' | 'none' = 'none';
132
+ if (opts.tls?.certPath && opts.tls?.keyPath) {
133
+ tlsSource = 'static';
134
+ } else if (opts.smartProxyConfig?.acme?.enabled !== false && opts.smartProxyConfig?.acme) {
135
+ tlsSource = 'acme';
136
+ }
137
+
138
+ const tls: interfaces.requests.IConfigData['tls'] = {
139
+ contactEmail: opts.tls?.contactEmail || opts.smartProxyConfig?.acme?.accountEmail || null,
140
+ domain: opts.tls?.domain || null,
141
+ source: tlsSource,
142
+ certPath: opts.tls?.certPath || null,
143
+ keyPath: opts.tls?.keyPath || null,
144
+ };
145
+
146
+ // --- Cache ---
147
+ const cacheConfig = opts.cacheConfig;
148
+ const cache: interfaces.requests.IConfigData['cache'] = {
149
+ enabled: cacheConfig?.enabled !== false,
150
+ storagePath: cacheConfig?.storagePath || resolvedPaths.defaultTsmDbPath,
151
+ dbName: cacheConfig?.dbName || 'dcrouter',
152
+ defaultTTLDays: cacheConfig?.defaultTTLDays || 30,
153
+ cleanupIntervalHours: cacheConfig?.cleanupIntervalHours || 1,
154
+ ttlConfig: cacheConfig?.ttlConfig ? { ...cacheConfig.ttlConfig } as Record<string, number> : {},
155
+ };
156
+
157
+ // --- RADIUS ---
158
+ const radiusCfg = opts.radiusConfig;
159
+ const radius: interfaces.requests.IConfigData['radius'] = {
160
+ enabled: !!dcRouter.radiusServer,
161
+ authPort: radiusCfg?.authPort || null,
162
+ acctPort: radiusCfg?.acctPort || null,
163
+ bindAddress: radiusCfg?.bindAddress || null,
164
+ clientCount: radiusCfg?.clients?.length || 0,
165
+ vlanDefaultVlan: radiusCfg?.vlanAssignment?.defaultVlan ?? null,
166
+ vlanAllowUnknownMacs: radiusCfg?.vlanAssignment?.allowUnknownMacs ?? null,
167
+ vlanMappingCount: radiusCfg?.vlanAssignment?.mappings?.length || 0,
168
+ };
169
+
170
+ // --- Remote Ingress ---
171
+ const riCfg = opts.remoteIngressConfig;
172
+ const remoteIngress: interfaces.requests.IConfigData['remoteIngress'] = {
173
+ enabled: !!dcRouter.remoteIngressManager,
174
+ tunnelPort: riCfg?.tunnelPort || null,
175
+ hubDomain: riCfg?.hubDomain || null,
176
+ tlsConfigured: !!(riCfg?.tls?.certPath && riCfg?.tls?.keyPath),
177
+ };
178
+
75
179
  return {
76
- email: {
77
- enabled: !!dcRouter.emailServer,
78
- ports: dcRouter.emailServer ? [25, 465, 587, 2525] : [],
79
- maxMessageSize: 10 * 1024 * 1024, // 10MB default
80
- rateLimits: {
81
- perMinute: 10,
82
- perHour: 100,
83
- perDay: 1000,
84
- },
85
- domains: emailDomains,
86
- },
87
- dns: {
88
- enabled: !!dcRouter.dnsServer,
89
- port: 53,
90
- nameservers: dcRouter.options.dnsNsDomains || [],
91
- caching: true,
92
- ttl: 300,
93
- },
94
- proxy: {
95
- enabled: !!dcRouter.smartProxy,
96
- httpPort: 80,
97
- httpsPort: 443,
98
- maxConnections: 1000,
99
- },
100
- security: {
101
- blockList: [],
102
- rateLimit: true,
103
- spamDetection: true,
104
- tlsRequired: false,
105
- },
180
+ system,
181
+ smartProxy,
182
+ email,
183
+ dns,
184
+ tls,
185
+ cache,
186
+ radius,
187
+ remoteIngress,
106
188
  };
107
189
  }
108
- }
190
+ }
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '8.1.0',
6
+ version: '9.1.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -21,7 +21,7 @@ export interface IStatsState {
21
21
  }
22
22
 
23
23
  export interface IConfigState {
24
- config: any | null;
24
+ config: interfaces.requests.IConfigData | null;
25
25
  isLoading: boolean;
26
26
  error: string | null;
27
27
  }
@@ -43,42 +43,52 @@ export class OpsDashboard extends DeesElement {
43
43
  private viewTabs = [
44
44
  {
45
45
  name: 'Overview',
46
+ iconName: 'lucide:layoutDashboard',
46
47
  element: OpsViewOverview,
47
48
  },
49
+ {
50
+ name: 'Configuration',
51
+ iconName: 'lucide:settings',
52
+ element: OpsViewConfig,
53
+ },
48
54
  {
49
55
  name: 'Network',
56
+ iconName: 'lucide:network',
50
57
  element: OpsViewNetwork,
51
58
  },
52
59
  {
53
60
  name: 'Emails',
61
+ iconName: 'lucide:mail',
54
62
  element: OpsViewEmails,
55
63
  },
56
64
  {
57
65
  name: 'Logs',
66
+ iconName: 'lucide:scrollText',
58
67
  element: OpsViewLogs,
59
68
  },
60
69
  {
61
70
  name: 'Routes',
71
+ iconName: 'lucide:route',
62
72
  element: OpsViewRoutes,
63
73
  },
64
74
  {
65
75
  name: 'ApiTokens',
76
+ iconName: 'lucide:key',
66
77
  element: OpsViewApiTokens,
67
78
  },
68
- {
69
- name: 'Configuration',
70
- element: OpsViewConfig,
71
- },
72
79
  {
73
80
  name: 'Security',
81
+ iconName: 'lucide:shield',
74
82
  element: OpsViewSecurity,
75
83
  },
76
84
  {
77
85
  name: 'Certificates',
86
+ iconName: 'lucide:badgeCheck',
78
87
  element: OpsViewCertificates,
79
88
  },
80
89
  {
81
90
  name: 'RemoteIngress',
91
+ iconName: 'lucide:globe',
82
92
  element: OpsViewRemoteIngress,
83
93
  },
84
94
  ];
@@ -225,13 +225,17 @@ export class OpsViewApiTokens extends DeesElement {
225
225
  name: 'Create',
226
226
  iconName: 'lucide:key',
227
227
  action: async (modalArg: any) => {
228
- const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
228
+ const contentEl = modalArg.shadowRoot?.querySelector('.content');
229
+ const form = contentEl?.querySelector('dees-form');
229
230
  if (!form) return;
230
231
  const formData = await form.collectFormData();
231
232
  if (!formData.name) return;
232
233
 
233
- // dees-input-tags returns string[] directly
234
- const scopes = (formData.scopes || [])
234
+ // dees-input-tags is not in dees-form's FORM_INPUT_TYPES, so collectFormData() won't
235
+ // include it. Query the tags input directly and call getValue().
236
+ const tagsInput = form.querySelector('dees-input-tags') as any;
237
+ const rawScopes: string[] = tagsInput?.getValue?.() || tagsInput?.value || formData.scopes || [];
238
+ const scopes = rawScopes
235
239
  .filter((s: string) => allScopes.includes(s as any)) as TApiTokenScope[];
236
240
 
237
241
  const expiresInDays = formData.expiresInDays