@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.
Files changed (99) hide show
  1. package/dist_serve/bundle.js +1659 -891
  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/{remoteingress.handler.d.ts → api-token.handler.d.ts} +5 -1
  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/index.d.ts +2 -0
  26. package/dist_ts_interfaces/requests/index.js +3 -1
  27. package/dist_ts_interfaces/requests/route-management.d.ts +114 -0
  28. package/dist_ts_interfaces/requests/route-management.js +2 -0
  29. package/dist_ts_web/00_commitinfo_data.js +1 -1
  30. package/dist_ts_web/appstate.d.ts +36 -0
  31. package/dist_ts_web/appstate.js +220 -2
  32. package/dist_ts_web/elements/index.d.ts +2 -0
  33. package/dist_ts_web/elements/index.js +3 -1
  34. package/dist_ts_web/elements/ops-dashboard.js +11 -1
  35. package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
  36. package/dist_ts_web/elements/ops-view-apitokens.js +306 -0
  37. package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
  38. package/dist_ts_web/elements/ops-view-routes.js +404 -0
  39. package/dist_ts_web/router.d.ts +1 -1
  40. package/dist_ts_web/router.js +2 -2
  41. package/package.json +2 -2
  42. package/ts/00_commitinfo_data.ts +1 -1
  43. package/ts/classes.dcrouter.ts +37 -1
  44. package/ts/config/classes.api-token-manager.ts +155 -0
  45. package/ts/config/classes.route-config-manager.ts +271 -0
  46. package/ts/config/index.ts +3 -1
  47. package/ts/opsserver/classes.opsserver.ts +4 -0
  48. package/ts/opsserver/handlers/api-token.handler.ts +96 -0
  49. package/ts/opsserver/handlers/index.ts +3 -1
  50. package/ts/opsserver/handlers/route-management.handler.ts +163 -0
  51. package/ts_web/00_commitinfo_data.ts +1 -1
  52. package/ts_web/appstate.ts +308 -1
  53. package/ts_web/elements/index.ts +2 -0
  54. package/ts_web/elements/ops-dashboard.ts +10 -0
  55. package/ts_web/elements/ops-view-apitokens.ts +281 -0
  56. package/ts_web/elements/ops-view-routes.ts +389 -0
  57. package/ts_web/router.ts +1 -1
  58. package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
  59. package/dist_ts/cache/classes.cache.cleaner.js +0 -130
  60. package/dist_ts/cache/classes.cached.document.d.ts +0 -76
  61. package/dist_ts/cache/classes.cached.document.js +0 -100
  62. package/dist_ts/cache/classes.cachedb.d.ts +0 -60
  63. package/dist_ts/cache/classes.cachedb.js +0 -126
  64. package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
  65. package/dist_ts/cache/documents/classes.cached.email.js +0 -337
  66. package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
  67. package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
  68. package/dist_ts/cache/documents/index.d.ts +0 -2
  69. package/dist_ts/cache/documents/index.js +0 -3
  70. package/dist_ts/cache/index.d.ts +0 -4
  71. package/dist_ts/cache/index.js +0 -7
  72. package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
  73. package/dist_ts/monitoring/classes.metricscache.js +0 -63
  74. package/dist_ts/monitoring/classes.metricsmanager.d.ts +0 -169
  75. package/dist_ts/monitoring/classes.metricsmanager.js +0 -591
  76. package/dist_ts/monitoring/index.d.ts +0 -1
  77. package/dist_ts/monitoring/index.js +0 -2
  78. package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
  79. package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
  80. package/dist_ts/opsserver/handlers/certificate.handler.d.ts +0 -34
  81. package/dist_ts/opsserver/handlers/certificate.handler.js +0 -419
  82. package/dist_ts/opsserver/handlers/config.handler.d.ts +0 -9
  83. package/dist_ts/opsserver/handlers/config.handler.js +0 -67
  84. package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +0 -32
  85. package/dist_ts/opsserver/handlers/email-ops.handler.js +0 -226
  86. package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
  87. package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
  88. package/dist_ts/opsserver/handlers/radius.handler.js +0 -296
  89. package/dist_ts/opsserver/handlers/remoteingress.handler.js +0 -154
  90. package/dist_ts/opsserver/handlers/security.handler.d.ts +0 -11
  91. package/dist_ts/opsserver/handlers/security.handler.js +0 -232
  92. package/dist_ts/opsserver/handlers/stats.handler.d.ts +0 -13
  93. package/dist_ts/opsserver/handlers/stats.handler.js +0 -400
  94. package/dist_ts/security/classes.securitylogger.d.ts +0 -140
  95. package/dist_ts/security/classes.securitylogger.js +0 -235
  96. package/dist_ts/storage/classes.storagemanager.d.ts +0 -82
  97. package/dist_ts/storage/classes.storagemanager.js +0 -344
  98. package/dist_ts/storage/index.d.ts +0 -1
  99. 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
+ }
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '8.0.0',
6
+ version: '8.1.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -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
  // ============================================================================
@@ -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,