@serve.zone/dcrouter 15.0.2 → 15.0.4

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 (72) hide show
  1. package/deno.json +1 -1
  2. package/dist_serve/bundle.js +768 -768
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/classes.dcrouter.js +9 -1
  5. package/dist_ts_web/00_commitinfo_data.js +1 -1
  6. package/dist_ts_web/appstate/acme.d.ts +17 -0
  7. package/dist_ts_web/appstate/acme.js +64 -0
  8. package/dist_ts_web/appstate/certificates.d.ts +37 -0
  9. package/dist_ts_web/appstate/certificates.js +107 -0
  10. package/dist_ts_web/appstate/config.d.ts +9 -0
  11. package/dist_ts_web/appstate/config.js +35 -0
  12. package/dist_ts_web/appstate/domains.d.ts +80 -0
  13. package/dist_ts_web/appstate/domains.js +324 -0
  14. package/dist_ts_web/appstate/email-domains.d.ts +25 -0
  15. package/dist_ts_web/appstate/email-domains.js +104 -0
  16. package/dist_ts_web/appstate/email-ops.d.ts +10 -0
  17. package/dist_ts_web/appstate/email-ops.js +40 -0
  18. package/dist_ts_web/appstate/login.d.ts +30 -0
  19. package/dist_ts_web/appstate/login.js +83 -0
  20. package/dist_ts_web/appstate/logs.d.ts +16 -0
  21. package/dist_ts_web/appstate/logs.js +27 -0
  22. package/dist_ts_web/appstate/network.d.ts +50 -0
  23. package/dist_ts_web/appstate/network.js +122 -0
  24. package/dist_ts_web/appstate/profiles-targets.d.ts +45 -0
  25. package/dist_ts_web/appstate/profiles-targets.js +173 -0
  26. package/dist_ts_web/appstate/remoteingress.d.ts +47 -0
  27. package/dist_ts_web/appstate/remoteingress.js +204 -0
  28. package/dist_ts_web/appstate/routes.d.ts +76 -0
  29. package/dist_ts_web/appstate/routes.js +316 -0
  30. package/dist_ts_web/appstate/runtime.d.ts +1 -0
  31. package/dist_ts_web/appstate/runtime.js +276 -0
  32. package/dist_ts_web/appstate/security.d.ts +29 -0
  33. package/dist_ts_web/appstate/security.js +167 -0
  34. package/dist_ts_web/appstate/shared.d.ts +3 -0
  35. package/dist_ts_web/appstate/shared.js +13 -0
  36. package/dist_ts_web/appstate/stats.d.ts +15 -0
  37. package/dist_ts_web/appstate/stats.js +59 -0
  38. package/dist_ts_web/appstate/target-profiles.d.ts +37 -0
  39. package/dist_ts_web/appstate/target-profiles.js +118 -0
  40. package/dist_ts_web/appstate/ui.d.ts +11 -0
  41. package/dist_ts_web/appstate/ui.js +55 -0
  42. package/dist_ts_web/appstate/users.d.ts +27 -0
  43. package/dist_ts_web/appstate/users.js +85 -0
  44. package/dist_ts_web/appstate/vpn.d.ts +44 -0
  45. package/dist_ts_web/appstate/vpn.js +148 -0
  46. package/dist_ts_web/appstate.d.ts +20 -568
  47. package/dist_ts_web/appstate.js +24 -2418
  48. package/package.json +3 -3
  49. package/ts/00_commitinfo_data.ts +1 -1
  50. package/ts/classes.dcrouter.ts +10 -0
  51. package/ts_web/00_commitinfo_data.ts +1 -1
  52. package/ts_web/appstate/acme.ts +93 -0
  53. package/ts_web/appstate/certificates.ts +159 -0
  54. package/ts_web/appstate/config.ts +49 -0
  55. package/ts_web/appstate/domains.ts +429 -0
  56. package/ts_web/appstate/email-domains.ts +155 -0
  57. package/ts_web/appstate/email-ops.ts +57 -0
  58. package/ts_web/appstate/login.ts +128 -0
  59. package/ts_web/appstate/logs.ts +50 -0
  60. package/ts_web/appstate/network.ts +161 -0
  61. package/ts_web/appstate/profiles-targets.ts +240 -0
  62. package/ts_web/appstate/remoteingress.ts +300 -0
  63. package/ts_web/appstate/routes.ts +447 -0
  64. package/ts_web/appstate/runtime.ts +308 -0
  65. package/ts_web/appstate/security.ts +229 -0
  66. package/ts_web/appstate/shared.ts +15 -0
  67. package/ts_web/appstate/stats.ts +79 -0
  68. package/ts_web/appstate/target-profiles.ts +164 -0
  69. package/ts_web/appstate/ui.ts +75 -0
  70. package/ts_web/appstate/users.ts +133 -0
  71. package/ts_web/appstate/vpn.ts +234 -0
  72. package/ts_web/appstate.ts +24 -3403
