@serve.zone/dcrouter 8.0.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 (88) hide show
  1. package/dist_serve/bundle.js +2420 -1227
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.d.ts +9 -0
  4. package/dist_ts/classes.dcrouter.js +27 -1
  5. package/dist_ts/config/classes.api-token-manager.d.ts +38 -0
  6. package/dist_ts/config/classes.api-token-manager.js +134 -0
  7. package/dist_ts/config/classes.route-config-manager.d.ts +35 -0
  8. package/dist_ts/config/classes.route-config-manager.js +231 -0
  9. package/dist_ts/config/index.d.ts +2 -0
  10. package/dist_ts/config/index.js +3 -1
  11. package/dist_ts/opsserver/classes.opsserver.d.ts +2 -0
  12. package/dist_ts/opsserver/classes.opsserver.js +5 -1
  13. package/dist_ts/opsserver/handlers/{config.handler.d.ts → api-token.handler.d.ts} +5 -2
  14. package/dist_ts/opsserver/handlers/api-token.handler.js +66 -0
  15. package/dist_ts/opsserver/handlers/index.d.ts +2 -0
  16. package/dist_ts/opsserver/handlers/index.js +3 -1
  17. package/dist_ts/opsserver/handlers/route-management.handler.d.ts +13 -0
  18. package/dist_ts/opsserver/handlers/route-management.handler.js +117 -0
  19. package/dist_ts_interfaces/data/index.d.ts +1 -0
  20. package/dist_ts_interfaces/data/index.js +2 -1
  21. package/dist_ts_interfaces/data/route-management.d.ts +68 -0
  22. package/dist_ts_interfaces/data/route-management.js +2 -0
  23. package/dist_ts_interfaces/requests/api-tokens.d.ts +63 -0
  24. package/dist_ts_interfaces/requests/api-tokens.js +2 -0
  25. package/dist_ts_interfaces/requests/config.d.ts +77 -1
  26. package/dist_ts_interfaces/requests/index.d.ts +2 -0
  27. package/dist_ts_interfaces/requests/index.js +3 -1
  28. package/dist_ts_interfaces/requests/route-management.d.ts +114 -0
  29. package/dist_ts_interfaces/requests/route-management.js +2 -0
  30. package/dist_ts_web/00_commitinfo_data.js +1 -1
  31. package/dist_ts_web/appstate.d.ts +37 -1
  32. package/dist_ts_web/appstate.js +220 -2
  33. package/dist_ts_web/elements/index.d.ts +2 -0
  34. package/dist_ts_web/elements/index.js +3 -1
  35. package/dist_ts_web/elements/ops-dashboard.js +23 -3
  36. package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
  37. package/dist_ts_web/elements/ops-view-apitokens.js +310 -0
  38. package/dist_ts_web/elements/ops-view-config.d.ts +10 -8
  39. package/dist_ts_web/elements/ops-view-config.js +215 -297
  40. package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
  41. package/dist_ts_web/elements/ops-view-routes.js +404 -0
  42. package/dist_ts_web/router.d.ts +1 -1
  43. package/dist_ts_web/router.js +2 -2
  44. package/package.json +2 -2
  45. package/ts/00_commitinfo_data.ts +1 -1
  46. package/ts/classes.dcrouter.ts +37 -1
  47. package/ts/config/classes.api-token-manager.ts +155 -0
  48. package/ts/config/classes.route-config-manager.ts +271 -0
  49. package/ts/config/index.ts +3 -1
  50. package/ts/opsserver/classes.opsserver.ts +4 -0
  51. package/ts/opsserver/handlers/api-token.handler.ts +96 -0
  52. package/ts/opsserver/handlers/config.handler.ts +154 -72
  53. package/ts/opsserver/handlers/index.ts +3 -1
  54. package/ts/opsserver/handlers/route-management.handler.ts +163 -0
  55. package/ts_web/00_commitinfo_data.ts +1 -1
  56. package/ts_web/appstate.ts +309 -2
  57. package/ts_web/elements/index.ts +2 -0
  58. package/ts_web/elements/ops-dashboard.ts +22 -2
  59. package/ts_web/elements/ops-view-apitokens.ts +285 -0
  60. package/ts_web/elements/ops-view-config.ts +237 -299
  61. package/ts_web/elements/ops-view-routes.ts +389 -0
  62. package/ts_web/router.ts +1 -1
  63. package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
  64. package/dist_ts/cache/classes.cache.cleaner.js +0 -130
  65. package/dist_ts/cache/classes.cached.document.d.ts +0 -76
  66. package/dist_ts/cache/classes.cached.document.js +0 -100
  67. package/dist_ts/cache/classes.cachedb.d.ts +0 -60
  68. package/dist_ts/cache/classes.cachedb.js +0 -126
  69. package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
  70. package/dist_ts/cache/documents/classes.cached.email.js +0 -337
  71. package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
  72. package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
  73. package/dist_ts/cache/documents/index.d.ts +0 -2
  74. package/dist_ts/cache/documents/index.js +0 -3
  75. package/dist_ts/cache/index.d.ts +0 -4
  76. package/dist_ts/cache/index.js +0 -7
  77. package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
  78. package/dist_ts/monitoring/classes.metricscache.js +0 -63
  79. package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
  80. package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
  81. package/dist_ts/opsserver/handlers/config.handler.js +0 -67
  82. package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
  83. package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
  84. package/dist_ts/security/classes.securitylogger.js +0 -235
  85. package/dist_ts/storage/classes.storagemanager.d.ts +0 -82
  86. package/dist_ts/storage/classes.storagemanager.js +0 -344
  87. package/dist_ts/storage/index.d.ts +0 -1
  88. package/dist_ts/storage/index.js +0 -3
