@serve.zone/dcrouter 12.0.0 → 12.2.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 +1179 -1004
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +17 -1
- package/dist_ts/classes.dcrouter.js +16 -3
- package/dist_ts/config/classes.db-seeder.d.ts +25 -0
- package/dist_ts/config/classes.db-seeder.js +69 -0
- package/dist_ts/config/classes.reference-resolver.d.ts +80 -0
- package/dist_ts/config/classes.reference-resolver.js +482 -0
- package/dist_ts/config/classes.route-config-manager.d.ts +13 -3
- package/dist_ts/config/classes.route-config-manager.js +53 -3
- package/dist_ts/config/index.d.ts +2 -0
- package/dist_ts/config/index.js +3 -1
- package/dist_ts/db/documents/classes.network-target.doc.d.ts +15 -0
- package/dist_ts/db/documents/classes.network-target.doc.js +118 -0
- package/dist_ts/db/documents/classes.security-profile.doc.d.ts +16 -0
- package/dist_ts/db/documents/classes.security-profile.doc.js +118 -0
- package/dist_ts/db/documents/classes.stored-route.doc.d.ts +2 -0
- package/dist_ts/db/documents/classes.stored-route.doc.js +8 -2
- package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +8 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.js +50 -2
- package/dist_ts/db/documents/index.d.ts +2 -0
- package/dist_ts/db/documents/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/index.d.ts +2 -0
- package/dist_ts/opsserver/handlers/index.js +3 -1
- package/dist_ts/opsserver/handlers/network-target.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/network-target.handler.js +117 -0
- package/dist_ts/opsserver/handlers/route-management.handler.js +3 -2
- package/dist_ts/opsserver/handlers/security-profile.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/security-profile.handler.js +119 -0
- package/dist_ts/opsserver/handlers/vpn.handler.js +35 -1
- package/dist_ts/vpn/classes.vpn-manager.d.ts +33 -0
- package/dist_ts/vpn/classes.vpn-manager.js +122 -7
- package/dist_ts_interfaces/data/route-management.d.ts +48 -1
- package/dist_ts_interfaces/data/vpn.d.ts +8 -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/network-targets.d.ts +102 -0
- package/dist_ts_interfaces/requests/network-targets.js +2 -0
- package/dist_ts_interfaces/requests/route-management.d.ts +3 -1
- package/dist_ts_interfaces/requests/security-profiles.d.ts +102 -0
- package/dist_ts_interfaces/requests/security-profiles.js +2 -0
- package/dist_ts_interfaces/requests/vpn.d.ts +16 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +59 -0
- package/dist_ts_web/appstate.js +192 -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 +13 -1
- package/dist_ts_web/elements/ops-view-networktargets.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-networktargets.js +246 -0
- package/dist_ts_web/elements/ops-view-securityprofiles.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-securityprofiles.js +275 -0
- package/dist_ts_web/elements/ops-view-vpn.js +155 -3
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +2 -2
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +35 -1
- package/ts/config/classes.db-seeder.ts +95 -0
- package/ts/config/classes.reference-resolver.ts +576 -0
- package/ts/config/classes.route-config-manager.ts +64 -1
- package/ts/config/index.ts +3 -1
- package/ts/db/documents/classes.network-target.doc.ts +48 -0
- package/ts/db/documents/classes.security-profile.doc.ts +49 -0
- package/ts/db/documents/classes.stored-route.doc.ts +4 -0
- package/ts/db/documents/classes.vpn-client.doc.ts +24 -0
- package/ts/db/documents/index.ts +2 -0
- package/ts/opsserver/classes.opsserver.ts +4 -0
- package/ts/opsserver/handlers/index.ts +3 -1
- package/ts/opsserver/handlers/network-target.handler.ts +167 -0
- package/ts/opsserver/handlers/route-management.handler.ts +2 -1
- package/ts/opsserver/handlers/security-profile.handler.ts +169 -0
- package/ts/opsserver/handlers/vpn.handler.ts +37 -0
- package/ts/vpn/classes.vpn-manager.ts +143 -6
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +275 -1
- package/ts_web/elements/index.ts +2 -0
- package/ts_web/elements/ops-dashboard.ts +12 -0
- package/ts_web/elements/ops-view-networktargets.ts +214 -0
- package/ts_web/elements/ops-view-securityprofiles.ts +242 -0
- package/ts_web/elements/ops-view-vpn.ts +153 -2
- package/ts_web/router.ts +1 -1
package/ts_web/appstate.ts
CHANGED
|
@@ -110,7 +110,7 @@ export const configStatePart = await appState.getStatePart<IConfigState>(
|
|
|
110
110
|
// Determine initial view from URL path
|
|
111
111
|
const getInitialView = (): string => {
|
|
112
112
|
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
|
|
113
|
-
const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'];
|
|
113
|
+
const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'securityprofiles', 'networktargets'];
|
|
114
114
|
const segments = path.split('/').filter(Boolean);
|
|
115
115
|
const view = segments[0];
|
|
116
116
|
return validViews.includes(view) ? view : 'overview';
|
|
@@ -444,6 +444,13 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
|
|
|
444
444
|
}, 100);
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
+
// If switching to security profiles or network targets views, fetch profiles/targets data
|
|
448
|
+
if ((viewName === 'securityprofiles' || viewName === 'networktargets') && currentState.activeView !== viewName) {
|
|
449
|
+
setTimeout(() => {
|
|
450
|
+
profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
|
|
451
|
+
}, 100);
|
|
452
|
+
}
|
|
453
|
+
|
|
447
454
|
return {
|
|
448
455
|
...currentState,
|
|
449
456
|
activeView: viewName,
|
|
@@ -984,6 +991,14 @@ export const createVpnClientAction = vpnStatePart.createAction<{
|
|
|
984
991
|
clientId: string;
|
|
985
992
|
serverDefinedClientTags?: string[];
|
|
986
993
|
description?: string;
|
|
994
|
+
forceDestinationSmartproxy?: boolean;
|
|
995
|
+
destinationAllowList?: string[];
|
|
996
|
+
destinationBlockList?: string[];
|
|
997
|
+
useHostIp?: boolean;
|
|
998
|
+
useDhcp?: boolean;
|
|
999
|
+
staticIp?: string;
|
|
1000
|
+
forceVlan?: boolean;
|
|
1001
|
+
vlanId?: number;
|
|
987
1002
|
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
|
|
988
1003
|
const context = getActionContext();
|
|
989
1004
|
const currentState = statePartArg.getState()!;
|
|
@@ -998,6 +1013,14 @@ export const createVpnClientAction = vpnStatePart.createAction<{
|
|
|
998
1013
|
clientId: dataArg.clientId,
|
|
999
1014
|
serverDefinedClientTags: dataArg.serverDefinedClientTags,
|
|
1000
1015
|
description: dataArg.description,
|
|
1016
|
+
forceDestinationSmartproxy: dataArg.forceDestinationSmartproxy,
|
|
1017
|
+
destinationAllowList: dataArg.destinationAllowList,
|
|
1018
|
+
destinationBlockList: dataArg.destinationBlockList,
|
|
1019
|
+
useHostIp: dataArg.useHostIp,
|
|
1020
|
+
useDhcp: dataArg.useDhcp,
|
|
1021
|
+
staticIp: dataArg.staticIp,
|
|
1022
|
+
forceVlan: dataArg.forceVlan,
|
|
1023
|
+
vlanId: dataArg.vlanId,
|
|
1001
1024
|
});
|
|
1002
1025
|
|
|
1003
1026
|
if (!response.success) {
|
|
@@ -1066,6 +1089,14 @@ export const updateVpnClientAction = vpnStatePart.createAction<{
|
|
|
1066
1089
|
clientId: string;
|
|
1067
1090
|
description?: string;
|
|
1068
1091
|
serverDefinedClientTags?: string[];
|
|
1092
|
+
forceDestinationSmartproxy?: boolean;
|
|
1093
|
+
destinationAllowList?: string[];
|
|
1094
|
+
destinationBlockList?: string[];
|
|
1095
|
+
useHostIp?: boolean;
|
|
1096
|
+
useDhcp?: boolean;
|
|
1097
|
+
staticIp?: string;
|
|
1098
|
+
forceVlan?: boolean;
|
|
1099
|
+
vlanId?: number;
|
|
1069
1100
|
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
|
|
1070
1101
|
const context = getActionContext();
|
|
1071
1102
|
const currentState = statePartArg.getState()!;
|
|
@@ -1080,6 +1111,14 @@ export const updateVpnClientAction = vpnStatePart.createAction<{
|
|
|
1080
1111
|
clientId: dataArg.clientId,
|
|
1081
1112
|
description: dataArg.description,
|
|
1082
1113
|
serverDefinedClientTags: dataArg.serverDefinedClientTags,
|
|
1114
|
+
forceDestinationSmartproxy: dataArg.forceDestinationSmartproxy,
|
|
1115
|
+
destinationAllowList: dataArg.destinationAllowList,
|
|
1116
|
+
destinationBlockList: dataArg.destinationBlockList,
|
|
1117
|
+
useHostIp: dataArg.useHostIp,
|
|
1118
|
+
useDhcp: dataArg.useDhcp,
|
|
1119
|
+
staticIp: dataArg.staticIp,
|
|
1120
|
+
forceVlan: dataArg.forceVlan,
|
|
1121
|
+
vlanId: dataArg.vlanId,
|
|
1083
1122
|
});
|
|
1084
1123
|
|
|
1085
1124
|
if (!response.success) {
|
|
@@ -1101,6 +1140,241 @@ export const clearNewClientConfigAction = vpnStatePart.createAction(
|
|
|
1101
1140
|
},
|
|
1102
1141
|
);
|
|
1103
1142
|
|
|
1143
|
+
// ============================================================================
|
|
1144
|
+
// Security Profiles & Network Targets State
|
|
1145
|
+
// ============================================================================
|
|
1146
|
+
|
|
1147
|
+
export interface IProfilesTargetsState {
|
|
1148
|
+
profiles: interfaces.data.ISecurityProfile[];
|
|
1149
|
+
targets: interfaces.data.INetworkTarget[];
|
|
1150
|
+
isLoading: boolean;
|
|
1151
|
+
error: string | null;
|
|
1152
|
+
lastUpdated: number;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
export const profilesTargetsStatePart = await appState.getStatePart<IProfilesTargetsState>(
|
|
1156
|
+
'profilesTargets',
|
|
1157
|
+
{
|
|
1158
|
+
profiles: [],
|
|
1159
|
+
targets: [],
|
|
1160
|
+
isLoading: false,
|
|
1161
|
+
error: null,
|
|
1162
|
+
lastUpdated: 0,
|
|
1163
|
+
},
|
|
1164
|
+
'soft'
|
|
1165
|
+
);
|
|
1166
|
+
|
|
1167
|
+
// ============================================================================
|
|
1168
|
+
// Security Profiles & Network Targets Actions
|
|
1169
|
+
// ============================================================================
|
|
1170
|
+
|
|
1171
|
+
export const fetchProfilesAndTargetsAction = profilesTargetsStatePart.createAction(
|
|
1172
|
+
async (statePartArg): Promise<IProfilesTargetsState> => {
|
|
1173
|
+
const context = getActionContext();
|
|
1174
|
+
const currentState = statePartArg.getState()!;
|
|
1175
|
+
if (!context.identity) return currentState;
|
|
1176
|
+
|
|
1177
|
+
try {
|
|
1178
|
+
const profilesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1179
|
+
interfaces.requests.IReq_GetSecurityProfiles
|
|
1180
|
+
>('/typedrequest', 'getSecurityProfiles');
|
|
1181
|
+
|
|
1182
|
+
const targetsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1183
|
+
interfaces.requests.IReq_GetNetworkTargets
|
|
1184
|
+
>('/typedrequest', 'getNetworkTargets');
|
|
1185
|
+
|
|
1186
|
+
const [profilesResponse, targetsResponse] = await Promise.all([
|
|
1187
|
+
profilesRequest.fire({ identity: context.identity }),
|
|
1188
|
+
targetsRequest.fire({ identity: context.identity }),
|
|
1189
|
+
]);
|
|
1190
|
+
|
|
1191
|
+
return {
|
|
1192
|
+
profiles: profilesResponse.profiles,
|
|
1193
|
+
targets: targetsResponse.targets,
|
|
1194
|
+
isLoading: false,
|
|
1195
|
+
error: null,
|
|
1196
|
+
lastUpdated: Date.now(),
|
|
1197
|
+
};
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
return {
|
|
1200
|
+
...currentState,
|
|
1201
|
+
isLoading: false,
|
|
1202
|
+
error: error instanceof Error ? error.message : 'Failed to fetch profiles/targets',
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
);
|
|
1207
|
+
|
|
1208
|
+
export const createProfileAction = profilesTargetsStatePart.createAction<{
|
|
1209
|
+
name: string;
|
|
1210
|
+
description?: string;
|
|
1211
|
+
security: any;
|
|
1212
|
+
extendsProfiles?: string[];
|
|
1213
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1214
|
+
const context = getActionContext();
|
|
1215
|
+
try {
|
|
1216
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1217
|
+
interfaces.requests.IReq_CreateSecurityProfile
|
|
1218
|
+
>('/typedrequest', 'createSecurityProfile');
|
|
1219
|
+
await request.fire({
|
|
1220
|
+
identity: context.identity!,
|
|
1221
|
+
name: dataArg.name,
|
|
1222
|
+
description: dataArg.description,
|
|
1223
|
+
security: dataArg.security,
|
|
1224
|
+
extendsProfiles: dataArg.extendsProfiles,
|
|
1225
|
+
});
|
|
1226
|
+
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1227
|
+
} catch (error: unknown) {
|
|
1228
|
+
return {
|
|
1229
|
+
...statePartArg.getState()!,
|
|
1230
|
+
error: error instanceof Error ? error.message : 'Failed to create profile',
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
export const updateProfileAction = profilesTargetsStatePart.createAction<{
|
|
1236
|
+
id: string;
|
|
1237
|
+
name?: string;
|
|
1238
|
+
description?: string;
|
|
1239
|
+
security?: any;
|
|
1240
|
+
extendsProfiles?: string[];
|
|
1241
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1242
|
+
const context = getActionContext();
|
|
1243
|
+
try {
|
|
1244
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1245
|
+
interfaces.requests.IReq_UpdateSecurityProfile
|
|
1246
|
+
>('/typedrequest', 'updateSecurityProfile');
|
|
1247
|
+
await request.fire({
|
|
1248
|
+
identity: context.identity!,
|
|
1249
|
+
id: dataArg.id,
|
|
1250
|
+
name: dataArg.name,
|
|
1251
|
+
description: dataArg.description,
|
|
1252
|
+
security: dataArg.security,
|
|
1253
|
+
extendsProfiles: dataArg.extendsProfiles,
|
|
1254
|
+
});
|
|
1255
|
+
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1256
|
+
} catch (error: unknown) {
|
|
1257
|
+
return {
|
|
1258
|
+
...statePartArg.getState()!,
|
|
1259
|
+
error: error instanceof Error ? error.message : 'Failed to update profile',
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
export const deleteProfileAction = profilesTargetsStatePart.createAction<{
|
|
1265
|
+
id: string;
|
|
1266
|
+
force?: boolean;
|
|
1267
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1268
|
+
const context = getActionContext();
|
|
1269
|
+
try {
|
|
1270
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1271
|
+
interfaces.requests.IReq_DeleteSecurityProfile
|
|
1272
|
+
>('/typedrequest', 'deleteSecurityProfile');
|
|
1273
|
+
const response = await request.fire({
|
|
1274
|
+
identity: context.identity!,
|
|
1275
|
+
id: dataArg.id,
|
|
1276
|
+
force: dataArg.force,
|
|
1277
|
+
});
|
|
1278
|
+
if (!response.success) {
|
|
1279
|
+
return {
|
|
1280
|
+
...statePartArg.getState()!,
|
|
1281
|
+
error: response.message || 'Failed to delete profile',
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1285
|
+
} catch (error: unknown) {
|
|
1286
|
+
return {
|
|
1287
|
+
...statePartArg.getState()!,
|
|
1288
|
+
error: error instanceof Error ? error.message : 'Failed to delete profile',
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
export const createTargetAction = profilesTargetsStatePart.createAction<{
|
|
1294
|
+
name: string;
|
|
1295
|
+
description?: string;
|
|
1296
|
+
host: string | string[];
|
|
1297
|
+
port: number;
|
|
1298
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1299
|
+
const context = getActionContext();
|
|
1300
|
+
try {
|
|
1301
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1302
|
+
interfaces.requests.IReq_CreateNetworkTarget
|
|
1303
|
+
>('/typedrequest', 'createNetworkTarget');
|
|
1304
|
+
await request.fire({
|
|
1305
|
+
identity: context.identity!,
|
|
1306
|
+
name: dataArg.name,
|
|
1307
|
+
description: dataArg.description,
|
|
1308
|
+
host: dataArg.host,
|
|
1309
|
+
port: dataArg.port,
|
|
1310
|
+
});
|
|
1311
|
+
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1312
|
+
} catch (error: unknown) {
|
|
1313
|
+
return {
|
|
1314
|
+
...statePartArg.getState()!,
|
|
1315
|
+
error: error instanceof Error ? error.message : 'Failed to create target',
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
export const updateTargetAction = profilesTargetsStatePart.createAction<{
|
|
1321
|
+
id: string;
|
|
1322
|
+
name?: string;
|
|
1323
|
+
description?: string;
|
|
1324
|
+
host?: string | string[];
|
|
1325
|
+
port?: number;
|
|
1326
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1327
|
+
const context = getActionContext();
|
|
1328
|
+
try {
|
|
1329
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1330
|
+
interfaces.requests.IReq_UpdateNetworkTarget
|
|
1331
|
+
>('/typedrequest', 'updateNetworkTarget');
|
|
1332
|
+
await request.fire({
|
|
1333
|
+
identity: context.identity!,
|
|
1334
|
+
id: dataArg.id,
|
|
1335
|
+
name: dataArg.name,
|
|
1336
|
+
description: dataArg.description,
|
|
1337
|
+
host: dataArg.host,
|
|
1338
|
+
port: dataArg.port,
|
|
1339
|
+
});
|
|
1340
|
+
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1341
|
+
} catch (error: unknown) {
|
|
1342
|
+
return {
|
|
1343
|
+
...statePartArg.getState()!,
|
|
1344
|
+
error: error instanceof Error ? error.message : 'Failed to update target',
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
export const deleteTargetAction = profilesTargetsStatePart.createAction<{
|
|
1350
|
+
id: string;
|
|
1351
|
+
force?: boolean;
|
|
1352
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1353
|
+
const context = getActionContext();
|
|
1354
|
+
try {
|
|
1355
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1356
|
+
interfaces.requests.IReq_DeleteNetworkTarget
|
|
1357
|
+
>('/typedrequest', 'deleteNetworkTarget');
|
|
1358
|
+
const response = await request.fire({
|
|
1359
|
+
identity: context.identity!,
|
|
1360
|
+
id: dataArg.id,
|
|
1361
|
+
force: dataArg.force,
|
|
1362
|
+
});
|
|
1363
|
+
if (!response.success) {
|
|
1364
|
+
return {
|
|
1365
|
+
...statePartArg.getState()!,
|
|
1366
|
+
error: response.message || 'Failed to delete target',
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1370
|
+
} catch (error: unknown) {
|
|
1371
|
+
return {
|
|
1372
|
+
...statePartArg.getState()!,
|
|
1373
|
+
error: error instanceof Error ? error.message : 'Failed to delete target',
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1104
1378
|
// ============================================================================
|
|
1105
1379
|
// Route Management Actions
|
|
1106
1380
|
// ============================================================================
|
package/ts_web/elements/index.ts
CHANGED
|
@@ -10,4 +10,6 @@ export * from './ops-view-security.js';
|
|
|
10
10
|
export * from './ops-view-certificates.js';
|
|
11
11
|
export * from './ops-view-remoteingress.js';
|
|
12
12
|
export * from './ops-view-vpn.js';
|
|
13
|
+
export * from './ops-view-securityprofiles.js';
|
|
14
|
+
export * from './ops-view-networktargets.js';
|
|
13
15
|
export * from './shared/index.js';
|
|
@@ -25,6 +25,8 @@ import { OpsViewSecurity } from './ops-view-security.js';
|
|
|
25
25
|
import { OpsViewCertificates } from './ops-view-certificates.js';
|
|
26
26
|
import { OpsViewRemoteIngress } from './ops-view-remoteingress.js';
|
|
27
27
|
import { OpsViewVpn } from './ops-view-vpn.js';
|
|
28
|
+
import { OpsViewSecurityProfiles } from './ops-view-securityprofiles.js';
|
|
29
|
+
import { OpsViewNetworkTargets } from './ops-view-networktargets.js';
|
|
28
30
|
|
|
29
31
|
@customElement('ops-dashboard')
|
|
30
32
|
export class OpsDashboard extends DeesElement {
|
|
@@ -73,6 +75,16 @@ export class OpsDashboard extends DeesElement {
|
|
|
73
75
|
iconName: 'lucide:route',
|
|
74
76
|
element: OpsViewRoutes,
|
|
75
77
|
},
|
|
78
|
+
{
|
|
79
|
+
name: 'SecurityProfiles',
|
|
80
|
+
iconName: 'lucide:shieldCheck',
|
|
81
|
+
element: OpsViewSecurityProfiles,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'NetworkTargets',
|
|
85
|
+
iconName: 'lucide:server',
|
|
86
|
+
element: OpsViewNetworkTargets,
|
|
87
|
+
},
|
|
76
88
|
{
|
|
77
89
|
name: 'ApiTokens',
|
|
78
90
|
iconName: 'lucide:key',
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeesElement,
|
|
3
|
+
html,
|
|
4
|
+
customElement,
|
|
5
|
+
type TemplateResult,
|
|
6
|
+
css,
|
|
7
|
+
state,
|
|
8
|
+
cssManager,
|
|
9
|
+
} from '@design.estate/dees-element';
|
|
10
|
+
import * as appstate from '../appstate.js';
|
|
11
|
+
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
12
|
+
import { viewHostCss } from './shared/css.js';
|
|
13
|
+
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
'ops-view-networktargets': OpsViewNetworkTargets;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@customElement('ops-view-networktargets')
|
|
22
|
+
export class OpsViewNetworkTargets extends DeesElement {
|
|
23
|
+
@state()
|
|
24
|
+
accessor profilesState: appstate.IProfilesTargetsState = appstate.profilesTargetsStatePart.getState()!;
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
super();
|
|
28
|
+
const sub = appstate.profilesTargetsStatePart.select().subscribe((newState) => {
|
|
29
|
+
this.profilesState = newState;
|
|
30
|
+
});
|
|
31
|
+
this.rxSubscriptions.push(sub);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async connectedCallback() {
|
|
35
|
+
await super.connectedCallback();
|
|
36
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public static styles = [
|
|
40
|
+
cssManager.defaultStyles,
|
|
41
|
+
viewHostCss,
|
|
42
|
+
css`
|
|
43
|
+
.targetsContainer {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: 24px;
|
|
47
|
+
}
|
|
48
|
+
`,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
public render(): TemplateResult {
|
|
52
|
+
const targets = this.profilesState.targets;
|
|
53
|
+
|
|
54
|
+
const statsTiles: IStatsTile[] = [
|
|
55
|
+
{
|
|
56
|
+
id: 'totalTargets',
|
|
57
|
+
title: 'Total Targets',
|
|
58
|
+
type: 'number',
|
|
59
|
+
value: targets.length,
|
|
60
|
+
icon: 'lucide:server',
|
|
61
|
+
description: 'Reusable network targets',
|
|
62
|
+
color: '#8b5cf6',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
return html`
|
|
67
|
+
<div class="targetsContainer">
|
|
68
|
+
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
69
|
+
<dees-table
|
|
70
|
+
.heading=${'Network Targets'}
|
|
71
|
+
.data=${targets}
|
|
72
|
+
.displayFunction=${(target: interfaces.data.INetworkTarget) => ({
|
|
73
|
+
Name: target.name,
|
|
74
|
+
Host: Array.isArray(target.host) ? target.host.join(', ') : target.host,
|
|
75
|
+
Port: target.port,
|
|
76
|
+
Description: target.description || '-',
|
|
77
|
+
})}
|
|
78
|
+
.dataActions=${[
|
|
79
|
+
{
|
|
80
|
+
name: 'Create Target',
|
|
81
|
+
iconName: 'lucide:plus',
|
|
82
|
+
type: ['header' as const],
|
|
83
|
+
action: async (_: any, table: any) => {
|
|
84
|
+
await this.showCreateTargetDialog(table);
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'Refresh',
|
|
89
|
+
iconName: 'lucide:rotateCw',
|
|
90
|
+
type: ['header' as const],
|
|
91
|
+
action: async () => {
|
|
92
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'Edit',
|
|
97
|
+
iconName: 'lucide:pencil',
|
|
98
|
+
type: ['contextmenu' as const],
|
|
99
|
+
action: async (target: interfaces.data.INetworkTarget, table: any) => {
|
|
100
|
+
await this.showEditTargetDialog(target, table);
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'Delete',
|
|
105
|
+
iconName: 'lucide:trash2',
|
|
106
|
+
type: ['contextmenu' as const],
|
|
107
|
+
action: async (target: interfaces.data.INetworkTarget) => {
|
|
108
|
+
await this.deleteTarget(target);
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
]}
|
|
112
|
+
></dees-table>
|
|
113
|
+
</div>
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private async showCreateTargetDialog(table: any) {
|
|
118
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
119
|
+
DeesModal.createAndShow({
|
|
120
|
+
heading: 'Create Network Target',
|
|
121
|
+
content: html`
|
|
122
|
+
<dees-form>
|
|
123
|
+
<dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
|
|
124
|
+
<dees-input-text .key=${'host'} .label=${'Host'} .required=${true}></dees-input-text>
|
|
125
|
+
<dees-input-text .key=${'port'} .label=${'Port'} .required=${true} .value=${'443'}></dees-input-text>
|
|
126
|
+
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
|
127
|
+
</dees-form>
|
|
128
|
+
`,
|
|
129
|
+
menuOptions: [
|
|
130
|
+
{
|
|
131
|
+
name: 'Create',
|
|
132
|
+
action: async (modalArg: any) => {
|
|
133
|
+
const form = modalArg.shadowRoot!.querySelector('dees-form');
|
|
134
|
+
const data = await form.collectFormData();
|
|
135
|
+
|
|
136
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.createTargetAction, {
|
|
137
|
+
name: String(data.name),
|
|
138
|
+
description: data.description ? String(data.description) : undefined,
|
|
139
|
+
host: String(data.host),
|
|
140
|
+
port: parseInt(String(data.port)) || 443,
|
|
141
|
+
});
|
|
142
|
+
modalArg.destroy();
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async showEditTargetDialog(target: interfaces.data.INetworkTarget, table: any) {
|
|
151
|
+
const hostStr = Array.isArray(target.host) ? target.host.join(', ') : target.host;
|
|
152
|
+
|
|
153
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
154
|
+
DeesModal.createAndShow({
|
|
155
|
+
heading: `Edit Target: ${target.name}`,
|
|
156
|
+
content: html`
|
|
157
|
+
<dees-form>
|
|
158
|
+
<dees-input-text .key=${'name'} .label=${'Name'} .value=${target.name}></dees-input-text>
|
|
159
|
+
<dees-input-text .key=${'host'} .label=${'Host'} .value=${hostStr}></dees-input-text>
|
|
160
|
+
<dees-input-text .key=${'port'} .label=${'Port'} .value=${String(target.port)}></dees-input-text>
|
|
161
|
+
<dees-input-text .key=${'description'} .label=${'Description'} .value=${target.description || ''}></dees-input-text>
|
|
162
|
+
</dees-form>
|
|
163
|
+
`,
|
|
164
|
+
menuOptions: [
|
|
165
|
+
{
|
|
166
|
+
name: 'Save',
|
|
167
|
+
action: async (modalArg: any) => {
|
|
168
|
+
const form = modalArg.shadowRoot!.querySelector('dees-form');
|
|
169
|
+
const data = await form.collectFormData();
|
|
170
|
+
|
|
171
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.updateTargetAction, {
|
|
172
|
+
id: target.id,
|
|
173
|
+
name: String(data.name),
|
|
174
|
+
description: data.description ? String(data.description) : undefined,
|
|
175
|
+
host: String(data.host),
|
|
176
|
+
port: parseInt(String(data.port)) || 443,
|
|
177
|
+
});
|
|
178
|
+
modalArg.destroy();
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
182
|
+
],
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private async deleteTarget(target: interfaces.data.INetworkTarget) {
|
|
187
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteTargetAction, {
|
|
188
|
+
id: target.id,
|
|
189
|
+
force: false,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const currentState = appstate.profilesTargetsStatePart.getState()!;
|
|
193
|
+
if (currentState.error?.includes('in use')) {
|
|
194
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
195
|
+
DeesModal.createAndShow({
|
|
196
|
+
heading: 'Target In Use',
|
|
197
|
+
content: html`<p>${currentState.error} Force delete?</p>`,
|
|
198
|
+
menuOptions: [
|
|
199
|
+
{
|
|
200
|
+
name: 'Force Delete',
|
|
201
|
+
action: async (modalArg: any) => {
|
|
202
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteTargetAction, {
|
|
203
|
+
id: target.id,
|
|
204
|
+
force: true,
|
|
205
|
+
});
|
|
206
|
+
modalArg.destroy();
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|