import * as plugins from '../plugins.js'; import * as interfaces from '../../ts_interfaces/index.js'; import { appState } from './shared.js'; import { getActionContext } from './login.js'; import { captureRealtimeTicket, isRealtimeTicketCurrent } from './realtime.js'; // ============================================================================ // Remote Ingress State // ============================================================================ export interface IRemoteIngressState { edges: interfaces.data.IRemoteIngress[]; statuses: interfaces.data.IRemoteIngressStatus[]; hubSettings: interfaces.data.IRemoteIngressHubSettings | null; selectedEdgeId: string | null; newEdgeId: string | null; isLoading: boolean; error: string | null; lastUpdated: number; } export const remoteIngressStatePart = await appState.getStatePart( 'remoteIngress', { edges: [], statuses: [], hubSettings: null, selectedEdgeId: null, newEdgeId: null, isLoading: false, error: null, lastUpdated: 0, }, 'soft' ); // Remote Ingress Standalone Functions // ============================================================================ export async function fetchConnectionToken(edgeId: string) { const context = getActionContext(); const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetRemoteIngressConnectionToken >('/typedrequest', 'getRemoteIngressConnectionToken'); return request.fire({ identity: context.identity!, edgeId }); } // ============================================================================ // Remote Ingress Actions // ============================================================================ export const fetchRemoteIngressAction = remoteIngressStatePart.createAction(async (statePartArg): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; if (!context.identity) return currentState; const ticket = captureRealtimeTicket('edges'); try { const edgesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetRemoteIngresses >('/typedrequest', 'getRemoteIngresses'); const statusRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetRemoteIngressStatus >('/typedrequest', 'getRemoteIngressStatus'); const hubSettingsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetRemoteIngressHubSettings >('/typedrequest', 'getRemoteIngressHubSettings'); const [edgesResponse, statusResponse, hubSettingsResponse] = await Promise.all([ edgesRequest.fire({ identity: context.identity }), statusRequest.fire({ identity: context.identity }), hubSettingsRequest.fire({ identity: context.identity }), ]); if (!isRealtimeTicketCurrent(ticket)) return statePartArg.getState()!; return { ...statePartArg.getState()!, edges: edgesResponse.edges, statuses: statusResponse.statuses, hubSettings: hubSettingsResponse.settings, isLoading: false, error: null, lastUpdated: Date.now(), }; } catch (error) { if (!isRealtimeTicketCurrent(ticket)) return statePartArg.getState()!; return { ...statePartArg.getState()!, isLoading: false, error: error instanceof Error ? error.message : 'Failed to fetch remote ingress data', }; } }); export const createRemoteIngressAction = remoteIngressStatePart.createAction<{ name: string; listenPorts?: number[]; autoDerivePorts?: boolean; performance?: interfaces.data.IRemoteIngressPerformanceConfig; egress?: interfaces.data.IRemoteIngressEgressPolicyConfig; tags?: string[]; }>(async (statePartArg, dataArg, actionContext): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; try { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_CreateRemoteIngress >('/typedrequest', 'createRemoteIngress'); const response = await request.fire({ identity: context.identity!, name: dataArg.name, listenPorts: dataArg.listenPorts, autoDerivePorts: dataArg.autoDerivePorts, performance: dataArg.performance, egress: dataArg.egress, tags: dataArg.tags, }); if (response.success) { // Refresh the list await actionContext!.dispatch(fetchRemoteIngressAction, null); return { ...statePartArg.getState()!, newEdgeId: response.edge.id, }; } return currentState; } catch (error: unknown) { return { ...currentState, error: error instanceof Error ? error.message : 'Failed to create edge', }; } }); export const deleteRemoteIngressAction = remoteIngressStatePart.createAction( async (statePartArg, edgeId, actionContext): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; try { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_DeleteRemoteIngress >('/typedrequest', 'deleteRemoteIngress'); await request.fire({ identity: context.identity!, id: edgeId, }); return await actionContext!.dispatch(fetchRemoteIngressAction, null); } catch (error: unknown) { return { ...currentState, error: error instanceof Error ? error.message : 'Failed to delete edge', }; } } ); export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{ id: string; name?: string; listenPorts?: number[]; autoDerivePorts?: boolean; performance?: interfaces.data.IRemoteIngressPerformanceConfig; egress?: interfaces.data.IRemoteIngressEgressPolicyConfig; tags?: string[]; }>(async (statePartArg, dataArg, actionContext): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; try { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_UpdateRemoteIngress >('/typedrequest', 'updateRemoteIngress'); await request.fire({ identity: context.identity!, id: dataArg.id, name: dataArg.name, listenPorts: dataArg.listenPorts, autoDerivePorts: dataArg.autoDerivePorts, performance: dataArg.performance, egress: dataArg.egress, tags: dataArg.tags, }); return await actionContext!.dispatch(fetchRemoteIngressAction, null); } catch (error: unknown) { return { ...currentState, error: error instanceof Error ? error.message : 'Failed to update edge', }; } }); export const updateRemoteIngressHubSettingsAction = remoteIngressStatePart.createAction<{ enabled?: boolean; tunnelPort?: number; hubDomain?: string | null; performance?: interfaces.data.IRemoteIngressPerformanceConfig | null; }>(async (statePartArg, dataArg, actionContext): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; try { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_UpdateRemoteIngressHubSettings >('/typedrequest', 'updateRemoteIngressHubSettings'); const response = await request.fire({ identity: context.identity!, enabled: dataArg.enabled, tunnelPort: dataArg.tunnelPort, hubDomain: dataArg.hubDomain, performance: dataArg.performance, }); if (!response.success) { return { ...currentState, error: response.message || 'Failed to update RemoteIngress hub settings', }; } return await actionContext!.dispatch(fetchRemoteIngressAction, null); } catch (error: unknown) { return { ...currentState, error: error instanceof Error ? error.message : 'Failed to update RemoteIngress hub settings', }; } }); export const regenerateRemoteIngressSecretAction = remoteIngressStatePart.createAction( async (statePartArg, edgeId): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; try { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_RegenerateRemoteIngressSecret >('/typedrequest', 'regenerateRemoteIngressSecret'); const response = await request.fire({ identity: context.identity!, id: edgeId, }); if (response.success) { return { ...currentState, newEdgeId: edgeId, }; } return currentState; } catch (error) { return { ...currentState, error: error instanceof Error ? error.message : 'Failed to regenerate secret', }; } } ); export const clearNewEdgeIdAction = remoteIngressStatePart.createAction( async (statePartArg): Promise => { return { ...statePartArg.getState()!, newEdgeId: null, }; } ); export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{ id: string; enabled: boolean; }>(async (statePartArg, dataArg, actionContext): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; try { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_UpdateRemoteIngress >('/typedrequest', 'updateRemoteIngress'); await request.fire({ identity: context.identity!, id: dataArg.id, enabled: dataArg.enabled, }); return await actionContext!.dispatch(fetchRemoteIngressAction, null); } catch (error: unknown) { return { ...currentState, error: error instanceof Error ? error.message : 'Failed to toggle edge', }; } });