@serve.zone/dcrouter 7.4.3 → 8.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 (110) hide show
  1. package/dist_serve/bundle.js +11567 -3516
  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/{email-ops.handler.d.ts → api-token.handler.d.ts} +4 -4
  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/{radius.handler.d.ts → route-management.handler.d.ts} +6 -1
  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/email-ops.d.ts +51 -108
  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 +38 -16
  32. package/dist_ts_web/appstate.js +226 -177
  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 +11 -1
  36. package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
  37. package/dist_ts_web/elements/ops-view-apitokens.js +306 -0
  38. package/dist_ts_web/elements/ops-view-emails.d.ts +8 -31
  39. package/dist_ts_web/elements/ops-view-emails.js +54 -769
  40. package/dist_ts_web/elements/ops-view-logs.d.ts +2 -8
  41. package/dist_ts_web/elements/ops-view-logs.js +4 -101
  42. package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
  43. package/dist_ts_web/elements/ops-view-routes.js +404 -0
  44. package/dist_ts_web/plugins.d.ts +2 -1
  45. package/dist_ts_web/plugins.js +4 -2
  46. package/dist_ts_web/router.d.ts +1 -7
  47. package/dist_ts_web/router.js +8 -82
  48. package/package.json +2 -1
  49. package/ts/00_commitinfo_data.ts +1 -1
  50. package/ts/classes.dcrouter.ts +37 -1
  51. package/ts/config/classes.api-token-manager.ts +155 -0
  52. package/ts/config/classes.route-config-manager.ts +271 -0
  53. package/ts/config/index.ts +3 -1
  54. package/ts/opsserver/classes.opsserver.ts +4 -0
  55. package/ts/opsserver/handlers/api-token.handler.ts +96 -0
  56. package/ts/opsserver/handlers/email-ops.handler.ts +177 -225
  57. package/ts/opsserver/handlers/index.ts +3 -1
  58. package/ts/opsserver/handlers/route-management.handler.ts +163 -0
  59. package/ts_web/00_commitinfo_data.ts +1 -1
  60. package/ts_web/appstate.ts +316 -222
  61. package/ts_web/elements/index.ts +2 -0
  62. package/ts_web/elements/ops-dashboard.ts +10 -0
  63. package/ts_web/elements/ops-view-apitokens.ts +281 -0
  64. package/ts_web/elements/ops-view-emails.ts +40 -749
  65. package/ts_web/elements/ops-view-logs.ts +2 -87
  66. package/ts_web/elements/ops-view-routes.ts +389 -0
  67. package/ts_web/plugins.ts +4 -0
  68. package/ts_web/router.ts +7 -82
  69. package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
  70. package/dist_ts/cache/classes.cache.cleaner.js +0 -130
  71. package/dist_ts/cache/classes.cached.document.d.ts +0 -76
  72. package/dist_ts/cache/classes.cached.document.js +0 -100
  73. package/dist_ts/cache/classes.cachedb.d.ts +0 -60
  74. package/dist_ts/cache/classes.cachedb.js +0 -126
  75. package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
  76. package/dist_ts/cache/documents/classes.cached.email.js +0 -337
  77. package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
  78. package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
  79. package/dist_ts/cache/documents/index.d.ts +0 -2
  80. package/dist_ts/cache/documents/index.js +0 -3
  81. package/dist_ts/cache/index.d.ts +0 -4
  82. package/dist_ts/cache/index.js +0 -7
  83. package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
  84. package/dist_ts/monitoring/classes.metricscache.js +0 -63
  85. package/dist_ts/monitoring/classes.metricsmanager.d.ts +0 -169
  86. package/dist_ts/monitoring/classes.metricsmanager.js +0 -591
  87. package/dist_ts/monitoring/index.d.ts +0 -1
  88. package/dist_ts/monitoring/index.js +0 -2
  89. package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
  90. package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
  91. package/dist_ts/opsserver/handlers/certificate.handler.d.ts +0 -34
  92. package/dist_ts/opsserver/handlers/certificate.handler.js +0 -419
  93. package/dist_ts/opsserver/handlers/config.handler.d.ts +0 -9
  94. package/dist_ts/opsserver/handlers/config.handler.js +0 -67
  95. package/dist_ts/opsserver/handlers/email-ops.handler.js +0 -219
  96. package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
  97. package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
  98. package/dist_ts/opsserver/handlers/radius.handler.js +0 -296
  99. package/dist_ts/opsserver/handlers/remoteingress.handler.d.ts +0 -8
  100. package/dist_ts/opsserver/handlers/remoteingress.handler.js +0 -154
  101. package/dist_ts/opsserver/handlers/security.handler.d.ts +0 -11
  102. package/dist_ts/opsserver/handlers/security.handler.js +0 -232
  103. package/dist_ts/opsserver/handlers/stats.handler.d.ts +0 -13
  104. package/dist_ts/opsserver/handlers/stats.handler.js +0 -400
  105. package/dist_ts/security/classes.securitylogger.d.ts +0 -140
  106. package/dist_ts/security/classes.securitylogger.js +0 -235
  107. package/dist_ts/storage/classes.storagemanager.d.ts +0 -82
  108. package/dist_ts/storage/classes.storagemanager.js +0 -344
  109. package/dist_ts/storage/index.d.ts +0 -1
  110. package/dist_ts/storage/index.js +0 -3
@@ -0,0 +1,271 @@
1
+ import * as plugins from '../plugins.js';
2
+ import { logger } from '../logger.js';
3
+ import type { StorageManager } from '../storage/index.js';
4
+ import type {
5
+ IStoredRoute,
6
+ IRouteOverride,
7
+ IMergedRoute,
8
+ IRouteWarning,
9
+ } from '../../ts_interfaces/data/route-management.js';
10
+
11
+ const ROUTES_PREFIX = '/config-api/routes/';
12
+ const OVERRIDES_PREFIX = '/config-api/overrides/';
13
+
14
+ export class RouteConfigManager {
15
+ private storedRoutes = new Map<string, IStoredRoute>();
16
+ private overrides = new Map<string, IRouteOverride>();
17
+ private warnings: IRouteWarning[] = [];
18
+
19
+ constructor(
20
+ private storageManager: StorageManager,
21
+ private getHardcodedRoutes: () => plugins.smartproxy.IRouteConfig[],
22
+ private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
23
+ ) {}
24
+
25
+ /**
26
+ * Load persisted routes and overrides, compute warnings, apply to SmartProxy.
27
+ */
28
+ public async initialize(): Promise<void> {
29
+ await this.loadStoredRoutes();
30
+ await this.loadOverrides();
31
+ this.computeWarnings();
32
+ this.logWarnings();
33
+ await this.applyRoutes();
34
+ }
35
+
36
+ // =========================================================================
37
+ // Merged view
38
+ // =========================================================================
39
+
40
+ public getMergedRoutes(): { routes: IMergedRoute[]; warnings: IRouteWarning[] } {
41
+ const merged: IMergedRoute[] = [];
42
+
43
+ // Hardcoded routes
44
+ for (const route of this.getHardcodedRoutes()) {
45
+ const name = route.name || '';
46
+ const override = this.overrides.get(name);
47
+ merged.push({
48
+ route,
49
+ source: 'hardcoded',
50
+ enabled: override ? override.enabled : true,
51
+ overridden: !!override,
52
+ });
53
+ }
54
+
55
+ // Programmatic routes
56
+ for (const stored of this.storedRoutes.values()) {
57
+ merged.push({
58
+ route: stored.route,
59
+ source: 'programmatic',
60
+ enabled: stored.enabled,
61
+ overridden: false,
62
+ storedRouteId: stored.id,
63
+ createdAt: stored.createdAt,
64
+ updatedAt: stored.updatedAt,
65
+ });
66
+ }
67
+
68
+ return { routes: merged, warnings: [...this.warnings] };
69
+ }
70
+
71
+ // =========================================================================
72
+ // Programmatic route CRUD
73
+ // =========================================================================
74
+
75
+ public async createRoute(
76
+ route: plugins.smartproxy.IRouteConfig,
77
+ createdBy: string,
78
+ enabled = true,
79
+ ): Promise<string> {
80
+ const id = plugins.uuid.v4();
81
+ const now = Date.now();
82
+
83
+ // Ensure route has a name
84
+ if (!route.name) {
85
+ route.name = `programmatic-${id.slice(0, 8)}`;
86
+ }
87
+
88
+ const stored: IStoredRoute = {
89
+ id,
90
+ route,
91
+ enabled,
92
+ createdAt: now,
93
+ updatedAt: now,
94
+ createdBy,
95
+ };
96
+
97
+ this.storedRoutes.set(id, stored);
98
+ await this.persistRoute(stored);
99
+ await this.applyRoutes();
100
+ return id;
101
+ }
102
+
103
+ public async updateRoute(
104
+ id: string,
105
+ patch: { route?: Partial<plugins.smartproxy.IRouteConfig>; enabled?: boolean },
106
+ ): Promise<boolean> {
107
+ const stored = this.storedRoutes.get(id);
108
+ if (!stored) return false;
109
+
110
+ if (patch.route) {
111
+ stored.route = { ...stored.route, ...patch.route } as plugins.smartproxy.IRouteConfig;
112
+ }
113
+ if (patch.enabled !== undefined) {
114
+ stored.enabled = patch.enabled;
115
+ }
116
+ stored.updatedAt = Date.now();
117
+
118
+ await this.persistRoute(stored);
119
+ await this.applyRoutes();
120
+ return true;
121
+ }
122
+
123
+ public async deleteRoute(id: string): Promise<boolean> {
124
+ if (!this.storedRoutes.has(id)) return false;
125
+ this.storedRoutes.delete(id);
126
+ await this.storageManager.delete(`${ROUTES_PREFIX}${id}.json`);
127
+ await this.applyRoutes();
128
+ return true;
129
+ }
130
+
131
+ public async toggleRoute(id: string, enabled: boolean): Promise<boolean> {
132
+ return this.updateRoute(id, { enabled });
133
+ }
134
+
135
+ // =========================================================================
136
+ // Hardcoded route overrides
137
+ // =========================================================================
138
+
139
+ public async setOverride(routeName: string, enabled: boolean, updatedBy: string): Promise<void> {
140
+ const override: IRouteOverride = {
141
+ routeName,
142
+ enabled,
143
+ updatedAt: Date.now(),
144
+ updatedBy,
145
+ };
146
+ this.overrides.set(routeName, override);
147
+ await this.storageManager.setJSON(`${OVERRIDES_PREFIX}${routeName}.json`, override);
148
+ this.computeWarnings();
149
+ await this.applyRoutes();
150
+ }
151
+
152
+ public async removeOverride(routeName: string): Promise<boolean> {
153
+ if (!this.overrides.has(routeName)) return false;
154
+ this.overrides.delete(routeName);
155
+ await this.storageManager.delete(`${OVERRIDES_PREFIX}${routeName}.json`);
156
+ this.computeWarnings();
157
+ await this.applyRoutes();
158
+ return true;
159
+ }
160
+
161
+ // =========================================================================
162
+ // Private: persistence
163
+ // =========================================================================
164
+
165
+ private async loadStoredRoutes(): Promise<void> {
166
+ const keys = await this.storageManager.list(ROUTES_PREFIX);
167
+ for (const key of keys) {
168
+ if (!key.endsWith('.json')) continue;
169
+ const stored = await this.storageManager.getJSON<IStoredRoute>(key);
170
+ if (stored?.id) {
171
+ this.storedRoutes.set(stored.id, stored);
172
+ }
173
+ }
174
+ if (this.storedRoutes.size > 0) {
175
+ logger.log('info', `Loaded ${this.storedRoutes.size} programmatic route(s) from storage`);
176
+ }
177
+ }
178
+
179
+ private async loadOverrides(): Promise<void> {
180
+ const keys = await this.storageManager.list(OVERRIDES_PREFIX);
181
+ for (const key of keys) {
182
+ if (!key.endsWith('.json')) continue;
183
+ const override = await this.storageManager.getJSON<IRouteOverride>(key);
184
+ if (override?.routeName) {
185
+ this.overrides.set(override.routeName, override);
186
+ }
187
+ }
188
+ if (this.overrides.size > 0) {
189
+ logger.log('info', `Loaded ${this.overrides.size} route override(s) from storage`);
190
+ }
191
+ }
192
+
193
+ private async persistRoute(stored: IStoredRoute): Promise<void> {
194
+ await this.storageManager.setJSON(`${ROUTES_PREFIX}${stored.id}.json`, stored);
195
+ }
196
+
197
+ // =========================================================================
198
+ // Private: warnings
199
+ // =========================================================================
200
+
201
+ private computeWarnings(): void {
202
+ this.warnings = [];
203
+ const hardcodedNames = new Set(this.getHardcodedRoutes().map((r) => r.name || ''));
204
+
205
+ // Check overrides
206
+ for (const [routeName, override] of this.overrides) {
207
+ if (!hardcodedNames.has(routeName)) {
208
+ this.warnings.push({
209
+ type: 'orphaned-override',
210
+ routeName,
211
+ message: `Orphaned override for route '${routeName}' — hardcoded route no longer exists`,
212
+ });
213
+ } else if (!override.enabled) {
214
+ this.warnings.push({
215
+ type: 'disabled-hardcoded',
216
+ routeName,
217
+ message: `Route '${routeName}' is disabled via API override`,
218
+ });
219
+ }
220
+ }
221
+
222
+ // Check disabled programmatic routes
223
+ for (const stored of this.storedRoutes.values()) {
224
+ if (!stored.enabled) {
225
+ const name = stored.route.name || stored.id;
226
+ this.warnings.push({
227
+ type: 'disabled-programmatic',
228
+ routeName: name,
229
+ message: `Programmatic route '${name}' (id: ${stored.id}) is disabled`,
230
+ });
231
+ }
232
+ }
233
+ }
234
+
235
+ private logWarnings(): void {
236
+ for (const w of this.warnings) {
237
+ logger.log('warn', w.message);
238
+ }
239
+ }
240
+
241
+ // =========================================================================
242
+ // Private: apply merged routes to SmartProxy
243
+ // =========================================================================
244
+
245
+ private async applyRoutes(): Promise<void> {
246
+ const smartProxy = this.getSmartProxy();
247
+ if (!smartProxy) return;
248
+
249
+ const enabledRoutes: plugins.smartproxy.IRouteConfig[] = [];
250
+
251
+ // Add enabled hardcoded routes (respecting overrides)
252
+ for (const route of this.getHardcodedRoutes()) {
253
+ const name = route.name || '';
254
+ const override = this.overrides.get(name);
255
+ if (override && !override.enabled) {
256
+ continue; // Skip disabled hardcoded route
257
+ }
258
+ enabledRoutes.push(route);
259
+ }
260
+
261
+ // Add enabled programmatic routes
262
+ for (const stored of this.storedRoutes.values()) {
263
+ if (stored.enabled) {
264
+ enabledRoutes.push(stored.route);
265
+ }
266
+ }
267
+
268
+ await smartProxy.updateRoutes(enabledRoutes);
269
+ logger.log('info', `Applied ${enabledRoutes.length} routes to SmartProxy (${this.storedRoutes.size} programmatic, ${this.overrides.size} overrides)`);
270
+ }
271
+ }
@@ -1,2 +1,4 @@
1
1
  // Export validation tools only
2
- export * from './validator.js';
2
+ export * from './validator.js';
3
+ export { RouteConfigManager } from './classes.route-config-manager.js';
4
+ export { ApiTokenManager } from './classes.api-token-manager.js';
@@ -20,6 +20,8 @@ export class OpsServer {
20
20
  private emailOpsHandler: handlers.EmailOpsHandler;
21
21
  private certificateHandler: handlers.CertificateHandler;
22
22
  private remoteIngressHandler: handlers.RemoteIngressHandler;
23
+ private routeManagementHandler: handlers.RouteManagementHandler;
24
+ private apiTokenHandler: handlers.ApiTokenHandler;
23
25
 
24
26
  constructor(dcRouterRefArg: DcRouter) {
25
27
  this.dcRouterRef = dcRouterRefArg;
@@ -61,6 +63,8 @@ export class OpsServer {
61
63
  this.emailOpsHandler = new handlers.EmailOpsHandler(this);
62
64
  this.certificateHandler = new handlers.CertificateHandler(this);
63
65
  this.remoteIngressHandler = new handlers.RemoteIngressHandler(this);
66
+ this.routeManagementHandler = new handlers.RouteManagementHandler(this);
67
+ this.apiTokenHandler = new handlers.ApiTokenHandler(this);
64
68
 
65
69
  console.log('✅ OpsServer TypedRequest handlers initialized');
66
70
  }
@@ -0,0 +1,96 @@
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 ApiTokenHandler {
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
+ * Token management requires admin JWT only (tokens cannot manage tokens).
15
+ */
16
+ private async requireAdmin(identity?: interfaces.data.IIdentity): Promise<string> {
17
+ if (!identity?.jwt) {
18
+ throw new plugins.typedrequest.TypedResponseError('unauthorized');
19
+ }
20
+ const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({ identity });
21
+ if (!isAdmin) {
22
+ throw new plugins.typedrequest.TypedResponseError('admin access required');
23
+ }
24
+ return identity.userId;
25
+ }
26
+
27
+ private registerHandlers(): void {
28
+ // Create API token
29
+ this.typedrouter.addTypedHandler(
30
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateApiToken>(
31
+ 'createApiToken',
32
+ async (dataArg) => {
33
+ const userId = await this.requireAdmin(dataArg.identity);
34
+ const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
35
+ if (!manager) {
36
+ return { success: false, message: 'Token management not initialized' };
37
+ }
38
+ const result = await manager.createToken(
39
+ dataArg.name,
40
+ dataArg.scopes,
41
+ dataArg.expiresInDays ?? null,
42
+ userId,
43
+ );
44
+ return { success: true, tokenId: result.id, tokenValue: result.rawToken };
45
+ },
46
+ ),
47
+ );
48
+
49
+ // List API tokens
50
+ this.typedrouter.addTypedHandler(
51
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListApiTokens>(
52
+ 'listApiTokens',
53
+ async (dataArg) => {
54
+ await this.requireAdmin(dataArg.identity);
55
+ const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
56
+ if (!manager) {
57
+ return { tokens: [] };
58
+ }
59
+ return { tokens: manager.listTokens() };
60
+ },
61
+ ),
62
+ );
63
+
64
+ // Revoke API token
65
+ this.typedrouter.addTypedHandler(
66
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RevokeApiToken>(
67
+ 'revokeApiToken',
68
+ async (dataArg) => {
69
+ await this.requireAdmin(dataArg.identity);
70
+ const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
71
+ if (!manager) {
72
+ return { success: false, message: 'Token management not initialized' };
73
+ }
74
+ const ok = await manager.revokeToken(dataArg.id);
75
+ return { success: ok, message: ok ? undefined : 'Token not found' };
76
+ },
77
+ ),
78
+ );
79
+
80
+ // Toggle API token
81
+ this.typedrouter.addTypedHandler(
82
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ToggleApiToken>(
83
+ 'toggleApiToken',
84
+ async (dataArg) => {
85
+ await this.requireAdmin(dataArg.identity);
86
+ const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
87
+ if (!manager) {
88
+ return { success: false, message: 'Token management not initialized' };
89
+ }
90
+ const ok = await manager.toggleToken(dataArg.id, dataArg.enabled);
91
+ return { success: ok, message: ok ? undefined : 'Token not found' };
92
+ },
93
+ ),
94
+ );
95
+ }
96
+ }