feat(gateway-clients): add managed gateway client administration and token-bound route ownership

This commit is contained in:
2026-05-09 22:35:07 +00:00
parent d73b250382
commit 8dd0c3def9
22 changed files with 1287 additions and 48 deletions
+111
View File
@@ -285,6 +285,7 @@ export interface IRouteManagementState {
mergedRoutes: interfaces.data.IMergedRoute[];
warnings: interfaces.data.IRouteWarning[];
apiTokens: interfaces.data.IApiTokenInfo[];
gatewayClients: interfaces.data.IGatewayClient[];
isLoading: boolean;
error: string | null;
lastUpdated: number;
@@ -296,6 +297,7 @@ export const routeManagementStatePart = await appState.getStatePart<IRouteManage
mergedRoutes: [],
warnings: [],
apiTokens: [],
gatewayClients: [],
isLoading: false,
error: null,
lastUpdated: 0,
@@ -2477,6 +2479,115 @@ export const fetchApiTokensAction = routeManagementStatePart.createAction(async
}
});
export const fetchGatewayClientsAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_ListGatewayClients
>('/typedrequest', 'listGatewayClients');
const response = await request.fire({ identity: context.identity });
return {
...currentState,
gatewayClients: response.gatewayClients,
error: null,
lastUpdated: Date.now(),
};
} catch (error) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to fetch gateway clients',
};
}
});
export async function createGatewayClient(data: {
id?: string;
type: interfaces.data.IGatewayClient['type'];
name: string;
description?: string;
hostnamePatterns?: string[];
allowedRouteTargets?: interfaces.data.IGatewayClient['allowedRouteTargets'];
}) {
const context = getActionContext();
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_CreateGatewayClient
>('/typedrequest', 'createGatewayClient');
return request.fire({
identity: context.identity!,
capabilities: {
readDomains: true,
readDnsRecords: true,
syncRoutes: true,
syncDnsRecords: false,
requestCertificates: false,
},
...data,
});
}
export const updateGatewayClientAction = routeManagementStatePart.createAction<{
id: string;
name?: string;
description?: string;
hostnamePatterns?: string[];
allowedRouteTargets?: interfaces.data.IGatewayClient['allowedRouteTargets'];
enabled?: boolean;
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_UpdateGatewayClient
>('/typedrequest', 'updateGatewayClient');
await request.fire({ identity: context.identity!, ...dataArg });
return await actionContext!.dispatch(fetchGatewayClientsAction, null);
} catch (error) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to update gateway client',
};
}
});
export const deleteGatewayClientAction = routeManagementStatePart.createAction<string>(
async (statePartArg, gatewayClientId, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_DeleteGatewayClient
>('/typedrequest', 'deleteGatewayClient');
await request.fire({ identity: context.identity!, id: gatewayClientId });
return await actionContext!.dispatch(fetchGatewayClientsAction, null);
} catch (error) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to delete gateway client',
};
}
},
);
export async function createGatewayClientToken(
gatewayClientId: string,
name?: string,
expiresInDays?: number | null,
) {
const context = getActionContext();
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_CreateGatewayClientToken
>('/typedrequest', 'createGatewayClientToken');
return request.fire({
identity: context.identity!,
gatewayClientId,
name,
expiresInDays,
});
}
// Users (read-only list)
export const fetchUsersAction = usersStatePart.createAction(async (statePartArg): Promise<IUsersState> => {
const context = getActionContext();