feat(config): add reusable security profiles and network targets with route reference resolution

This commit is contained in:
2026-04-02 15:44:36 +00:00
parent 6344c2deae
commit 55699f6618
31 changed files with 2845 additions and 12 deletions

View File

@@ -110,7 +110,7 @@ export const configStatePart = await appState.getStatePart<IConfigState>(
// Determine initial view from URL path
const getInitialView = (): string => {
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'];
const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'securityprofiles', 'networktargets'];
const segments = path.split('/').filter(Boolean);
const view = segments[0];
return validViews.includes(view) ? view : 'overview';
@@ -444,6 +444,13 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
}, 100);
}
// If switching to security profiles or network targets views, fetch profiles/targets data
if ((viewName === 'securityprofiles' || viewName === 'networktargets') && currentState.activeView !== viewName) {
setTimeout(() => {
profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
}, 100);
}
return {
...currentState,
activeView: viewName,
@@ -1133,6 +1140,241 @@ export const clearNewClientConfigAction = vpnStatePart.createAction(
},
);
// ============================================================================
// Security Profiles & Network Targets State
// ============================================================================
export interface IProfilesTargetsState {
profiles: interfaces.data.ISecurityProfile[];
targets: interfaces.data.INetworkTarget[];
isLoading: boolean;
error: string | null;
lastUpdated: number;
}
export const profilesTargetsStatePart = await appState.getStatePart<IProfilesTargetsState>(
'profilesTargets',
{
profiles: [],
targets: [],
isLoading: false,
error: null,
lastUpdated: 0,
},
'soft'
);
// ============================================================================
// Security Profiles & Network Targets Actions
// ============================================================================
export const fetchProfilesAndTargetsAction = profilesTargetsStatePart.createAction(
async (statePartArg): Promise<IProfilesTargetsState> => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
const profilesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetSecurityProfiles
>('/typedrequest', 'getSecurityProfiles');
const targetsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetNetworkTargets
>('/typedrequest', 'getNetworkTargets');
const [profilesResponse, targetsResponse] = await Promise.all([
profilesRequest.fire({ identity: context.identity }),
targetsRequest.fire({ identity: context.identity }),
]);
return {
profiles: profilesResponse.profiles,
targets: targetsResponse.targets,
isLoading: false,
error: null,
lastUpdated: Date.now(),
};
} catch (error) {
return {
...currentState,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch profiles/targets',
};
}
}
);
export const createProfileAction = profilesTargetsStatePart.createAction<{
name: string;
description?: string;
security: any;
extendsProfiles?: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
const context = getActionContext();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_CreateSecurityProfile
>('/typedrequest', 'createSecurityProfile');
await request.fire({
identity: context.identity!,
name: dataArg.name,
description: dataArg.description,
security: dataArg.security,
extendsProfiles: dataArg.extendsProfiles,
});
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
} catch (error: unknown) {
return {
...statePartArg.getState()!,
error: error instanceof Error ? error.message : 'Failed to create profile',
};
}
});
export const updateProfileAction = profilesTargetsStatePart.createAction<{
id: string;
name?: string;
description?: string;
security?: any;
extendsProfiles?: string[];
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
const context = getActionContext();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_UpdateSecurityProfile
>('/typedrequest', 'updateSecurityProfile');
await request.fire({
identity: context.identity!,
id: dataArg.id,
name: dataArg.name,
description: dataArg.description,
security: dataArg.security,
extendsProfiles: dataArg.extendsProfiles,
});
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
} catch (error: unknown) {
return {
...statePartArg.getState()!,
error: error instanceof Error ? error.message : 'Failed to update profile',
};
}
});
export const deleteProfileAction = profilesTargetsStatePart.createAction<{
id: string;
force?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
const context = getActionContext();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_DeleteSecurityProfile
>('/typedrequest', 'deleteSecurityProfile');
const response = await request.fire({
identity: context.identity!,
id: dataArg.id,
force: dataArg.force,
});
if (!response.success) {
return {
...statePartArg.getState()!,
error: response.message || 'Failed to delete profile',
};
}
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
} catch (error: unknown) {
return {
...statePartArg.getState()!,
error: error instanceof Error ? error.message : 'Failed to delete profile',
};
}
});
export const createTargetAction = profilesTargetsStatePart.createAction<{
name: string;
description?: string;
host: string | string[];
port: number;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
const context = getActionContext();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_CreateNetworkTarget
>('/typedrequest', 'createNetworkTarget');
await request.fire({
identity: context.identity!,
name: dataArg.name,
description: dataArg.description,
host: dataArg.host,
port: dataArg.port,
});
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
} catch (error: unknown) {
return {
...statePartArg.getState()!,
error: error instanceof Error ? error.message : 'Failed to create target',
};
}
});
export const updateTargetAction = profilesTargetsStatePart.createAction<{
id: string;
name?: string;
description?: string;
host?: string | string[];
port?: number;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
const context = getActionContext();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_UpdateNetworkTarget
>('/typedrequest', 'updateNetworkTarget');
await request.fire({
identity: context.identity!,
id: dataArg.id,
name: dataArg.name,
description: dataArg.description,
host: dataArg.host,
port: dataArg.port,
});
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
} catch (error: unknown) {
return {
...statePartArg.getState()!,
error: error instanceof Error ? error.message : 'Failed to update target',
};
}
});
export const deleteTargetAction = profilesTargetsStatePart.createAction<{
id: string;
force?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
const context = getActionContext();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_DeleteNetworkTarget
>('/typedrequest', 'deleteNetworkTarget');
const response = await request.fire({
identity: context.identity!,
id: dataArg.id,
force: dataArg.force,
});
if (!response.success) {
return {
...statePartArg.getState()!,
error: response.message || 'Failed to delete target',
};
}
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
} catch (error: unknown) {
return {
...statePartArg.getState()!,
error: error instanceof Error ? error.message : 'Failed to delete target',
};
}
});
// ============================================================================
// Route Management Actions
// ============================================================================