@@ -0,0 +1,128 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+
5
+ export interface ILoginState {
6
+ identity: interfaces.data.IIdentity | null;
7
+ isLoggedIn: boolean;
8
+ }
9
+
10
+ export type IAdminBootstrapStatus = interfaces.requests.IReq_GetAdminBootstrapStatus['response'];
11
+
12
+ export const loginStatePart = await appState.getStatePart<ILoginState>(
13
+ 'login',
14
+ {
15
+ identity: null,
16
+ isLoggedIn: false,
17
+ },
18
+ 'persistent' // Login state persists across browser sessions
19
+ );
20
+
21
+ // Actions for state management
22
+ interface IActionContext {
23
+ identity: interfaces.data.IIdentity | null;
24
+ }
25
+
26
+ export const getActionContext = (): IActionContext => {
27
+ const identity = loginStatePart.getState()!.identity;
28
+ // Treat expired JWTs as no identity — prevents stale persisted sessions from firing requests
29
+ if (identity && identity.expiresAt && identity.expiresAt < Date.now()) {
30
+ return { identity: null };
31
+ }
32
+ return { identity };
33
+ };
34
+
35
+ // Login Action
36
+ export const loginAction = loginStatePart.createAction<{
37
+ username: string;
38
+ password: string;
39
+ authSource?: interfaces.requests.TAdminLoginAuthSource;
40
+ }>(async (statePartArg, dataArg): Promise<ILoginState> => {
41
+ const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
42
+ interfaces.requests.IReq_AdminLoginWithUsernameAndPassword
43
+ >('/typedrequest', 'adminLoginWithUsernameAndPassword');
44
+
45
+ try {
46
+ const response = await typedRequest.fire({
47
+ username: dataArg.username,
48
+ password: dataArg.password,
49
+ authSource: dataArg.authSource,
50
+ });
51
+
52
+ if (response.identity) {
53
+ return {
54
+ identity: response.identity,
55
+ isLoggedIn: true,
56
+ };
57
+ }
58
+ return statePartArg.getState()!;
59
+ } catch (error: unknown) {
60
+ console.error('Login failed:', error);
61
+ return statePartArg.getState()!;
62
+ }
63
+ });
64
+
65
+ export async function getAdminBootstrapStatus(): Promise<IAdminBootstrapStatus> {
66
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
67
+ interfaces.requests.IReq_GetAdminBootstrapStatus
68
+ >('/typedrequest', 'getAdminBootstrapStatus');
69
+
70
+ return request.fire({});
71
+ }
72
+
73
+ export async function createInitialAdminUser(optionsArg: {
74
+ email: string;
75
+ name?: string;
76
+ password: string;
77
+ enableIdpGlobalAuth?: boolean;
78
+ }) {
79
+ const context = getActionContext();
80
+ if (!context.identity) {
81
+ throw new Error('No identity available for admin bootstrap');
82
+ }
83
+
84
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
85
+ interfaces.requests.IReq_CreateInitialAdminUser
86
+ >('/typedrequest', 'createInitialAdminUser');
87
+
88
+ const response = await request.fire({
89
+ identity: context.identity,
90
+ email: optionsArg.email,
91
+ name: optionsArg.name,
92
+ password: optionsArg.password,
93
+ enableIdpGlobalAuth: optionsArg.enableIdpGlobalAuth,
94
+ });
95
+
96
+ if (response.identity) {
97
+ loginStatePart.setState({
98
+ identity: response.identity,
99
+ isLoggedIn: true,
100
+ });
101
+ }
102
+
103
+ return response;
104
+ }
105
+
106
+ // Logout Action — always clears state, even if identity is expired/missing
107
+ export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
108
+ const context = getActionContext();
109
+
110
+ // Try to notify server, but don't block logout if identity is missing/expired
111
+ if (context.identity) {
112
+ const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
113
+ interfaces.requests.IReq_AdminLogout
114
+ >('/typedrequest', 'adminLogout');
115
+ try {
116
+ await typedRequest.fire({ identity: context.identity });
117
+ } catch (error) {
118
+ console.error('Logout error:', error);
119
+ }
120
+ }
121
+
122
+ // Always clear login state
123
+ return {
124
+ identity: null,
125
+ isLoggedIn: false,
126
+ };
127
+ });
128
+
@@ -0,0 +1,50 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+ import { getActionContext } from './login.js';
5
+
6
+ export interface ILogState {
7
+ recentLogs: interfaces.data.ILogEntry[];
8
+ isStreaming: boolean;
9
+ filters: {
10
+ level?: string[];
11
+ category?: string[];
12
+ };
13
+ }
14
+
15
+ export const logStatePart = await appState.getStatePart<ILogState>(
16
+ 'logs',
17
+ {
18
+ recentLogs: [],
19
+ isStreaming: false,
20
+ filters: {},
21
+ },
22
+ 'soft'
23
+ );
24
+
25
+ // Fetch Recent Logs Action
26
+ export const fetchRecentLogsAction = logStatePart.createAction<{
27
+ limit?: number;
28
+ level?: 'debug' | 'info' | 'warn' | 'error';
29
+ category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
30
+ }>(async (statePartArg, dataArg): Promise<ILogState> => {
31
+ const context = getActionContext();
32
+ if (!context.identity) return statePartArg.getState()!;
33
+
34
+ const logsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
35
+ interfaces.requests.IReq_GetRecentLogs
36
+ >('/typedrequest', 'getRecentLogs');
37
+
38
+ const response = await logsRequest.fire({
39
+ identity: context.identity,
40
+ limit: dataArg.limit || 100,
41
+ level: dataArg.level,
42
+ category: dataArg.category,
43
+ });
44
+
45
+ return {
46
+ ...statePartArg.getState()!,
47
+ recentLogs: response.logs,
48
+ };
49
+ });
50
+
@@ -0,0 +1,161 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+ import { getActionContext } from './login.js';
5
+ import { runBackgroundRefresh } from './shared.js';
6
+
7
+ export interface INetworkState {
8
+ connections: interfaces.data.IConnectionInfo[];
9
+ connectionsByIP: { [ip: string]: number };
10
+ throughputRate: { bytesInPerSecond: number; bytesOutPerSecond: number };
11
+ totalBytes: { in: number; out: number };
12
+ topIPs: Array<{ ip: string; count: number }>;
13
+ topIPsByBandwidth: Array<{ ip: string; count: number; bwIn: number; bwOut: number }>;
14
+ topASNs: interfaces.data.IAsnActivity[];
15
+ throughputByIP: Array<{ ip: string; in: number; out: number }>;
16
+ ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
17
+ domainActivity: interfaces.data.IDomainActivity[];
18
+ throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
19
+ requestsPerSecond: number;
20
+ requestsTotal: number;
21
+ backends: interfaces.data.IBackendInfo[];
22
+ frontendProtocols: interfaces.data.IProtocolDistribution | null;
23
+ backendProtocols: interfaces.data.IProtocolDistribution | null;
24
+ lastUpdated: number;
25
+ isLoading: boolean;
26
+ error: string | null;
27
+ }
28
+
29
+ export const networkStatePart = await appState.getStatePart<INetworkState>(
30
+ 'network',
31
+ {
32
+ connections: [],
33
+ connectionsByIP: {},
34
+ throughputRate: { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
35
+ totalBytes: { in: 0, out: 0 },
36
+ topIPs: [],
37
+ topIPsByBandwidth: [],
38
+ topASNs: [],
39
+ throughputByIP: [],
40
+ ipIntelligence: [],
41
+ domainActivity: [],
42
+ throughputHistory: [],
43
+ requestsPerSecond: 0,
44
+ requestsTotal: 0,
45
+ backends: [],
46
+ frontendProtocols: null,
47
+ backendProtocols: null,
48
+ lastUpdated: 0,
49
+ isLoading: false,
50
+ error: null,
51
+ },
52
+ 'soft'
53
+ );
54
+
55
+ export function refreshNetworkIpIntelligence(identity: interfaces.data.IIdentity, ipAddresses: string[]): void {
56
+ const ips = [...new Set(ipAddresses.filter(Boolean))].slice(0, 100);
57
+ if (ips.length === 0) return;
58
+
59
+ runBackgroundRefresh('networkIpIntelligence', 'IP intelligence refresh failed:', async () => {
60
+ const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
61
+ interfaces.requests.IReq_ListIpIntelligence
62
+ >('/typedrequest', 'listIpIntelligence');
63
+ const intelligenceResponse = await intelligenceRequest.fire({
64
+ identity,
65
+ ipAddresses: ips,
66
+ limit: Math.max(100, ips.length),
67
+ });
68
+ networkStatePart.setState({
69
+ ...networkStatePart.getState()!,
70
+ ipIntelligence: intelligenceResponse.records || [],
71
+ });
72
+ });
73
+ }
74
+
75
+
76
+ // Fetch Network Stats Action
77
+ export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg): Promise<INetworkState> => {
78
+ const context = getActionContext();
79
+ const currentState = statePartArg.getState()!;
80
+ if (!context.identity) return currentState;
81
+
82
+ try {
83
+ // Get network stats for throughput and IP data
84
+ const networkStatsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
85
+ interfaces.requests.IReq_GetNetworkStats
86
+ >('/typedrequest', 'getNetworkStats');
87
+
88
+ const networkStatsResponse = await networkStatsRequest.fire({
89
+ identity: context.identity,
90
+ });
91
+
92
+ // Use the connections data for the connection list
93
+ // and network stats for throughput and IP analytics
94
+ const connectionsByIP: { [ip: string]: number } = {};
95
+ const throughputByIP = new Map<string, { in: number; out: number }>();
96
+ for (const item of networkStatsResponse.throughputByIP || []) {
97
+ throughputByIP.set(item.ip, { in: item.in, out: item.out });
98
+ }
99
+
100
+ // Build connectionsByIP from network stats if available
101
+ if (networkStatsResponse.connectionsByIP && Array.isArray(networkStatsResponse.connectionsByIP)) {
102
+ networkStatsResponse.connectionsByIP.forEach((item: { ip: string; count: number }) => {
103
+ connectionsByIP[item.ip] = item.count;
104
+ });
105
+ }
106
+
107
+ const connections: interfaces.data.IConnectionInfo[] = Object.entries(connectionsByIP).map(([ip, count]) => {
108
+ const tp = throughputByIP.get(ip);
109
+ return {
110
+ id: `ip-${ip}`,
111
+ remoteAddress: ip,
112
+ localAddress: 'server',
113
+ startTime: 0,
114
+ protocol: 'https',
115
+ state: 'connected',
116
+ bytesReceived: tp?.in || 0,
117
+ bytesSent: tp?.out || 0,
118
+ connectionCount: count,
119
+ };
120
+ });
121
+
122
+ refreshNetworkIpIntelligence(context.identity, [
123
+ ...Object.keys(connectionsByIP),
124
+ ...(networkStatsResponse.topIPs || []).map((item) => item.ip),
125
+ ...(networkStatsResponse.topIPsByBandwidth || []).map((item) => item.ip),
126
+ ]);
127
+
128
+ return {
129
+ connections,
130
+ connectionsByIP,
131
+ throughputRate: networkStatsResponse.throughputRate || { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
132
+ totalBytes: networkStatsResponse.totalDataTransferred
133
+ ? { in: networkStatsResponse.totalDataTransferred.bytesIn, out: networkStatsResponse.totalDataTransferred.bytesOut }
134
+ : { in: 0, out: 0 },
135
+ topIPs: networkStatsResponse.topIPs || [],
136
+ topIPsByBandwidth: networkStatsResponse.topIPsByBandwidth || [],
137
+ topASNs: networkStatsResponse.topASNs || [],
138
+ throughputByIP: networkStatsResponse.throughputByIP || [],
139
+ ipIntelligence: currentState.ipIntelligence,
140
+ domainActivity: networkStatsResponse.domainActivity || [],
141
+ throughputHistory: networkStatsResponse.throughputHistory || [],
142
+ requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
143
+ requestsTotal: networkStatsResponse.requestsTotal || 0,
144
+ backends: networkStatsResponse.backends || [],
145
+ frontendProtocols: networkStatsResponse.frontendProtocols || null,
146
+ backendProtocols: networkStatsResponse.backendProtocols || null,
147
+ lastUpdated: Date.now(),
148
+ isLoading: false,
149
+ error: null,
150
+ };
151
+ } catch (error) {
152
+ console.error('Failed to fetch network stats:', error);
153
+ return {
154
+ ...currentState,
155
+ isLoading: false,
156
+ error: error instanceof Error ? error.message : 'Failed to fetch network stats',
157
+ };
158
+ }
159
+ });
160
+
161
+ // ============================================================================
@@ -0,0 +1,240 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+ import { getActionContext } from './login.js';
5
+
6
+ // ============================================================================
7
+ // Source Profiles & Network Targets State
8
+ // ============================================================================
9
+
10
+ export interface IProfilesTargetsState {
11
+ profiles: interfaces.data.ISourceProfile[];
12
+ targets: interfaces.data.INetworkTarget[];
13
+ isLoading: boolean;
14
+ error: string | null;
15
+ lastUpdated: number;
16
+ }
17
+
18
+ export const profilesTargetsStatePart = await appState.getStatePart<IProfilesTargetsState>(
19
+ 'profilesTargets',
20
+ {
21
+ profiles: [],
22
+ targets: [],
23
+ isLoading: false,
24
+ error: null,
25
+ lastUpdated: 0,
26
+ },
27
+ 'soft'
28
+ );
29
+
30
+ // ============================================================================
31
+ // Source Profiles & Network Targets Actions
32
+ // ============================================================================
33
+
34
+ export const fetchProfilesAndTargetsAction = profilesTargetsStatePart.createAction(
35
+ async (statePartArg): Promise<IProfilesTargetsState> => {
36
+ const context = getActionContext();
37
+ const currentState = statePartArg.getState()!;
38
+ if (!context.identity) return currentState;
39
+
40
+ try {
41
+ const profilesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
42
+ interfaces.requests.IReq_GetSourceProfiles
43
+ >('/typedrequest', 'getSourceProfiles');
44
+
45
+ const targetsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
46
+ interfaces.requests.IReq_GetNetworkTargets
47
+ >('/typedrequest', 'getNetworkTargets');
48
+
49
+ const [profilesResponse, targetsResponse] = await Promise.all([
50
+ profilesRequest.fire({ identity: context.identity }),
51
+ targetsRequest.fire({ identity: context.identity }),
52
+ ]);
53
+
54
+ return {
55
+ profiles: profilesResponse.profiles,
56
+ targets: targetsResponse.targets,
57
+ isLoading: false,
58
+ error: null,
59
+ lastUpdated: Date.now(),
60
+ };
61
+ } catch (error) {
62
+ return {
63
+ ...currentState,
64
+ isLoading: false,
65
+ error: error instanceof Error ? error.message : 'Failed to fetch profiles/targets',
66
+ };
67
+ }
68
+ }
69
+ );
70
+
71
+ export const createProfileAction = profilesTargetsStatePart.createAction<{
72
+ name: string;
73
+ description?: string;
74
+ security: any;
75
+ extendsProfiles?: string[];
76
+ }>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
77
+ const context = getActionContext();
78
+ try {
79
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
80
+ interfaces.requests.IReq_CreateSourceProfile
81
+ >('/typedrequest', 'createSourceProfile');
82
+ await request.fire({
83
+ identity: context.identity!,
84
+ name: dataArg.name,
85
+ description: dataArg.description,
86
+ security: dataArg.security,
87
+ extendsProfiles: dataArg.extendsProfiles,
88
+ });
89
+ return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
90
+ } catch (error: unknown) {
91
+ return {
92
+ ...statePartArg.getState()!,
93
+ error: error instanceof Error ? error.message : 'Failed to create profile',
94
+ };
95
+ }
96
+ });
97
+
98
+ export const updateProfileAction = profilesTargetsStatePart.createAction<{
99
+ id: string;
100
+ name?: string;
101
+ description?: string;
102
+ security?: any;
103
+ extendsProfiles?: string[];
104
+ }>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
105
+ const context = getActionContext();
106
+ try {
107
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
108
+ interfaces.requests.IReq_UpdateSourceProfile
109
+ >('/typedrequest', 'updateSourceProfile');
110
+ await request.fire({
111
+ identity: context.identity!,
112
+ id: dataArg.id,
113
+ name: dataArg.name,
114
+ description: dataArg.description,
115
+ security: dataArg.security,
116
+ extendsProfiles: dataArg.extendsProfiles,
117
+ });
118
+ return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
119
+ } catch (error: unknown) {
120
+ return {
121
+ ...statePartArg.getState()!,
122
+ error: error instanceof Error ? error.message : 'Failed to update profile',
123
+ };
124
+ }
125
+ });
126
+
127
+ export const deleteProfileAction = profilesTargetsStatePart.createAction<{
128
+ id: string;
129
+ force?: boolean;
130
+ }>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
131
+ const context = getActionContext();
132
+ try {
133
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
134
+ interfaces.requests.IReq_DeleteSourceProfile
135
+ >('/typedrequest', 'deleteSourceProfile');
136
+ const response = await request.fire({
137
+ identity: context.identity!,
138
+ id: dataArg.id,
139
+ force: dataArg.force,
140
+ });
141
+ if (!response.success) {
142
+ return {
143
+ ...statePartArg.getState()!,
144
+ error: response.message || 'Failed to delete profile',
145
+ };
146
+ }
147
+ return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
148
+ } catch (error: unknown) {
149
+ return {
150
+ ...statePartArg.getState()!,
151
+ error: error instanceof Error ? error.message : 'Failed to delete profile',
152
+ };
153
+ }
154
+ });
155
+
156
+ export const createTargetAction = profilesTargetsStatePart.createAction<{
157
+ name: string;
158
+ description?: string;
159
+ host: string | string[];
160
+ port: number;
161
+ }>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
162
+ const context = getActionContext();
163
+ try {
164
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
165
+ interfaces.requests.IReq_CreateNetworkTarget
166
+ >('/typedrequest', 'createNetworkTarget');
167
+ await request.fire({
168
+ identity: context.identity!,
169
+ name: dataArg.name,
170
+ description: dataArg.description,
171
+ host: dataArg.host,
172
+ port: dataArg.port,
173
+ });
174
+ return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
175
+ } catch (error: unknown) {
176
+ return {
177
+ ...statePartArg.getState()!,
178
+ error: error instanceof Error ? error.message : 'Failed to create target',
179
+ };
180
+ }
181
+ });
182
+
183
+ export const updateTargetAction = profilesTargetsStatePart.createAction<{
184
+ id: string;
185
+ name?: string;
186
+ description?: string;
187
+ host?: string | string[];
188
+ port?: number;
189
+ }>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
190
+ const context = getActionContext();
191
+ try {
192
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
193
+ interfaces.requests.IReq_UpdateNetworkTarget
194
+ >('/typedrequest', 'updateNetworkTarget');
195
+ await request.fire({
196
+ identity: context.identity!,
197
+ id: dataArg.id,
198
+ name: dataArg.name,
199
+ description: dataArg.description,
200
+ host: dataArg.host,
201
+ port: dataArg.port,
202
+ });
203
+ return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
204
+ } catch (error: unknown) {
205
+ return {
206
+ ...statePartArg.getState()!,
207
+ error: error instanceof Error ? error.message : 'Failed to update target',
208
+ };
209
+ }
210
+ });
211
+
212
+ export const deleteTargetAction = profilesTargetsStatePart.createAction<{
213
+ id: string;
214
+ force?: boolean;
215
+ }>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
216
+ const context = getActionContext();
217
+ try {
218
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
219
+ interfaces.requests.IReq_DeleteNetworkTarget
220
+ >('/typedrequest', 'deleteNetworkTarget');
221
+ const response = await request.fire({
222
+ identity: context.identity!,
223
+ id: dataArg.id,
224
+ force: dataArg.force,
225
+ });
226
+ if (!response.success) {
227
+ return {
228
+ ...statePartArg.getState()!,
229
+ error: response.message || 'Failed to delete target',
230
+ };
231
+ }
232
+ return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
233
+ } catch (error: unknown) {
234
+ return {
235
+ ...statePartArg.getState()!,
236
+ error: error instanceof Error ? error.message : 'Failed to delete target',
237
+ };
238
+ }
239
+ });
240
+