@@ -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
+ }
@@ -6,4 +6,6 @@ export * from './stats.handler.js';
6
6
  export * from './radius.handler.js';
7
7
  export * from './email-ops.handler.js';
8
8
  export * from './certificate.handler.js';
9
- export * from './remoteingress.handler.js';
9
+ export * from './remoteingress.handler.js';
10
+ export * from './route-management.handler.js';
11
+ export * from './api-token.handler.js';
@@ -0,0 +1,163 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { OpsServer } from '../classes.opsserver.js';
3
+ import * as interfaces from '../../../ts_interfaces/index.js';
4
+
5
+ export class RouteManagementHandler {
6
+ public typedrouter = new plugins.typedrequest.TypedRouter();
7
+
8
+ constructor(private opsServerRef: OpsServer) {
9
+ this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
10
+ this.registerHandlers();
11
+ }
12
+
13
+ /**
14
+ * Validate auth: JWT identity OR API token with required scope.
15
+ * Returns a userId string on success, throws on failure.
16
+ */
17
+ private async requireAuth(
18
+ request: { identity?: interfaces.data.IIdentity; apiToken?: string },
19
+ requiredScope?: interfaces.data.TApiTokenScope,
20
+ ): Promise<string> {
21
+ // Try JWT identity first
22
+ if (request.identity?.jwt) {
23
+ try {
24
+ const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
25
+ identity: request.identity,
26
+ });
27
+ if (isAdmin) return request.identity.userId;
28
+ } catch { /* fall through */ }
29
+ }
30
+
31
+ // Try API token
32
+ if (request.apiToken) {
33
+ const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
34
+ if (tokenManager) {
35
+ const token = await tokenManager.validateToken(request.apiToken);
36
+ if (token) {
37
+ if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
38
+ return token.createdBy;
39
+ }
40
+ throw new plugins.typedrequest.TypedResponseError('insufficient scope');
41
+ }
42
+ }
43
+ }
44
+
45
+ throw new plugins.typedrequest.TypedResponseError('unauthorized');
46
+ }
47
+
48
+ private registerHandlers(): void {
49
+ // Get merged routes
50
+ this.typedrouter.addTypedHandler(
51
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetMergedRoutes>(
52
+ 'getMergedRoutes',
53
+ async (dataArg) => {
54
+ await this.requireAuth(dataArg, 'routes:read');
55
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
56
+ if (!manager) {
57
+ return { routes: [], warnings: [] };
58
+ }
59
+ return manager.getMergedRoutes();
60
+ },
61
+ ),
62
+ );
63
+
64
+ // Create route
65
+ this.typedrouter.addTypedHandler(
66
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateRoute>(
67
+ 'createRoute',
68
+ async (dataArg) => {
69
+ const userId = await this.requireAuth(dataArg, 'routes:write');
70
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
71
+ if (!manager) {
72
+ return { success: false, message: 'Route management not initialized' };
73
+ }
74
+ const id = await manager.createRoute(dataArg.route, userId, dataArg.enabled ?? true);
75
+ return { success: true, storedRouteId: id };
76
+ },
77
+ ),
78
+ );
79
+
80
+ // Update route
81
+ this.typedrouter.addTypedHandler(
82
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateRoute>(
83
+ 'updateRoute',
84
+ async (dataArg) => {
85
+ await this.requireAuth(dataArg, 'routes:write');
86
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
87
+ if (!manager) {
88
+ return { success: false, message: 'Route management not initialized' };
89
+ }
90
+ const ok = await manager.updateRoute(dataArg.id, {
91
+ route: dataArg.route as any,
92
+ enabled: dataArg.enabled,
93
+ });
94
+ return { success: ok, message: ok ? undefined : 'Route not found' };
95
+ },
96
+ ),
97
+ );
98
+
99
+ // Delete route
100
+ this.typedrouter.addTypedHandler(
101
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteRoute>(
102
+ 'deleteRoute',
103
+ async (dataArg) => {
104
+ await this.requireAuth(dataArg, 'routes:write');
105
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
106
+ if (!manager) {
107
+ return { success: false, message: 'Route management not initialized' };
108
+ }
109
+ const ok = await manager.deleteRoute(dataArg.id);
110
+ return { success: ok, message: ok ? undefined : 'Route not found' };
111
+ },
112
+ ),
113
+ );
114
+
115
+ // Set override on a hardcoded route
116
+ this.typedrouter.addTypedHandler(
117
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetRouteOverride>(
118
+ 'setRouteOverride',
119
+ async (dataArg) => {
120
+ const userId = await this.requireAuth(dataArg, 'routes:write');
121
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
122
+ if (!manager) {
123
+ return { success: false, message: 'Route management not initialized' };
124
+ }
125
+ await manager.setOverride(dataArg.routeName, dataArg.enabled, userId);
126
+ return { success: true };
127
+ },
128
+ ),
129
+ );
130
+
131
+ // Remove override from a hardcoded route
132
+ this.typedrouter.addTypedHandler(
133
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveRouteOverride>(
134
+ 'removeRouteOverride',
135
+ async (dataArg) => {
136
+ await this.requireAuth(dataArg, 'routes:write');
137
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
138
+ if (!manager) {
139
+ return { success: false, message: 'Route management not initialized' };
140
+ }
141
+ const ok = await manager.removeOverride(dataArg.routeName);
142
+ return { success: ok, message: ok ? undefined : 'Override not found' };
143
+ },
144
+ ),
145
+ );
146
+
147
+ // Toggle programmatic route
148
+ this.typedrouter.addTypedHandler(
149
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ToggleRoute>(
150
+ 'toggleRoute',
151
+ async (dataArg) => {
152
+ await this.requireAuth(dataArg, 'routes:write');
153
+ const manager = this.opsServerRef.dcRouterRef.routeConfigManager;
154
+ if (!manager) {
155
+ return { success: false, message: 'Route management not initialized' };
156
+ }
157
+ const ok = await manager.toggleRoute(dataArg.id, dataArg.enabled);
158
+ return { success: ok, message: ok ? undefined : 'Route not found' };
159
+ },
160
+ ),
161
+ );
162
+ }
163
+ }
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '8.0.0',
6
+ version: '9.1.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }