@serve.zone/dcrouter 8.0.0 → 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.
- package/dist_serve/bundle.js +1659 -891
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +9 -0
- package/dist_ts/classes.dcrouter.js +27 -1
- package/dist_ts/config/classes.api-token-manager.d.ts +38 -0
- package/dist_ts/config/classes.api-token-manager.js +134 -0
- package/dist_ts/config/classes.route-config-manager.d.ts +35 -0
- package/dist_ts/config/classes.route-config-manager.js +231 -0
- package/dist_ts/config/index.d.ts +2 -0
- package/dist_ts/config/index.js +3 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +2 -0
- package/dist_ts/opsserver/classes.opsserver.js +5 -1
- package/dist_ts/opsserver/handlers/{remoteingress.handler.d.ts → api-token.handler.d.ts} +5 -1
- package/dist_ts/opsserver/handlers/api-token.handler.js +66 -0
- package/dist_ts/opsserver/handlers/index.d.ts +2 -0
- package/dist_ts/opsserver/handlers/index.js +3 -1
- package/dist_ts/opsserver/handlers/{radius.handler.d.ts → route-management.handler.d.ts} +6 -1
- package/dist_ts/opsserver/handlers/route-management.handler.js +117 -0
- package/dist_ts_interfaces/data/index.d.ts +1 -0
- package/dist_ts_interfaces/data/index.js +2 -1
- package/dist_ts_interfaces/data/route-management.d.ts +68 -0
- package/dist_ts_interfaces/data/route-management.js +2 -0
- package/dist_ts_interfaces/requests/api-tokens.d.ts +63 -0
- package/dist_ts_interfaces/requests/api-tokens.js +2 -0
- package/dist_ts_interfaces/requests/index.d.ts +2 -0
- package/dist_ts_interfaces/requests/index.js +3 -1
- package/dist_ts_interfaces/requests/route-management.d.ts +114 -0
- package/dist_ts_interfaces/requests/route-management.js +2 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +36 -0
- package/dist_ts_web/appstate.js +220 -2
- package/dist_ts_web/elements/index.d.ts +2 -0
- package/dist_ts_web/elements/index.js +3 -1
- package/dist_ts_web/elements/ops-dashboard.js +11 -1
- package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
- package/dist_ts_web/elements/ops-view-apitokens.js +306 -0
- package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
- package/dist_ts_web/elements/ops-view-routes.js +404 -0
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +2 -2
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +37 -1
- package/ts/config/classes.api-token-manager.ts +155 -0
- package/ts/config/classes.route-config-manager.ts +271 -0
- package/ts/config/index.ts +3 -1
- package/ts/opsserver/classes.opsserver.ts +4 -0
- package/ts/opsserver/handlers/api-token.handler.ts +96 -0
- package/ts/opsserver/handlers/index.ts +3 -1
- package/ts/opsserver/handlers/route-management.handler.ts +163 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +308 -1
- package/ts_web/elements/index.ts +2 -0
- package/ts_web/elements/ops-dashboard.ts +10 -0
- package/ts_web/elements/ops-view-apitokens.ts +281 -0
- package/ts_web/elements/ops-view-routes.ts +389 -0
- package/ts_web/router.ts +1 -1
- package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
- package/dist_ts/cache/classes.cache.cleaner.js +0 -130
- package/dist_ts/cache/classes.cached.document.d.ts +0 -76
- package/dist_ts/cache/classes.cached.document.js +0 -100
- package/dist_ts/cache/classes.cachedb.d.ts +0 -60
- package/dist_ts/cache/classes.cachedb.js +0 -126
- package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
- package/dist_ts/cache/documents/classes.cached.email.js +0 -337
- package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
- package/dist_ts/cache/documents/index.d.ts +0 -2
- package/dist_ts/cache/documents/index.js +0 -3
- package/dist_ts/cache/index.d.ts +0 -4
- package/dist_ts/cache/index.js +0 -7
- package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
- package/dist_ts/monitoring/classes.metricscache.js +0 -63
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +0 -169
- package/dist_ts/monitoring/classes.metricsmanager.js +0 -591
- package/dist_ts/monitoring/index.d.ts +0 -1
- package/dist_ts/monitoring/index.js +0 -2
- package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
- package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
- package/dist_ts/opsserver/handlers/certificate.handler.d.ts +0 -34
- package/dist_ts/opsserver/handlers/certificate.handler.js +0 -419
- package/dist_ts/opsserver/handlers/config.handler.d.ts +0 -9
- package/dist_ts/opsserver/handlers/config.handler.js +0 -67
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +0 -32
- package/dist_ts/opsserver/handlers/email-ops.handler.js +0 -226
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
- package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
- package/dist_ts/opsserver/handlers/radius.handler.js +0 -296
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +0 -154
- package/dist_ts/opsserver/handlers/security.handler.d.ts +0 -11
- package/dist_ts/opsserver/handlers/security.handler.js +0 -232
- package/dist_ts/opsserver/handlers/stats.handler.d.ts +0 -13
- package/dist_ts/opsserver/handlers/stats.handler.js +0 -400
- package/dist_ts/security/classes.securitylogger.d.ts +0 -140
- package/dist_ts/security/classes.securitylogger.js +0 -235
- package/dist_ts/storage/classes.storagemanager.d.ts +0 -82
- package/dist_ts/storage/classes.storagemanager.js +0 -344
- package/dist_ts/storage/index.d.ts +0 -1
- package/dist_ts/storage/index.js +0 -3
|
@@ -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
|
+
}
|
package/ts_web/appstate.ts
CHANGED
|
@@ -109,7 +109,7 @@ export const configStatePart = await appState.getStatePart<IConfigState>(
|
|
|
109
109
|
// Determine initial view from URL path
|
|
110
110
|
const getInitialView = (): string => {
|
|
111
111
|
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
|
|
112
|
-
const validViews = ['overview', 'network', 'emails', 'logs', 'configuration', 'security', 'certificates', 'remoteingress'];
|
|
112
|
+
const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'];
|
|
113
113
|
const segments = path.split('/').filter(Boolean);
|
|
114
114
|
const view = segments[0];
|
|
115
115
|
return validViews.includes(view) ? view : 'overview';
|
|
@@ -206,6 +206,32 @@ export const remoteIngressStatePart = await appState.getStatePart<IRemoteIngress
|
|
|
206
206
|
'soft'
|
|
207
207
|
);
|
|
208
208
|
|
|
209
|
+
// ============================================================================
|
|
210
|
+
// Route Management State
|
|
211
|
+
// ============================================================================
|
|
212
|
+
|
|
213
|
+
export interface IRouteManagementState {
|
|
214
|
+
mergedRoutes: interfaces.data.IMergedRoute[];
|
|
215
|
+
warnings: interfaces.data.IRouteWarning[];
|
|
216
|
+
apiTokens: interfaces.data.IApiTokenInfo[];
|
|
217
|
+
isLoading: boolean;
|
|
218
|
+
error: string | null;
|
|
219
|
+
lastUpdated: number;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const routeManagementStatePart = await appState.getStatePart<IRouteManagementState>(
|
|
223
|
+
'routeManagement',
|
|
224
|
+
{
|
|
225
|
+
mergedRoutes: [],
|
|
226
|
+
warnings: [],
|
|
227
|
+
apiTokens: [],
|
|
228
|
+
isLoading: false,
|
|
229
|
+
error: null,
|
|
230
|
+
lastUpdated: 0,
|
|
231
|
+
},
|
|
232
|
+
'soft'
|
|
233
|
+
);
|
|
234
|
+
|
|
209
235
|
// Actions for state management
|
|
210
236
|
interface IActionContext {
|
|
211
237
|
identity: interfaces.data.IIdentity | null;
|
|
@@ -392,6 +418,20 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
|
|
|
392
418
|
}, 100);
|
|
393
419
|
}
|
|
394
420
|
|
|
421
|
+
// If switching to routes view, ensure we fetch route data
|
|
422
|
+
if (viewName === 'routes' && currentState.activeView !== 'routes') {
|
|
423
|
+
setTimeout(() => {
|
|
424
|
+
routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
425
|
+
}, 100);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// If switching to apitokens view, ensure we fetch token data
|
|
429
|
+
if (viewName === 'apitokens' && currentState.activeView !== 'apitokens') {
|
|
430
|
+
setTimeout(() => {
|
|
431
|
+
routeManagementStatePart.dispatchAction(fetchApiTokensAction, null);
|
|
432
|
+
}, 100);
|
|
433
|
+
}
|
|
434
|
+
|
|
395
435
|
// If switching to remoteingress view, ensure we fetch edge data
|
|
396
436
|
if (viewName === 'remoteingress' && currentState.activeView !== 'remoteingress') {
|
|
397
437
|
setTimeout(() => {
|
|
@@ -862,6 +902,273 @@ export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
|
862
902
|
}
|
|
863
903
|
});
|
|
864
904
|
|
|
905
|
+
// ============================================================================
|
|
906
|
+
// Route Management Actions
|
|
907
|
+
// ============================================================================
|
|
908
|
+
|
|
909
|
+
export const fetchMergedRoutesAction = routeManagementStatePart.createAction(async (statePartArg) => {
|
|
910
|
+
const context = getActionContext();
|
|
911
|
+
const currentState = statePartArg.getState();
|
|
912
|
+
|
|
913
|
+
try {
|
|
914
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
915
|
+
interfaces.requests.IReq_GetMergedRoutes
|
|
916
|
+
>('/typedrequest', 'getMergedRoutes');
|
|
917
|
+
|
|
918
|
+
const response = await request.fire({
|
|
919
|
+
identity: context.identity,
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
return {
|
|
923
|
+
...currentState,
|
|
924
|
+
mergedRoutes: response.routes,
|
|
925
|
+
warnings: response.warnings,
|
|
926
|
+
isLoading: false,
|
|
927
|
+
error: null,
|
|
928
|
+
lastUpdated: Date.now(),
|
|
929
|
+
};
|
|
930
|
+
} catch (error) {
|
|
931
|
+
return {
|
|
932
|
+
...currentState,
|
|
933
|
+
isLoading: false,
|
|
934
|
+
error: error instanceof Error ? error.message : 'Failed to fetch routes',
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
export const createRouteAction = routeManagementStatePart.createAction<{
|
|
940
|
+
route: any;
|
|
941
|
+
enabled?: boolean;
|
|
942
|
+
}>(async (statePartArg, dataArg) => {
|
|
943
|
+
const context = getActionContext();
|
|
944
|
+
const currentState = statePartArg.getState();
|
|
945
|
+
|
|
946
|
+
try {
|
|
947
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
948
|
+
interfaces.requests.IReq_CreateRoute
|
|
949
|
+
>('/typedrequest', 'createRoute');
|
|
950
|
+
|
|
951
|
+
await request.fire({
|
|
952
|
+
identity: context.identity,
|
|
953
|
+
route: dataArg.route,
|
|
954
|
+
enabled: dataArg.enabled,
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
await routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
958
|
+
return statePartArg.getState();
|
|
959
|
+
} catch (error) {
|
|
960
|
+
return {
|
|
961
|
+
...currentState,
|
|
962
|
+
error: error instanceof Error ? error.message : 'Failed to create route',
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
export const deleteRouteAction = routeManagementStatePart.createAction<string>(
|
|
968
|
+
async (statePartArg, routeId) => {
|
|
969
|
+
const context = getActionContext();
|
|
970
|
+
const currentState = statePartArg.getState();
|
|
971
|
+
|
|
972
|
+
try {
|
|
973
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
974
|
+
interfaces.requests.IReq_DeleteRoute
|
|
975
|
+
>('/typedrequest', 'deleteRoute');
|
|
976
|
+
|
|
977
|
+
await request.fire({
|
|
978
|
+
identity: context.identity,
|
|
979
|
+
id: routeId,
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
await routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
983
|
+
return statePartArg.getState();
|
|
984
|
+
} catch (error) {
|
|
985
|
+
return {
|
|
986
|
+
...currentState,
|
|
987
|
+
error: error instanceof Error ? error.message : 'Failed to delete route',
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
|
|
993
|
+
export const toggleRouteAction = routeManagementStatePart.createAction<{
|
|
994
|
+
id: string;
|
|
995
|
+
enabled: boolean;
|
|
996
|
+
}>(async (statePartArg, dataArg) => {
|
|
997
|
+
const context = getActionContext();
|
|
998
|
+
const currentState = statePartArg.getState();
|
|
999
|
+
|
|
1000
|
+
try {
|
|
1001
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1002
|
+
interfaces.requests.IReq_ToggleRoute
|
|
1003
|
+
>('/typedrequest', 'toggleRoute');
|
|
1004
|
+
|
|
1005
|
+
await request.fire({
|
|
1006
|
+
identity: context.identity,
|
|
1007
|
+
id: dataArg.id,
|
|
1008
|
+
enabled: dataArg.enabled,
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
await routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
1012
|
+
return statePartArg.getState();
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
return {
|
|
1015
|
+
...currentState,
|
|
1016
|
+
error: error instanceof Error ? error.message : 'Failed to toggle route',
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
export const setRouteOverrideAction = routeManagementStatePart.createAction<{
|
|
1022
|
+
routeName: string;
|
|
1023
|
+
enabled: boolean;
|
|
1024
|
+
}>(async (statePartArg, dataArg) => {
|
|
1025
|
+
const context = getActionContext();
|
|
1026
|
+
const currentState = statePartArg.getState();
|
|
1027
|
+
|
|
1028
|
+
try {
|
|
1029
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1030
|
+
interfaces.requests.IReq_SetRouteOverride
|
|
1031
|
+
>('/typedrequest', 'setRouteOverride');
|
|
1032
|
+
|
|
1033
|
+
await request.fire({
|
|
1034
|
+
identity: context.identity,
|
|
1035
|
+
routeName: dataArg.routeName,
|
|
1036
|
+
enabled: dataArg.enabled,
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
await routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
1040
|
+
return statePartArg.getState();
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
return {
|
|
1043
|
+
...currentState,
|
|
1044
|
+
error: error instanceof Error ? error.message : 'Failed to set override',
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
export const removeRouteOverrideAction = routeManagementStatePart.createAction<string>(
|
|
1050
|
+
async (statePartArg, routeName) => {
|
|
1051
|
+
const context = getActionContext();
|
|
1052
|
+
const currentState = statePartArg.getState();
|
|
1053
|
+
|
|
1054
|
+
try {
|
|
1055
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1056
|
+
interfaces.requests.IReq_RemoveRouteOverride
|
|
1057
|
+
>('/typedrequest', 'removeRouteOverride');
|
|
1058
|
+
|
|
1059
|
+
await request.fire({
|
|
1060
|
+
identity: context.identity,
|
|
1061
|
+
routeName,
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
await routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
1065
|
+
return statePartArg.getState();
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
return {
|
|
1068
|
+
...currentState,
|
|
1069
|
+
error: error instanceof Error ? error.message : 'Failed to remove override',
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
// ============================================================================
|
|
1076
|
+
// API Token Actions
|
|
1077
|
+
// ============================================================================
|
|
1078
|
+
|
|
1079
|
+
export const fetchApiTokensAction = routeManagementStatePart.createAction(async (statePartArg) => {
|
|
1080
|
+
const context = getActionContext();
|
|
1081
|
+
const currentState = statePartArg.getState();
|
|
1082
|
+
|
|
1083
|
+
try {
|
|
1084
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1085
|
+
interfaces.requests.IReq_ListApiTokens
|
|
1086
|
+
>('/typedrequest', 'listApiTokens');
|
|
1087
|
+
|
|
1088
|
+
const response = await request.fire({
|
|
1089
|
+
identity: context.identity,
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
return {
|
|
1093
|
+
...currentState,
|
|
1094
|
+
apiTokens: response.tokens,
|
|
1095
|
+
};
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
return {
|
|
1098
|
+
...currentState,
|
|
1099
|
+
error: error instanceof Error ? error.message : 'Failed to fetch tokens',
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
export async function createApiToken(name: string, scopes: interfaces.data.TApiTokenScope[], expiresInDays?: number | null) {
|
|
1105
|
+
const context = getActionContext();
|
|
1106
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1107
|
+
interfaces.requests.IReq_CreateApiToken
|
|
1108
|
+
>('/typedrequest', 'createApiToken');
|
|
1109
|
+
|
|
1110
|
+
return request.fire({
|
|
1111
|
+
identity: context.identity,
|
|
1112
|
+
name,
|
|
1113
|
+
scopes,
|
|
1114
|
+
expiresInDays,
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
export const revokeApiTokenAction = routeManagementStatePart.createAction<string>(
|
|
1119
|
+
async (statePartArg, tokenId) => {
|
|
1120
|
+
const context = getActionContext();
|
|
1121
|
+
const currentState = statePartArg.getState();
|
|
1122
|
+
|
|
1123
|
+
try {
|
|
1124
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1125
|
+
interfaces.requests.IReq_RevokeApiToken
|
|
1126
|
+
>('/typedrequest', 'revokeApiToken');
|
|
1127
|
+
|
|
1128
|
+
await request.fire({
|
|
1129
|
+
identity: context.identity,
|
|
1130
|
+
id: tokenId,
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
await routeManagementStatePart.dispatchAction(fetchApiTokensAction, null);
|
|
1134
|
+
return statePartArg.getState();
|
|
1135
|
+
} catch (error) {
|
|
1136
|
+
return {
|
|
1137
|
+
...currentState,
|
|
1138
|
+
error: error instanceof Error ? error.message : 'Failed to revoke token',
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
);
|
|
1143
|
+
|
|
1144
|
+
export const toggleApiTokenAction = routeManagementStatePart.createAction<{
|
|
1145
|
+
id: string;
|
|
1146
|
+
enabled: boolean;
|
|
1147
|
+
}>(async (statePartArg, dataArg) => {
|
|
1148
|
+
const context = getActionContext();
|
|
1149
|
+
const currentState = statePartArg.getState();
|
|
1150
|
+
|
|
1151
|
+
try {
|
|
1152
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1153
|
+
interfaces.requests.IReq_ToggleApiToken
|
|
1154
|
+
>('/typedrequest', 'toggleApiToken');
|
|
1155
|
+
|
|
1156
|
+
await request.fire({
|
|
1157
|
+
identity: context.identity,
|
|
1158
|
+
id: dataArg.id,
|
|
1159
|
+
enabled: dataArg.enabled,
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
await routeManagementStatePart.dispatchAction(fetchApiTokensAction, null);
|
|
1163
|
+
return statePartArg.getState();
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
return {
|
|
1166
|
+
...currentState,
|
|
1167
|
+
error: error instanceof Error ? error.message : 'Failed to toggle token',
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
|
|
865
1172
|
// ============================================================================
|
|
866
1173
|
// TypedSocket Client for Real-time Log Streaming
|
|
867
1174
|
// ============================================================================
|
package/ts_web/elements/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * from './ops-view-network.js';
|
|
|
4
4
|
export * from './ops-view-emails.js';
|
|
5
5
|
export * from './ops-view-logs.js';
|
|
6
6
|
export * from './ops-view-config.js';
|
|
7
|
+
export * from './ops-view-routes.js';
|
|
8
|
+
export * from './ops-view-apitokens.js';
|
|
7
9
|
export * from './ops-view-security.js';
|
|
8
10
|
export * from './ops-view-certificates.js';
|
|
9
11
|
export * from './ops-view-remoteingress.js';
|
|
@@ -18,6 +18,8 @@ import { OpsViewNetwork } from './ops-view-network.js';
|
|
|
18
18
|
import { OpsViewEmails } from './ops-view-emails.js';
|
|
19
19
|
import { OpsViewLogs } from './ops-view-logs.js';
|
|
20
20
|
import { OpsViewConfig } from './ops-view-config.js';
|
|
21
|
+
import { OpsViewRoutes } from './ops-view-routes.js';
|
|
22
|
+
import { OpsViewApiTokens } from './ops-view-apitokens.js';
|
|
21
23
|
import { OpsViewSecurity } from './ops-view-security.js';
|
|
22
24
|
import { OpsViewCertificates } from './ops-view-certificates.js';
|
|
23
25
|
import { OpsViewRemoteIngress } from './ops-view-remoteingress.js';
|
|
@@ -55,6 +57,14 @@ export class OpsDashboard extends DeesElement {
|
|
|
55
57
|
name: 'Logs',
|
|
56
58
|
element: OpsViewLogs,
|
|
57
59
|
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Routes',
|
|
62
|
+
element: OpsViewRoutes,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'ApiTokens',
|
|
66
|
+
element: OpsViewApiTokens,
|
|
67
|
+
},
|
|
58
68
|
{
|
|
59
69
|
name: 'Configuration',
|
|
60
70
|
element: OpsViewConfig,
|