@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.
- package/deno.json +1 -1
- package/dist_serve/bundle.js +768 -768
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +9 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate/acme.d.ts +17 -0
- package/dist_ts_web/appstate/acme.js +64 -0
- package/dist_ts_web/appstate/certificates.d.ts +37 -0
- package/dist_ts_web/appstate/certificates.js +107 -0
- package/dist_ts_web/appstate/config.d.ts +9 -0
- package/dist_ts_web/appstate/config.js +35 -0
- package/dist_ts_web/appstate/domains.d.ts +80 -0
- package/dist_ts_web/appstate/domains.js +324 -0
- package/dist_ts_web/appstate/email-domains.d.ts +25 -0
- package/dist_ts_web/appstate/email-domains.js +104 -0
- package/dist_ts_web/appstate/email-ops.d.ts +10 -0
- package/dist_ts_web/appstate/email-ops.js +40 -0
- package/dist_ts_web/appstate/login.d.ts +30 -0
- package/dist_ts_web/appstate/login.js +83 -0
- package/dist_ts_web/appstate/logs.d.ts +16 -0
- package/dist_ts_web/appstate/logs.js +27 -0
- package/dist_ts_web/appstate/network.d.ts +50 -0
- package/dist_ts_web/appstate/network.js +122 -0
- package/dist_ts_web/appstate/profiles-targets.d.ts +45 -0
- package/dist_ts_web/appstate/profiles-targets.js +173 -0
- package/dist_ts_web/appstate/remoteingress.d.ts +47 -0
- package/dist_ts_web/appstate/remoteingress.js +204 -0
- package/dist_ts_web/appstate/routes.d.ts +76 -0
- package/dist_ts_web/appstate/routes.js +316 -0
- package/dist_ts_web/appstate/runtime.d.ts +1 -0
- package/dist_ts_web/appstate/runtime.js +276 -0
- package/dist_ts_web/appstate/security.d.ts +29 -0
- package/dist_ts_web/appstate/security.js +167 -0
- package/dist_ts_web/appstate/shared.d.ts +3 -0
- package/dist_ts_web/appstate/shared.js +13 -0
- package/dist_ts_web/appstate/stats.d.ts +15 -0
- package/dist_ts_web/appstate/stats.js +59 -0
- package/dist_ts_web/appstate/target-profiles.d.ts +37 -0
- package/dist_ts_web/appstate/target-profiles.js +118 -0
- package/dist_ts_web/appstate/ui.d.ts +11 -0
- package/dist_ts_web/appstate/ui.js +55 -0
- package/dist_ts_web/appstate/users.d.ts +27 -0
- package/dist_ts_web/appstate/users.js +85 -0
- package/dist_ts_web/appstate/vpn.d.ts +44 -0
- package/dist_ts_web/appstate/vpn.js +148 -0
- package/dist_ts_web/appstate.d.ts +20 -568
- package/dist_ts_web/appstate.js +24 -2418
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +10 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate/acme.ts +93 -0
- package/ts_web/appstate/certificates.ts +159 -0
- package/ts_web/appstate/config.ts +49 -0
- package/ts_web/appstate/domains.ts +429 -0
- package/ts_web/appstate/email-domains.ts +155 -0
- package/ts_web/appstate/email-ops.ts +57 -0
- package/ts_web/appstate/login.ts +128 -0
- package/ts_web/appstate/logs.ts +50 -0
- package/ts_web/appstate/network.ts +161 -0
- package/ts_web/appstate/profiles-targets.ts +240 -0
- package/ts_web/appstate/remoteingress.ts +300 -0
- package/ts_web/appstate/routes.ts +447 -0
- package/ts_web/appstate/runtime.ts +308 -0
- package/ts_web/appstate/security.ts +229 -0
- package/ts_web/appstate/shared.ts +15 -0
- package/ts_web/appstate/stats.ts +79 -0
- package/ts_web/appstate/target-profiles.ts +164 -0
- package/ts_web/appstate/ui.ts +75 -0
- package/ts_web/appstate/users.ts +133 -0
- package/ts_web/appstate/vpn.ts +234 -0
- package/ts_web/appstate.ts +24 -3403
package/ts_web/appstate.ts
CHANGED
|
@@ -1,3403 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
error: string | null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface IConfigState {
|
|
29
|
-
config: interfaces.requests.IConfigData | null;
|
|
30
|
-
isLoading: boolean;
|
|
31
|
-
error: string | null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface IUiState {
|
|
35
|
-
activeView: string;
|
|
36
|
-
activeSubview: string | null;
|
|
37
|
-
sidebarCollapsed: boolean;
|
|
38
|
-
autoRefresh: boolean;
|
|
39
|
-
refreshInterval: number; // milliseconds
|
|
40
|
-
theme: 'light' | 'dark';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface ILogState {
|
|
44
|
-
recentLogs: interfaces.data.ILogEntry[];
|
|
45
|
-
isStreaming: boolean;
|
|
46
|
-
filters: {
|
|
47
|
-
level?: string[];
|
|
48
|
-
category?: string[];
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface INetworkState {
|
|
53
|
-
connections: interfaces.data.IConnectionInfo[];
|
|
54
|
-
connectionsByIP: { [ip: string]: number };
|
|
55
|
-
throughputRate: { bytesInPerSecond: number; bytesOutPerSecond: number };
|
|
56
|
-
totalBytes: { in: number; out: number };
|
|
57
|
-
topIPs: Array<{ ip: string; count: number }>;
|
|
58
|
-
topIPsByBandwidth: Array<{ ip: string; count: number; bwIn: number; bwOut: number }>;
|
|
59
|
-
topASNs: interfaces.data.IAsnActivity[];
|
|
60
|
-
throughputByIP: Array<{ ip: string; in: number; out: number }>;
|
|
61
|
-
ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
|
|
62
|
-
domainActivity: interfaces.data.IDomainActivity[];
|
|
63
|
-
throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
|
|
64
|
-
requestsPerSecond: number;
|
|
65
|
-
requestsTotal: number;
|
|
66
|
-
backends: interfaces.data.IBackendInfo[];
|
|
67
|
-
frontendProtocols: interfaces.data.IProtocolDistribution | null;
|
|
68
|
-
backendProtocols: interfaces.data.IProtocolDistribution | null;
|
|
69
|
-
lastUpdated: number;
|
|
70
|
-
isLoading: boolean;
|
|
71
|
-
error: string | null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface ISecurityPolicyState {
|
|
75
|
-
rules: interfaces.data.ISecurityBlockRule[];
|
|
76
|
-
ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
|
|
77
|
-
compiledPolicy: interfaces.data.ISecurityCompiledPolicy | null;
|
|
78
|
-
auditEvents: interfaces.data.ISecurityPolicyAuditEvent[];
|
|
79
|
-
isLoading: boolean;
|
|
80
|
-
error: string | null;
|
|
81
|
-
lastUpdated: number;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface ICertificateState {
|
|
85
|
-
certificates: interfaces.requests.ICertificateInfo[];
|
|
86
|
-
summary: { total: number; valid: number; expiring: number; expired: number; failed: number; unknown: number };
|
|
87
|
-
isLoading: boolean;
|
|
88
|
-
error: string | null;
|
|
89
|
-
lastUpdated: number;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface IEmailOpsState {
|
|
93
|
-
emails: interfaces.requests.IEmail[];
|
|
94
|
-
isLoading: boolean;
|
|
95
|
-
error: string | null;
|
|
96
|
-
lastUpdated: number;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Create state parts with appropriate persistence
|
|
100
|
-
export const loginStatePart = await appState.getStatePart<ILoginState>(
|
|
101
|
-
'login',
|
|
102
|
-
{
|
|
103
|
-
identity: null,
|
|
104
|
-
isLoggedIn: false,
|
|
105
|
-
},
|
|
106
|
-
'persistent' // Login state persists across browser sessions
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
export const statsStatePart = await appState.getStatePart<IStatsState>(
|
|
110
|
-
'stats',
|
|
111
|
-
{
|
|
112
|
-
serverStats: null,
|
|
113
|
-
emailStats: null,
|
|
114
|
-
dnsStats: null,
|
|
115
|
-
securityMetrics: null,
|
|
116
|
-
radiusStats: null,
|
|
117
|
-
vpnStats: null,
|
|
118
|
-
lastUpdated: 0,
|
|
119
|
-
isLoading: false,
|
|
120
|
-
error: null,
|
|
121
|
-
},
|
|
122
|
-
'soft' // Stats are cached but not persisted
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
export const configStatePart = await appState.getStatePart<IConfigState>(
|
|
126
|
-
'config',
|
|
127
|
-
{
|
|
128
|
-
config: null,
|
|
129
|
-
isLoading: false,
|
|
130
|
-
error: null,
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
// Determine initial view from URL path
|
|
135
|
-
const getInitialView = (): string => {
|
|
136
|
-
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
|
|
137
|
-
const validViews = ['overview', 'network', 'email', 'logs', 'access', 'security', 'domains'];
|
|
138
|
-
const segments = path.split('/').filter(Boolean);
|
|
139
|
-
const view = segments[0];
|
|
140
|
-
return validViews.includes(view) ? view : 'overview';
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
// Determine initial subview (second URL segment) from the path
|
|
144
|
-
const getInitialSubview = (): string | null => {
|
|
145
|
-
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
|
|
146
|
-
const segments = path.split('/').filter(Boolean);
|
|
147
|
-
return segments[1] ?? null;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
export const uiStatePart = await appState.getStatePart<IUiState>(
|
|
151
|
-
'ui',
|
|
152
|
-
{
|
|
153
|
-
activeView: getInitialView(),
|
|
154
|
-
activeSubview: getInitialSubview(),
|
|
155
|
-
sidebarCollapsed: false,
|
|
156
|
-
autoRefresh: true,
|
|
157
|
-
refreshInterval: 1000, // 1 second
|
|
158
|
-
theme: 'light',
|
|
159
|
-
},
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
export const logStatePart = await appState.getStatePart<ILogState>(
|
|
163
|
-
'logs',
|
|
164
|
-
{
|
|
165
|
-
recentLogs: [],
|
|
166
|
-
isStreaming: false,
|
|
167
|
-
filters: {},
|
|
168
|
-
},
|
|
169
|
-
'soft'
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
export const networkStatePart = await appState.getStatePart<INetworkState>(
|
|
173
|
-
'network',
|
|
174
|
-
{
|
|
175
|
-
connections: [],
|
|
176
|
-
connectionsByIP: {},
|
|
177
|
-
throughputRate: { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
|
|
178
|
-
totalBytes: { in: 0, out: 0 },
|
|
179
|
-
topIPs: [],
|
|
180
|
-
topIPsByBandwidth: [],
|
|
181
|
-
topASNs: [],
|
|
182
|
-
throughputByIP: [],
|
|
183
|
-
ipIntelligence: [],
|
|
184
|
-
domainActivity: [],
|
|
185
|
-
throughputHistory: [],
|
|
186
|
-
requestsPerSecond: 0,
|
|
187
|
-
requestsTotal: 0,
|
|
188
|
-
backends: [],
|
|
189
|
-
frontendProtocols: null,
|
|
190
|
-
backendProtocols: null,
|
|
191
|
-
lastUpdated: 0,
|
|
192
|
-
isLoading: false,
|
|
193
|
-
error: null,
|
|
194
|
-
},
|
|
195
|
-
'soft'
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
export const securityPolicyStatePart = await appState.getStatePart<ISecurityPolicyState>(
|
|
199
|
-
'securityPolicy',
|
|
200
|
-
{
|
|
201
|
-
rules: [],
|
|
202
|
-
ipIntelligence: [],
|
|
203
|
-
compiledPolicy: null,
|
|
204
|
-
auditEvents: [],
|
|
205
|
-
isLoading: false,
|
|
206
|
-
error: null,
|
|
207
|
-
lastUpdated: 0,
|
|
208
|
-
},
|
|
209
|
-
'soft',
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
export const emailOpsStatePart = await appState.getStatePart<IEmailOpsState>(
|
|
213
|
-
'emailOps',
|
|
214
|
-
{
|
|
215
|
-
emails: [],
|
|
216
|
-
isLoading: false,
|
|
217
|
-
error: null,
|
|
218
|
-
lastUpdated: 0,
|
|
219
|
-
},
|
|
220
|
-
'soft'
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
export const certificateStatePart = await appState.getStatePart<ICertificateState>(
|
|
224
|
-
'certificates',
|
|
225
|
-
{
|
|
226
|
-
certificates: [],
|
|
227
|
-
summary: { total: 0, valid: 0, expiring: 0, expired: 0, failed: 0, unknown: 0 },
|
|
228
|
-
isLoading: false,
|
|
229
|
-
error: null,
|
|
230
|
-
lastUpdated: 0,
|
|
231
|
-
},
|
|
232
|
-
'soft'
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
// ============================================================================
|
|
236
|
-
// ACME Config State (DB-backed singleton, managed via Domains > Certificates)
|
|
237
|
-
// ============================================================================
|
|
238
|
-
|
|
239
|
-
export interface IAcmeConfigState {
|
|
240
|
-
config: interfaces.data.IAcmeConfig | null;
|
|
241
|
-
isLoading: boolean;
|
|
242
|
-
error: string | null;
|
|
243
|
-
lastUpdated: number;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export const acmeConfigStatePart = await appState.getStatePart<IAcmeConfigState>(
|
|
247
|
-
'acmeConfig',
|
|
248
|
-
{
|
|
249
|
-
config: null,
|
|
250
|
-
isLoading: false,
|
|
251
|
-
error: null,
|
|
252
|
-
lastUpdated: 0,
|
|
253
|
-
},
|
|
254
|
-
'soft',
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
// ============================================================================
|
|
258
|
-
// Remote Ingress State
|
|
259
|
-
// ============================================================================
|
|
260
|
-
|
|
261
|
-
export interface IRemoteIngressState {
|
|
262
|
-
edges: interfaces.data.IRemoteIngress[];
|
|
263
|
-
statuses: interfaces.data.IRemoteIngressStatus[];
|
|
264
|
-
hubSettings: interfaces.data.IRemoteIngressHubSettings | null;
|
|
265
|
-
selectedEdgeId: string | null;
|
|
266
|
-
newEdgeId: string | null;
|
|
267
|
-
isLoading: boolean;
|
|
268
|
-
error: string | null;
|
|
269
|
-
lastUpdated: number;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export const remoteIngressStatePart = await appState.getStatePart<IRemoteIngressState>(
|
|
273
|
-
'remoteIngress',
|
|
274
|
-
{
|
|
275
|
-
edges: [],
|
|
276
|
-
statuses: [],
|
|
277
|
-
hubSettings: null,
|
|
278
|
-
selectedEdgeId: null,
|
|
279
|
-
newEdgeId: null,
|
|
280
|
-
isLoading: false,
|
|
281
|
-
error: null,
|
|
282
|
-
lastUpdated: 0,
|
|
283
|
-
},
|
|
284
|
-
'soft'
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
// ============================================================================
|
|
288
|
-
// Route Management State
|
|
289
|
-
// ============================================================================
|
|
290
|
-
|
|
291
|
-
export interface IRouteManagementState {
|
|
292
|
-
mergedRoutes: interfaces.data.IMergedRoute[];
|
|
293
|
-
warnings: interfaces.data.IRouteWarning[];
|
|
294
|
-
httpRedirects: interfaces.data.IHttpRedirectInfo[];
|
|
295
|
-
apiTokens: interfaces.data.IApiTokenInfo[];
|
|
296
|
-
gatewayClients: interfaces.data.IGatewayClient[];
|
|
297
|
-
isLoading: boolean;
|
|
298
|
-
error: string | null;
|
|
299
|
-
lastUpdated: number;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export const routeManagementStatePart = await appState.getStatePart<IRouteManagementState>(
|
|
303
|
-
'routeManagement',
|
|
304
|
-
{
|
|
305
|
-
mergedRoutes: [],
|
|
306
|
-
warnings: [],
|
|
307
|
-
httpRedirects: [],
|
|
308
|
-
apiTokens: [],
|
|
309
|
-
gatewayClients: [],
|
|
310
|
-
isLoading: false,
|
|
311
|
-
error: null,
|
|
312
|
-
lastUpdated: 0,
|
|
313
|
-
},
|
|
314
|
-
'soft'
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
// ============================================================================
|
|
318
|
-
// Users State (read-only list of OpsServer user accounts)
|
|
319
|
-
// ============================================================================
|
|
320
|
-
|
|
321
|
-
export interface IUser {
|
|
322
|
-
id: string;
|
|
323
|
-
username: string;
|
|
324
|
-
email?: string;
|
|
325
|
-
name?: string;
|
|
326
|
-
role: string;
|
|
327
|
-
status?: 'active' | 'disabled';
|
|
328
|
-
authSources?: Array<'local' | 'idp.global'>;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
export interface IUsersState {
|
|
332
|
-
users: IUser[];
|
|
333
|
-
isLoading: boolean;
|
|
334
|
-
error: string | null;
|
|
335
|
-
lastUpdated: number;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export const usersStatePart = await appState.getStatePart<IUsersState>(
|
|
339
|
-
'users',
|
|
340
|
-
{
|
|
341
|
-
users: [],
|
|
342
|
-
isLoading: false,
|
|
343
|
-
error: null,
|
|
344
|
-
lastUpdated: 0,
|
|
345
|
-
},
|
|
346
|
-
'soft',
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
// Actions for state management
|
|
350
|
-
interface IActionContext {
|
|
351
|
-
identity: interfaces.data.IIdentity | null;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const getActionContext = (): IActionContext => {
|
|
355
|
-
const identity = loginStatePart.getState()!.identity;
|
|
356
|
-
// Treat expired JWTs as no identity — prevents stale persisted sessions from firing requests
|
|
357
|
-
if (identity && identity.expiresAt && identity.expiresAt < Date.now()) {
|
|
358
|
-
return { identity: null };
|
|
359
|
-
}
|
|
360
|
-
return { identity };
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// Login Action
|
|
364
|
-
export const loginAction = loginStatePart.createAction<{
|
|
365
|
-
username: string;
|
|
366
|
-
password: string;
|
|
367
|
-
authSource?: interfaces.requests.TAdminLoginAuthSource;
|
|
368
|
-
}>(async (statePartArg, dataArg): Promise<ILoginState> => {
|
|
369
|
-
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
370
|
-
interfaces.requests.IReq_AdminLoginWithUsernameAndPassword
|
|
371
|
-
>('/typedrequest', 'adminLoginWithUsernameAndPassword');
|
|
372
|
-
|
|
373
|
-
try {
|
|
374
|
-
const response = await typedRequest.fire({
|
|
375
|
-
username: dataArg.username,
|
|
376
|
-
password: dataArg.password,
|
|
377
|
-
authSource: dataArg.authSource,
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
if (response.identity) {
|
|
381
|
-
return {
|
|
382
|
-
identity: response.identity,
|
|
383
|
-
isLoggedIn: true,
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
return statePartArg.getState()!;
|
|
387
|
-
} catch (error: unknown) {
|
|
388
|
-
console.error('Login failed:', error);
|
|
389
|
-
return statePartArg.getState()!;
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
export async function getAdminBootstrapStatus(): Promise<IAdminBootstrapStatus> {
|
|
394
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
395
|
-
interfaces.requests.IReq_GetAdminBootstrapStatus
|
|
396
|
-
>('/typedrequest', 'getAdminBootstrapStatus');
|
|
397
|
-
|
|
398
|
-
return request.fire({});
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export async function createInitialAdminUser(optionsArg: {
|
|
402
|
-
email: string;
|
|
403
|
-
name?: string;
|
|
404
|
-
password: string;
|
|
405
|
-
enableIdpGlobalAuth?: boolean;
|
|
406
|
-
}) {
|
|
407
|
-
const context = getActionContext();
|
|
408
|
-
if (!context.identity) {
|
|
409
|
-
throw new Error('No identity available for admin bootstrap');
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
413
|
-
interfaces.requests.IReq_CreateInitialAdminUser
|
|
414
|
-
>('/typedrequest', 'createInitialAdminUser');
|
|
415
|
-
|
|
416
|
-
const response = await request.fire({
|
|
417
|
-
identity: context.identity,
|
|
418
|
-
email: optionsArg.email,
|
|
419
|
-
name: optionsArg.name,
|
|
420
|
-
password: optionsArg.password,
|
|
421
|
-
enableIdpGlobalAuth: optionsArg.enableIdpGlobalAuth,
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
if (response.identity) {
|
|
425
|
-
loginStatePart.setState({
|
|
426
|
-
identity: response.identity,
|
|
427
|
-
isLoggedIn: true,
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return response;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Logout Action — always clears state, even if identity is expired/missing
|
|
435
|
-
export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
|
|
436
|
-
const context = getActionContext();
|
|
437
|
-
|
|
438
|
-
// Try to notify server, but don't block logout if identity is missing/expired
|
|
439
|
-
if (context.identity) {
|
|
440
|
-
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
441
|
-
interfaces.requests.IReq_AdminLogout
|
|
442
|
-
>('/typedrequest', 'adminLogout');
|
|
443
|
-
try {
|
|
444
|
-
await typedRequest.fire({ identity: context.identity });
|
|
445
|
-
} catch (error) {
|
|
446
|
-
console.error('Logout error:', error);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Always clear login state
|
|
451
|
-
return {
|
|
452
|
-
identity: null,
|
|
453
|
-
isLoggedIn: false,
|
|
454
|
-
};
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Fetch All Stats Action - Using combined endpoint for efficiency
|
|
458
|
-
export const fetchAllStatsAction = statsStatePart.createAction(async (statePartArg): Promise<IStatsState> => {
|
|
459
|
-
const context = getActionContext();
|
|
460
|
-
const currentState = statePartArg.getState()!;
|
|
461
|
-
if (!context.identity) return currentState;
|
|
462
|
-
|
|
463
|
-
try {
|
|
464
|
-
// Use combined metrics endpoint - single request instead of 4
|
|
465
|
-
const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
466
|
-
interfaces.requests.IReq_GetCombinedMetrics
|
|
467
|
-
>('/typedrequest', 'getCombinedMetrics');
|
|
468
|
-
|
|
469
|
-
const combinedResponse = await combinedRequest.fire({
|
|
470
|
-
identity: context.identity,
|
|
471
|
-
sections: {
|
|
472
|
-
server: true,
|
|
473
|
-
email: true,
|
|
474
|
-
dns: true,
|
|
475
|
-
security: true,
|
|
476
|
-
network: false, // Network is fetched separately for the network view
|
|
477
|
-
radius: true,
|
|
478
|
-
vpn: true,
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
// Update state with all stats from combined response
|
|
483
|
-
return {
|
|
484
|
-
serverStats: combinedResponse.metrics.server || currentState.serverStats,
|
|
485
|
-
emailStats: combinedResponse.metrics.email || currentState.emailStats,
|
|
486
|
-
dnsStats: combinedResponse.metrics.dns || currentState.dnsStats,
|
|
487
|
-
securityMetrics: combinedResponse.metrics.security || currentState.securityMetrics,
|
|
488
|
-
radiusStats: combinedResponse.metrics.radius || currentState.radiusStats,
|
|
489
|
-
vpnStats: combinedResponse.metrics.vpn || currentState.vpnStats,
|
|
490
|
-
lastUpdated: Date.now(),
|
|
491
|
-
isLoading: false,
|
|
492
|
-
error: null,
|
|
493
|
-
};
|
|
494
|
-
} catch (error: unknown) {
|
|
495
|
-
return {
|
|
496
|
-
...currentState,
|
|
497
|
-
isLoading: false,
|
|
498
|
-
error: (error as Error).message || 'Failed to fetch statistics',
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// Fetch Configuration Action (read-only)
|
|
504
|
-
export const fetchConfigurationAction = configStatePart.createAction(async (statePartArg): Promise<IConfigState> => {
|
|
505
|
-
const context = getActionContext();
|
|
506
|
-
const currentState = statePartArg.getState()!;
|
|
507
|
-
if (!context.identity) return currentState;
|
|
508
|
-
|
|
509
|
-
try {
|
|
510
|
-
const configRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
511
|
-
interfaces.requests.IReq_GetConfiguration
|
|
512
|
-
>('/typedrequest', 'getConfiguration');
|
|
513
|
-
|
|
514
|
-
const response = await configRequest.fire({
|
|
515
|
-
identity: context.identity,
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
return {
|
|
519
|
-
config: response.config,
|
|
520
|
-
isLoading: false,
|
|
521
|
-
error: null,
|
|
522
|
-
};
|
|
523
|
-
} catch (error: unknown) {
|
|
524
|
-
return {
|
|
525
|
-
...currentState,
|
|
526
|
-
isLoading: false,
|
|
527
|
-
error: (error as Error).message || 'Failed to fetch configuration',
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
// Fetch Recent Logs Action
|
|
533
|
-
export const fetchRecentLogsAction = logStatePart.createAction<{
|
|
534
|
-
limit?: number;
|
|
535
|
-
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
536
|
-
category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
|
|
537
|
-
}>(async (statePartArg, dataArg): Promise<ILogState> => {
|
|
538
|
-
const context = getActionContext();
|
|
539
|
-
if (!context.identity) return statePartArg.getState()!;
|
|
540
|
-
|
|
541
|
-
const logsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
542
|
-
interfaces.requests.IReq_GetRecentLogs
|
|
543
|
-
>('/typedrequest', 'getRecentLogs');
|
|
544
|
-
|
|
545
|
-
const response = await logsRequest.fire({
|
|
546
|
-
identity: context.identity,
|
|
547
|
-
limit: dataArg.limit || 100,
|
|
548
|
-
level: dataArg.level,
|
|
549
|
-
category: dataArg.category,
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
return {
|
|
553
|
-
...statePartArg.getState()!,
|
|
554
|
-
recentLogs: response.logs,
|
|
555
|
-
};
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
// Toggle Auto Refresh Action
|
|
559
|
-
export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg): Promise<IUiState> => {
|
|
560
|
-
const currentState = statePartArg.getState()!;
|
|
561
|
-
return {
|
|
562
|
-
...currentState,
|
|
563
|
-
autoRefresh: !currentState.autoRefresh,
|
|
564
|
-
};
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// Set Active View Action
|
|
568
|
-
export const setActiveViewAction = uiStatePart.createAction<string>(async (statePartArg, viewName): Promise<IUiState> => {
|
|
569
|
-
const currentState = statePartArg.getState()!;
|
|
570
|
-
|
|
571
|
-
// If switching to network view, ensure we fetch network data
|
|
572
|
-
if (viewName === 'network' && currentState.activeView !== 'network') {
|
|
573
|
-
setTimeout(() => {
|
|
574
|
-
networkStatePart.dispatchAction(fetchNetworkStatsAction, null);
|
|
575
|
-
}, 100);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// If switching to the Domains group, ensure we fetch certificate data
|
|
579
|
-
// (Certificates is a subview of Domains).
|
|
580
|
-
if (viewName === 'domains' && currentState.activeView !== 'domains') {
|
|
581
|
-
setTimeout(() => {
|
|
582
|
-
certificateStatePart.dispatchAction(fetchCertificateOverviewAction, null);
|
|
583
|
-
}, 100);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
return {
|
|
587
|
-
...currentState,
|
|
588
|
-
activeView: viewName,
|
|
589
|
-
};
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
const backgroundRefreshesInFlight = new Set<string>();
|
|
593
|
-
|
|
594
|
-
function runBackgroundRefresh(key: string, errorMessage: string, task: () => Promise<void>): void {
|
|
595
|
-
if (backgroundRefreshesInFlight.has(key)) return;
|
|
596
|
-
backgroundRefreshesInFlight.add(key);
|
|
597
|
-
void task()
|
|
598
|
-
.catch((error) => console.error(errorMessage, error))
|
|
599
|
-
.finally(() => backgroundRefreshesInFlight.delete(key));
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
function refreshNetworkIpIntelligence(identity: interfaces.data.IIdentity, ipAddresses: string[]): void {
|
|
603
|
-
const ips = [...new Set(ipAddresses.filter(Boolean))].slice(0, 100);
|
|
604
|
-
if (ips.length === 0) return;
|
|
605
|
-
|
|
606
|
-
runBackgroundRefresh('networkIpIntelligence', 'IP intelligence refresh failed:', async () => {
|
|
607
|
-
const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
608
|
-
interfaces.requests.IReq_ListIpIntelligence
|
|
609
|
-
>('/typedrequest', 'listIpIntelligence');
|
|
610
|
-
const intelligenceResponse = await intelligenceRequest.fire({
|
|
611
|
-
identity,
|
|
612
|
-
ipAddresses: ips,
|
|
613
|
-
limit: Math.max(100, ips.length),
|
|
614
|
-
});
|
|
615
|
-
networkStatePart.setState({
|
|
616
|
-
...networkStatePart.getState()!,
|
|
617
|
-
ipIntelligence: intelligenceResponse.records || [],
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function refreshSecurityIpIntelligence(identity: interfaces.data.IIdentity): void {
|
|
623
|
-
runBackgroundRefresh('securityIpIntelligence', 'Security IP intelligence refresh failed:', async () => {
|
|
624
|
-
const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
625
|
-
interfaces.requests.IReq_ListIpIntelligence
|
|
626
|
-
>('/typedrequest', 'listIpIntelligence');
|
|
627
|
-
const intelligenceResponse = await intelligenceRequest.fire({
|
|
628
|
-
identity,
|
|
629
|
-
limit: 500,
|
|
630
|
-
});
|
|
631
|
-
securityPolicyStatePart.setState({
|
|
632
|
-
...securityPolicyStatePart.getState()!,
|
|
633
|
-
ipIntelligence: intelligenceResponse.records || [],
|
|
634
|
-
});
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// Fetch Network Stats Action
|
|
639
|
-
export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg): Promise<INetworkState> => {
|
|
640
|
-
const context = getActionContext();
|
|
641
|
-
const currentState = statePartArg.getState()!;
|
|
642
|
-
if (!context.identity) return currentState;
|
|
643
|
-
|
|
644
|
-
try {
|
|
645
|
-
// Get network stats for throughput and IP data
|
|
646
|
-
const networkStatsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
647
|
-
interfaces.requests.IReq_GetNetworkStats
|
|
648
|
-
>('/typedrequest', 'getNetworkStats');
|
|
649
|
-
|
|
650
|
-
const networkStatsResponse = await networkStatsRequest.fire({
|
|
651
|
-
identity: context.identity,
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
// Use the connections data for the connection list
|
|
655
|
-
// and network stats for throughput and IP analytics
|
|
656
|
-
const connectionsByIP: { [ip: string]: number } = {};
|
|
657
|
-
const throughputByIP = new Map<string, { in: number; out: number }>();
|
|
658
|
-
for (const item of networkStatsResponse.throughputByIP || []) {
|
|
659
|
-
throughputByIP.set(item.ip, { in: item.in, out: item.out });
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Build connectionsByIP from network stats if available
|
|
663
|
-
if (networkStatsResponse.connectionsByIP && Array.isArray(networkStatsResponse.connectionsByIP)) {
|
|
664
|
-
networkStatsResponse.connectionsByIP.forEach((item: { ip: string; count: number }) => {
|
|
665
|
-
connectionsByIP[item.ip] = item.count;
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const connections: interfaces.data.IConnectionInfo[] = Object.entries(connectionsByIP).map(([ip, count]) => {
|
|
670
|
-
const tp = throughputByIP.get(ip);
|
|
671
|
-
return {
|
|
672
|
-
id: `ip-${ip}`,
|
|
673
|
-
remoteAddress: ip,
|
|
674
|
-
localAddress: 'server',
|
|
675
|
-
startTime: 0,
|
|
676
|
-
protocol: 'https',
|
|
677
|
-
state: 'connected',
|
|
678
|
-
bytesReceived: tp?.in || 0,
|
|
679
|
-
bytesSent: tp?.out || 0,
|
|
680
|
-
connectionCount: count,
|
|
681
|
-
};
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
refreshNetworkIpIntelligence(context.identity, [
|
|
685
|
-
...Object.keys(connectionsByIP),
|
|
686
|
-
...(networkStatsResponse.topIPs || []).map((item) => item.ip),
|
|
687
|
-
...(networkStatsResponse.topIPsByBandwidth || []).map((item) => item.ip),
|
|
688
|
-
]);
|
|
689
|
-
|
|
690
|
-
return {
|
|
691
|
-
connections,
|
|
692
|
-
connectionsByIP,
|
|
693
|
-
throughputRate: networkStatsResponse.throughputRate || { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
|
|
694
|
-
totalBytes: networkStatsResponse.totalDataTransferred
|
|
695
|
-
? { in: networkStatsResponse.totalDataTransferred.bytesIn, out: networkStatsResponse.totalDataTransferred.bytesOut }
|
|
696
|
-
: { in: 0, out: 0 },
|
|
697
|
-
topIPs: networkStatsResponse.topIPs || [],
|
|
698
|
-
topIPsByBandwidth: networkStatsResponse.topIPsByBandwidth || [],
|
|
699
|
-
topASNs: networkStatsResponse.topASNs || [],
|
|
700
|
-
throughputByIP: networkStatsResponse.throughputByIP || [],
|
|
701
|
-
ipIntelligence: currentState.ipIntelligence,
|
|
702
|
-
domainActivity: networkStatsResponse.domainActivity || [],
|
|
703
|
-
throughputHistory: networkStatsResponse.throughputHistory || [],
|
|
704
|
-
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
|
|
705
|
-
requestsTotal: networkStatsResponse.requestsTotal || 0,
|
|
706
|
-
backends: networkStatsResponse.backends || [],
|
|
707
|
-
frontendProtocols: networkStatsResponse.frontendProtocols || null,
|
|
708
|
-
backendProtocols: networkStatsResponse.backendProtocols || null,
|
|
709
|
-
lastUpdated: Date.now(),
|
|
710
|
-
isLoading: false,
|
|
711
|
-
error: null,
|
|
712
|
-
};
|
|
713
|
-
} catch (error) {
|
|
714
|
-
console.error('Failed to fetch network stats:', error);
|
|
715
|
-
return {
|
|
716
|
-
...currentState,
|
|
717
|
-
isLoading: false,
|
|
718
|
-
error: error instanceof Error ? error.message : 'Failed to fetch network stats',
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
// ============================================================================
|
|
724
|
-
// Security Policy Actions
|
|
725
|
-
// ============================================================================
|
|
726
|
-
|
|
727
|
-
export const fetchSecurityPolicyAction = securityPolicyStatePart.createAction(
|
|
728
|
-
async (statePartArg): Promise<ISecurityPolicyState> => {
|
|
729
|
-
const context = getActionContext();
|
|
730
|
-
const currentState = statePartArg.getState()!;
|
|
731
|
-
if (!context.identity) return currentState;
|
|
732
|
-
|
|
733
|
-
try {
|
|
734
|
-
const rulesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
735
|
-
interfaces.requests.IReq_ListSecurityBlockRules
|
|
736
|
-
>('/typedrequest', 'listSecurityBlockRules');
|
|
737
|
-
const compiledPolicyRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
738
|
-
interfaces.requests.IReq_GetCompiledSecurityPolicy
|
|
739
|
-
>('/typedrequest', 'getCompiledSecurityPolicy');
|
|
740
|
-
const auditRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
741
|
-
interfaces.requests.IReq_ListSecurityPolicyAudit
|
|
742
|
-
>('/typedrequest', 'listSecurityPolicyAudit');
|
|
743
|
-
|
|
744
|
-
const [rulesResponse, compiledPolicyResponse, auditResponse] = await Promise.all([
|
|
745
|
-
rulesRequest.fire({ identity: context.identity }),
|
|
746
|
-
compiledPolicyRequest.fire({ identity: context.identity }),
|
|
747
|
-
auditRequest.fire({ identity: context.identity, limit: 100 }),
|
|
748
|
-
]);
|
|
749
|
-
|
|
750
|
-
refreshSecurityIpIntelligence(context.identity);
|
|
751
|
-
|
|
752
|
-
return {
|
|
753
|
-
rules: rulesResponse.rules || [],
|
|
754
|
-
ipIntelligence: currentState.ipIntelligence,
|
|
755
|
-
compiledPolicy: compiledPolicyResponse.policy,
|
|
756
|
-
auditEvents: auditResponse.events || [],
|
|
757
|
-
isLoading: false,
|
|
758
|
-
error: null,
|
|
759
|
-
lastUpdated: Date.now(),
|
|
760
|
-
};
|
|
761
|
-
} catch (error: unknown) {
|
|
762
|
-
return {
|
|
763
|
-
...currentState,
|
|
764
|
-
isLoading: false,
|
|
765
|
-
error: error instanceof Error ? error.message : 'Failed to fetch security policy',
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
},
|
|
769
|
-
);
|
|
770
|
-
|
|
771
|
-
export const createSecurityBlockRuleAction = securityPolicyStatePart.createAction<{
|
|
772
|
-
type: interfaces.data.TSecurityBlockRuleType;
|
|
773
|
-
value: string;
|
|
774
|
-
matchMode?: interfaces.data.TSecurityBlockRuleMatchMode;
|
|
775
|
-
reason?: string;
|
|
776
|
-
enabled?: boolean;
|
|
777
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<ISecurityPolicyState> => {
|
|
778
|
-
const context = getActionContext();
|
|
779
|
-
const currentState = statePartArg.getState()!;
|
|
780
|
-
if (!context.identity) return currentState;
|
|
781
|
-
|
|
782
|
-
try {
|
|
783
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
784
|
-
interfaces.requests.IReq_CreateSecurityBlockRule
|
|
785
|
-
>('/typedrequest', 'createSecurityBlockRule');
|
|
786
|
-
|
|
787
|
-
const response = await request.fire({
|
|
788
|
-
identity: context.identity,
|
|
789
|
-
type: dataArg.type,
|
|
790
|
-
value: dataArg.value,
|
|
791
|
-
matchMode: dataArg.matchMode,
|
|
792
|
-
reason: dataArg.reason,
|
|
793
|
-
enabled: dataArg.enabled,
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
if (!response.success) {
|
|
797
|
-
return { ...currentState, error: response.message || 'Failed to create security block rule' };
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
801
|
-
} catch (error: unknown) {
|
|
802
|
-
return {
|
|
803
|
-
...currentState,
|
|
804
|
-
error: error instanceof Error ? error.message : 'Failed to create security block rule',
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
export const updateSecurityBlockRuleAction = securityPolicyStatePart.createAction<{
|
|
810
|
-
id: string;
|
|
811
|
-
value?: string;
|
|
812
|
-
matchMode?: interfaces.data.TSecurityBlockRuleMatchMode;
|
|
813
|
-
reason?: string;
|
|
814
|
-
enabled?: boolean;
|
|
815
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<ISecurityPolicyState> => {
|
|
816
|
-
const context = getActionContext();
|
|
817
|
-
const currentState = statePartArg.getState()!;
|
|
818
|
-
if (!context.identity) return currentState;
|
|
819
|
-
|
|
820
|
-
try {
|
|
821
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
822
|
-
interfaces.requests.IReq_UpdateSecurityBlockRule
|
|
823
|
-
>('/typedrequest', 'updateSecurityBlockRule');
|
|
824
|
-
|
|
825
|
-
const response = await request.fire({
|
|
826
|
-
identity: context.identity,
|
|
827
|
-
id: dataArg.id,
|
|
828
|
-
value: dataArg.value,
|
|
829
|
-
matchMode: dataArg.matchMode,
|
|
830
|
-
reason: dataArg.reason,
|
|
831
|
-
enabled: dataArg.enabled,
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
if (!response.success) {
|
|
835
|
-
return { ...currentState, error: response.message || 'Failed to update security block rule' };
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
839
|
-
} catch (error: unknown) {
|
|
840
|
-
return {
|
|
841
|
-
...currentState,
|
|
842
|
-
error: error instanceof Error ? error.message : 'Failed to update security block rule',
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
export const deleteSecurityBlockRuleAction = securityPolicyStatePart.createAction<string>(
|
|
848
|
-
async (statePartArg, ruleId, actionContext): Promise<ISecurityPolicyState> => {
|
|
849
|
-
const context = getActionContext();
|
|
850
|
-
const currentState = statePartArg.getState()!;
|
|
851
|
-
if (!context.identity) return currentState;
|
|
852
|
-
|
|
853
|
-
try {
|
|
854
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
855
|
-
interfaces.requests.IReq_DeleteSecurityBlockRule
|
|
856
|
-
>('/typedrequest', 'deleteSecurityBlockRule');
|
|
857
|
-
|
|
858
|
-
const response = await request.fire({ identity: context.identity, id: ruleId });
|
|
859
|
-
if (!response.success) {
|
|
860
|
-
return { ...currentState, error: response.message || 'Failed to delete security block rule' };
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
864
|
-
} catch (error: unknown) {
|
|
865
|
-
return {
|
|
866
|
-
...currentState,
|
|
867
|
-
error: error instanceof Error ? error.message : 'Failed to delete security block rule',
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
},
|
|
871
|
-
);
|
|
872
|
-
|
|
873
|
-
export const refreshIpIntelligenceAction = securityPolicyStatePart.createAction<string>(
|
|
874
|
-
async (statePartArg, ipAddress, actionContext): Promise<ISecurityPolicyState> => {
|
|
875
|
-
const context = getActionContext();
|
|
876
|
-
const currentState = statePartArg.getState()!;
|
|
877
|
-
if (!context.identity) return currentState;
|
|
878
|
-
|
|
879
|
-
try {
|
|
880
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
881
|
-
interfaces.requests.IReq_RefreshIpIntelligence
|
|
882
|
-
>('/typedrequest', 'refreshIpIntelligence');
|
|
883
|
-
const response = await request.fire({ identity: context.identity, ipAddress });
|
|
884
|
-
if (!response.success) {
|
|
885
|
-
return { ...currentState, error: response.message || 'Failed to refresh IP intelligence' };
|
|
886
|
-
}
|
|
887
|
-
const refreshedState = await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
888
|
-
if (!response.record) return refreshedState;
|
|
889
|
-
return {
|
|
890
|
-
...refreshedState,
|
|
891
|
-
ipIntelligence: [
|
|
892
|
-
response.record,
|
|
893
|
-
...refreshedState.ipIntelligence.filter((record) => record.ipAddress !== response.record!.ipAddress),
|
|
894
|
-
],
|
|
895
|
-
};
|
|
896
|
-
} catch (error: unknown) {
|
|
897
|
-
return {
|
|
898
|
-
...currentState,
|
|
899
|
-
error: error instanceof Error ? error.message : 'Failed to refresh IP intelligence',
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
},
|
|
903
|
-
);
|
|
904
|
-
|
|
905
|
-
// ============================================================================
|
|
906
|
-
// Email Operations Actions
|
|
907
|
-
// ============================================================================
|
|
908
|
-
|
|
909
|
-
// Fetch All Emails Action
|
|
910
|
-
export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg): Promise<IEmailOpsState> => {
|
|
911
|
-
const context = getActionContext();
|
|
912
|
-
const currentState = statePartArg.getState()!;
|
|
913
|
-
if (!context.identity) return currentState;
|
|
914
|
-
|
|
915
|
-
try {
|
|
916
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
917
|
-
interfaces.requests.IReq_GetAllEmails
|
|
918
|
-
>('/typedrequest', 'getAllEmails');
|
|
919
|
-
|
|
920
|
-
const response = await request.fire({
|
|
921
|
-
identity: context.identity,
|
|
922
|
-
});
|
|
923
|
-
|
|
924
|
-
return {
|
|
925
|
-
emails: response.emails,
|
|
926
|
-
isLoading: false,
|
|
927
|
-
error: null,
|
|
928
|
-
lastUpdated: Date.now(),
|
|
929
|
-
};
|
|
930
|
-
} catch (error) {
|
|
931
|
-
return {
|
|
932
|
-
...currentState,
|
|
933
|
-
isLoading: false,
|
|
934
|
-
error: error instanceof Error ? error.message : 'Failed to fetch emails',
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
|
|
939
|
-
// ============================================================================
|
|
940
|
-
// Certificate Actions
|
|
941
|
-
// ============================================================================
|
|
942
|
-
|
|
943
|
-
export const fetchCertificateOverviewAction = certificateStatePart.createAction(async (statePartArg): Promise<ICertificateState> => {
|
|
944
|
-
const context = getActionContext();
|
|
945
|
-
const currentState = statePartArg.getState()!;
|
|
946
|
-
if (!context.identity) return currentState;
|
|
947
|
-
|
|
948
|
-
try {
|
|
949
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
950
|
-
interfaces.requests.IReq_GetCertificateOverview
|
|
951
|
-
>('/typedrequest', 'getCertificateOverview');
|
|
952
|
-
|
|
953
|
-
const response = await request.fire({
|
|
954
|
-
identity: context.identity,
|
|
955
|
-
});
|
|
956
|
-
|
|
957
|
-
return {
|
|
958
|
-
certificates: response.certificates,
|
|
959
|
-
summary: response.summary,
|
|
960
|
-
isLoading: false,
|
|
961
|
-
error: null,
|
|
962
|
-
lastUpdated: Date.now(),
|
|
963
|
-
};
|
|
964
|
-
} catch (error) {
|
|
965
|
-
return {
|
|
966
|
-
...currentState,
|
|
967
|
-
isLoading: false,
|
|
968
|
-
error: error instanceof Error ? error.message : 'Failed to fetch certificate overview',
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
});
|
|
972
|
-
|
|
973
|
-
export const reprovisionCertificateAction = certificateStatePart.createAction<{ domain: string; forceRenew?: boolean }>(
|
|
974
|
-
async (statePartArg, dataArg, actionContext): Promise<ICertificateState> => {
|
|
975
|
-
const context = getActionContext();
|
|
976
|
-
const currentState = statePartArg.getState()!;
|
|
977
|
-
|
|
978
|
-
try {
|
|
979
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
980
|
-
interfaces.requests.IReq_ReprovisionCertificateDomain
|
|
981
|
-
>('/typedrequest', 'reprovisionCertificateDomain');
|
|
982
|
-
|
|
983
|
-
await request.fire({
|
|
984
|
-
identity: context.identity!,
|
|
985
|
-
domain: dataArg.domain,
|
|
986
|
-
forceRenew: dataArg.forceRenew,
|
|
987
|
-
});
|
|
988
|
-
|
|
989
|
-
// Re-fetch overview after reprovisioning
|
|
990
|
-
return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
|
|
991
|
-
} catch (error: unknown) {
|
|
992
|
-
return {
|
|
993
|
-
...currentState,
|
|
994
|
-
error: error instanceof Error ? error.message : 'Failed to reprovision certificate',
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
);
|
|
999
|
-
|
|
1000
|
-
export const deleteCertificateAction = certificateStatePart.createAction<string>(
|
|
1001
|
-
async (statePartArg, domain, actionContext): Promise<ICertificateState> => {
|
|
1002
|
-
const context = getActionContext();
|
|
1003
|
-
const currentState = statePartArg.getState()!;
|
|
1004
|
-
|
|
1005
|
-
try {
|
|
1006
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1007
|
-
interfaces.requests.IReq_DeleteCertificate
|
|
1008
|
-
>('/typedrequest', 'deleteCertificate');
|
|
1009
|
-
|
|
1010
|
-
await request.fire({
|
|
1011
|
-
identity: context.identity!,
|
|
1012
|
-
domain,
|
|
1013
|
-
});
|
|
1014
|
-
|
|
1015
|
-
// Re-fetch overview after deletion
|
|
1016
|
-
return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
|
|
1017
|
-
} catch (error: unknown) {
|
|
1018
|
-
return {
|
|
1019
|
-
...currentState,
|
|
1020
|
-
error: error instanceof Error ? error.message : 'Failed to delete certificate',
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
);
|
|
1025
|
-
|
|
1026
|
-
export const importCertificateAction = certificateStatePart.createAction<{
|
|
1027
|
-
id: string;
|
|
1028
|
-
domainName: string;
|
|
1029
|
-
created: number;
|
|
1030
|
-
validUntil: number;
|
|
1031
|
-
privateKey: string;
|
|
1032
|
-
publicKey: string;
|
|
1033
|
-
csr: string;
|
|
1034
|
-
}>(
|
|
1035
|
-
async (statePartArg, cert, actionContext): Promise<ICertificateState> => {
|
|
1036
|
-
const context = getActionContext();
|
|
1037
|
-
const currentState = statePartArg.getState()!;
|
|
1038
|
-
|
|
1039
|
-
try {
|
|
1040
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1041
|
-
servezoneInterfaces.requests.gateway.IReq_ImportCertificate
|
|
1042
|
-
>('/typedrequest', 'importCertificate');
|
|
1043
|
-
|
|
1044
|
-
await request.fire({
|
|
1045
|
-
identity: context.identity!,
|
|
1046
|
-
cert,
|
|
1047
|
-
});
|
|
1048
|
-
|
|
1049
|
-
// Re-fetch overview after import
|
|
1050
|
-
return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
|
|
1051
|
-
} catch (error: unknown) {
|
|
1052
|
-
return {
|
|
1053
|
-
...currentState,
|
|
1054
|
-
error: error instanceof Error ? error.message : 'Failed to import certificate',
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
);
|
|
1059
|
-
|
|
1060
|
-
export async function fetchCertificateExport(domain: string) {
|
|
1061
|
-
const context = getActionContext();
|
|
1062
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1063
|
-
servezoneInterfaces.requests.gateway.IReq_ExportCertificate
|
|
1064
|
-
>('/typedrequest', 'exportCertificate');
|
|
1065
|
-
|
|
1066
|
-
return request.fire({
|
|
1067
|
-
identity: context.identity!,
|
|
1068
|
-
domain,
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
// ============================================================================
|
|
1073
|
-
// Remote Ingress Standalone Functions
|
|
1074
|
-
// ============================================================================
|
|
1075
|
-
|
|
1076
|
-
export async function fetchConnectionToken(edgeId: string) {
|
|
1077
|
-
const context = getActionContext();
|
|
1078
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1079
|
-
interfaces.requests.IReq_GetRemoteIngressConnectionToken
|
|
1080
|
-
>('/typedrequest', 'getRemoteIngressConnectionToken');
|
|
1081
|
-
return request.fire({ identity: context.identity!, edgeId });
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
// ============================================================================
|
|
1085
|
-
// Remote Ingress Actions
|
|
1086
|
-
// ============================================================================
|
|
1087
|
-
|
|
1088
|
-
export const fetchRemoteIngressAction = remoteIngressStatePart.createAction(async (statePartArg): Promise<IRemoteIngressState> => {
|
|
1089
|
-
const context = getActionContext();
|
|
1090
|
-
const currentState = statePartArg.getState()!;
|
|
1091
|
-
if (!context.identity) return currentState;
|
|
1092
|
-
|
|
1093
|
-
try {
|
|
1094
|
-
const edgesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1095
|
-
interfaces.requests.IReq_GetRemoteIngresses
|
|
1096
|
-
>('/typedrequest', 'getRemoteIngresses');
|
|
1097
|
-
|
|
1098
|
-
const statusRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1099
|
-
interfaces.requests.IReq_GetRemoteIngressStatus
|
|
1100
|
-
>('/typedrequest', 'getRemoteIngressStatus');
|
|
1101
|
-
|
|
1102
|
-
const hubSettingsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1103
|
-
interfaces.requests.IReq_GetRemoteIngressHubSettings
|
|
1104
|
-
>('/typedrequest', 'getRemoteIngressHubSettings');
|
|
1105
|
-
|
|
1106
|
-
const [edgesResponse, statusResponse, hubSettingsResponse] = await Promise.all([
|
|
1107
|
-
edgesRequest.fire({ identity: context.identity }),
|
|
1108
|
-
statusRequest.fire({ identity: context.identity }),
|
|
1109
|
-
hubSettingsRequest.fire({ identity: context.identity }),
|
|
1110
|
-
]);
|
|
1111
|
-
|
|
1112
|
-
return {
|
|
1113
|
-
...currentState,
|
|
1114
|
-
edges: edgesResponse.edges,
|
|
1115
|
-
statuses: statusResponse.statuses,
|
|
1116
|
-
hubSettings: hubSettingsResponse.settings,
|
|
1117
|
-
isLoading: false,
|
|
1118
|
-
error: null,
|
|
1119
|
-
lastUpdated: Date.now(),
|
|
1120
|
-
};
|
|
1121
|
-
} catch (error) {
|
|
1122
|
-
return {
|
|
1123
|
-
...currentState,
|
|
1124
|
-
isLoading: false,
|
|
1125
|
-
error: error instanceof Error ? error.message : 'Failed to fetch remote ingress data',
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
});
|
|
1129
|
-
|
|
1130
|
-
export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
1131
|
-
name: string;
|
|
1132
|
-
listenPorts?: number[];
|
|
1133
|
-
autoDerivePorts?: boolean;
|
|
1134
|
-
performance?: interfaces.data.IRemoteIngressPerformanceConfig;
|
|
1135
|
-
tags?: string[];
|
|
1136
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
|
|
1137
|
-
const context = getActionContext();
|
|
1138
|
-
const currentState = statePartArg.getState()!;
|
|
1139
|
-
|
|
1140
|
-
try {
|
|
1141
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1142
|
-
interfaces.requests.IReq_CreateRemoteIngress
|
|
1143
|
-
>('/typedrequest', 'createRemoteIngress');
|
|
1144
|
-
|
|
1145
|
-
const response = await request.fire({
|
|
1146
|
-
identity: context.identity!,
|
|
1147
|
-
name: dataArg.name,
|
|
1148
|
-
listenPorts: dataArg.listenPorts,
|
|
1149
|
-
autoDerivePorts: dataArg.autoDerivePorts,
|
|
1150
|
-
performance: dataArg.performance,
|
|
1151
|
-
tags: dataArg.tags,
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
if (response.success) {
|
|
1155
|
-
// Refresh the list
|
|
1156
|
-
await actionContext!.dispatch(fetchRemoteIngressAction, null);
|
|
1157
|
-
|
|
1158
|
-
return {
|
|
1159
|
-
...statePartArg.getState()!,
|
|
1160
|
-
newEdgeId: response.edge.id,
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
return currentState;
|
|
1165
|
-
} catch (error: unknown) {
|
|
1166
|
-
return {
|
|
1167
|
-
...currentState,
|
|
1168
|
-
error: error instanceof Error ? error.message : 'Failed to create edge',
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
});
|
|
1172
|
-
|
|
1173
|
-
export const deleteRemoteIngressAction = remoteIngressStatePart.createAction<string>(
|
|
1174
|
-
async (statePartArg, edgeId, actionContext): Promise<IRemoteIngressState> => {
|
|
1175
|
-
const context = getActionContext();
|
|
1176
|
-
const currentState = statePartArg.getState()!;
|
|
1177
|
-
|
|
1178
|
-
try {
|
|
1179
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1180
|
-
interfaces.requests.IReq_DeleteRemoteIngress
|
|
1181
|
-
>('/typedrequest', 'deleteRemoteIngress');
|
|
1182
|
-
|
|
1183
|
-
await request.fire({
|
|
1184
|
-
identity: context.identity!,
|
|
1185
|
-
id: edgeId,
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
|
|
1189
|
-
} catch (error: unknown) {
|
|
1190
|
-
return {
|
|
1191
|
-
...currentState,
|
|
1192
|
-
error: error instanceof Error ? error.message : 'Failed to delete edge',
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
);
|
|
1197
|
-
|
|
1198
|
-
export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
1199
|
-
id: string;
|
|
1200
|
-
name?: string;
|
|
1201
|
-
listenPorts?: number[];
|
|
1202
|
-
autoDerivePorts?: boolean;
|
|
1203
|
-
performance?: interfaces.data.IRemoteIngressPerformanceConfig;
|
|
1204
|
-
tags?: string[];
|
|
1205
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
|
|
1206
|
-
const context = getActionContext();
|
|
1207
|
-
const currentState = statePartArg.getState()!;
|
|
1208
|
-
|
|
1209
|
-
try {
|
|
1210
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1211
|
-
interfaces.requests.IReq_UpdateRemoteIngress
|
|
1212
|
-
>('/typedrequest', 'updateRemoteIngress');
|
|
1213
|
-
|
|
1214
|
-
await request.fire({
|
|
1215
|
-
identity: context.identity!,
|
|
1216
|
-
id: dataArg.id,
|
|
1217
|
-
name: dataArg.name,
|
|
1218
|
-
listenPorts: dataArg.listenPorts,
|
|
1219
|
-
autoDerivePorts: dataArg.autoDerivePorts,
|
|
1220
|
-
performance: dataArg.performance,
|
|
1221
|
-
tags: dataArg.tags,
|
|
1222
|
-
});
|
|
1223
|
-
|
|
1224
|
-
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
|
|
1225
|
-
} catch (error: unknown) {
|
|
1226
|
-
return {
|
|
1227
|
-
...currentState,
|
|
1228
|
-
error: error instanceof Error ? error.message : 'Failed to update edge',
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
|
-
});
|
|
1232
|
-
|
|
1233
|
-
export const updateRemoteIngressHubSettingsAction = remoteIngressStatePart.createAction<{
|
|
1234
|
-
enabled?: boolean;
|
|
1235
|
-
tunnelPort?: number;
|
|
1236
|
-
hubDomain?: string | null;
|
|
1237
|
-
performance?: interfaces.data.IRemoteIngressPerformanceConfig | null;
|
|
1238
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
|
|
1239
|
-
const context = getActionContext();
|
|
1240
|
-
const currentState = statePartArg.getState()!;
|
|
1241
|
-
|
|
1242
|
-
try {
|
|
1243
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1244
|
-
interfaces.requests.IReq_UpdateRemoteIngressHubSettings
|
|
1245
|
-
>('/typedrequest', 'updateRemoteIngressHubSettings');
|
|
1246
|
-
|
|
1247
|
-
const response = await request.fire({
|
|
1248
|
-
identity: context.identity!,
|
|
1249
|
-
enabled: dataArg.enabled,
|
|
1250
|
-
tunnelPort: dataArg.tunnelPort,
|
|
1251
|
-
hubDomain: dataArg.hubDomain,
|
|
1252
|
-
performance: dataArg.performance,
|
|
1253
|
-
});
|
|
1254
|
-
|
|
1255
|
-
if (!response.success) {
|
|
1256
|
-
return {
|
|
1257
|
-
...currentState,
|
|
1258
|
-
error: response.message || 'Failed to update RemoteIngress hub settings',
|
|
1259
|
-
};
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
|
|
1263
|
-
} catch (error: unknown) {
|
|
1264
|
-
return {
|
|
1265
|
-
...currentState,
|
|
1266
|
-
error: error instanceof Error ? error.message : 'Failed to update RemoteIngress hub settings',
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
|
|
1271
|
-
export const regenerateRemoteIngressSecretAction = remoteIngressStatePart.createAction<string>(
|
|
1272
|
-
async (statePartArg, edgeId): Promise<IRemoteIngressState> => {
|
|
1273
|
-
const context = getActionContext();
|
|
1274
|
-
const currentState = statePartArg.getState()!;
|
|
1275
|
-
|
|
1276
|
-
try {
|
|
1277
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1278
|
-
interfaces.requests.IReq_RegenerateRemoteIngressSecret
|
|
1279
|
-
>('/typedrequest', 'regenerateRemoteIngressSecret');
|
|
1280
|
-
|
|
1281
|
-
const response = await request.fire({
|
|
1282
|
-
identity: context.identity!,
|
|
1283
|
-
id: edgeId,
|
|
1284
|
-
});
|
|
1285
|
-
|
|
1286
|
-
if (response.success) {
|
|
1287
|
-
return {
|
|
1288
|
-
...currentState,
|
|
1289
|
-
newEdgeId: edgeId,
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
return currentState;
|
|
1294
|
-
} catch (error) {
|
|
1295
|
-
return {
|
|
1296
|
-
...currentState,
|
|
1297
|
-
error: error instanceof Error ? error.message : 'Failed to regenerate secret',
|
|
1298
|
-
};
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
);
|
|
1302
|
-
|
|
1303
|
-
export const clearNewEdgeIdAction = remoteIngressStatePart.createAction(
|
|
1304
|
-
async (statePartArg): Promise<IRemoteIngressState> => {
|
|
1305
|
-
return {
|
|
1306
|
-
...statePartArg.getState()!,
|
|
1307
|
-
newEdgeId: null,
|
|
1308
|
-
};
|
|
1309
|
-
}
|
|
1310
|
-
);
|
|
1311
|
-
|
|
1312
|
-
export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
1313
|
-
id: string;
|
|
1314
|
-
enabled: boolean;
|
|
1315
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
|
|
1316
|
-
const context = getActionContext();
|
|
1317
|
-
const currentState = statePartArg.getState()!;
|
|
1318
|
-
|
|
1319
|
-
try {
|
|
1320
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1321
|
-
interfaces.requests.IReq_UpdateRemoteIngress
|
|
1322
|
-
>('/typedrequest', 'updateRemoteIngress');
|
|
1323
|
-
|
|
1324
|
-
await request.fire({
|
|
1325
|
-
identity: context.identity!,
|
|
1326
|
-
id: dataArg.id,
|
|
1327
|
-
enabled: dataArg.enabled,
|
|
1328
|
-
});
|
|
1329
|
-
|
|
1330
|
-
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
|
|
1331
|
-
} catch (error: unknown) {
|
|
1332
|
-
return {
|
|
1333
|
-
...currentState,
|
|
1334
|
-
error: error instanceof Error ? error.message : 'Failed to toggle edge',
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
|
-
|
|
1339
|
-
// ============================================================================
|
|
1340
|
-
// VPN State
|
|
1341
|
-
// ============================================================================
|
|
1342
|
-
|
|
1343
|
-
export interface IVpnState {
|
|
1344
|
-
clients: interfaces.data.IVpnClient[];
|
|
1345
|
-
connectedClients: interfaces.data.IVpnConnectedClient[];
|
|
1346
|
-
status: interfaces.data.IVpnServerStatus | null;
|
|
1347
|
-
isLoading: boolean;
|
|
1348
|
-
error: string | null;
|
|
1349
|
-
lastUpdated: number;
|
|
1350
|
-
/** WireGuard config shown after create/rotate (only shown once) */
|
|
1351
|
-
newClientConfig: string | null;
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
export const vpnStatePart = await appState.getStatePart<IVpnState>(
|
|
1355
|
-
'vpn',
|
|
1356
|
-
{
|
|
1357
|
-
clients: [],
|
|
1358
|
-
connectedClients: [],
|
|
1359
|
-
status: null,
|
|
1360
|
-
isLoading: false,
|
|
1361
|
-
error: null,
|
|
1362
|
-
lastUpdated: 0,
|
|
1363
|
-
newClientConfig: null,
|
|
1364
|
-
},
|
|
1365
|
-
'soft'
|
|
1366
|
-
);
|
|
1367
|
-
|
|
1368
|
-
// ============================================================================
|
|
1369
|
-
// VPN Actions
|
|
1370
|
-
// ============================================================================
|
|
1371
|
-
|
|
1372
|
-
export const fetchVpnAction = vpnStatePart.createAction(async (statePartArg): Promise<IVpnState> => {
|
|
1373
|
-
const context = getActionContext();
|
|
1374
|
-
const currentState = statePartArg.getState()!;
|
|
1375
|
-
if (!context.identity) return currentState;
|
|
1376
|
-
|
|
1377
|
-
try {
|
|
1378
|
-
const clientsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1379
|
-
interfaces.requests.IReq_GetVpnClients
|
|
1380
|
-
>('/typedrequest', 'getVpnClients');
|
|
1381
|
-
|
|
1382
|
-
const statusRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1383
|
-
interfaces.requests.IReq_GetVpnStatus
|
|
1384
|
-
>('/typedrequest', 'getVpnStatus');
|
|
1385
|
-
|
|
1386
|
-
const connectedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1387
|
-
interfaces.requests.IReq_GetVpnConnectedClients
|
|
1388
|
-
>('/typedrequest', 'getVpnConnectedClients');
|
|
1389
|
-
|
|
1390
|
-
const [clientsResponse, statusResponse, connectedResponse] = await Promise.all([
|
|
1391
|
-
clientsRequest.fire({ identity: context.identity }),
|
|
1392
|
-
statusRequest.fire({ identity: context.identity }),
|
|
1393
|
-
connectedRequest.fire({ identity: context.identity }),
|
|
1394
|
-
]);
|
|
1395
|
-
|
|
1396
|
-
return {
|
|
1397
|
-
...currentState,
|
|
1398
|
-
clients: clientsResponse.clients,
|
|
1399
|
-
connectedClients: connectedResponse.connectedClients,
|
|
1400
|
-
status: statusResponse.status,
|
|
1401
|
-
isLoading: false,
|
|
1402
|
-
error: null,
|
|
1403
|
-
lastUpdated: Date.now(),
|
|
1404
|
-
};
|
|
1405
|
-
} catch (error) {
|
|
1406
|
-
return {
|
|
1407
|
-
...currentState,
|
|
1408
|
-
isLoading: false,
|
|
1409
|
-
error: error instanceof Error ? error.message : 'Failed to fetch VPN data',
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
});
|
|
1413
|
-
|
|
1414
|
-
export const createVpnClientAction = vpnStatePart.createAction<{
|
|
1415
|
-
clientId: string;
|
|
1416
|
-
targetProfileIds?: string[];
|
|
1417
|
-
description?: string;
|
|
1418
|
-
|
|
1419
|
-
destinationAllowList?: string[];
|
|
1420
|
-
destinationBlockList?: string[];
|
|
1421
|
-
useHostIp?: boolean;
|
|
1422
|
-
useDhcp?: boolean;
|
|
1423
|
-
staticIp?: string;
|
|
1424
|
-
forceVlan?: boolean;
|
|
1425
|
-
vlanId?: number;
|
|
1426
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
|
|
1427
|
-
const context = getActionContext();
|
|
1428
|
-
const currentState = statePartArg.getState()!;
|
|
1429
|
-
|
|
1430
|
-
try {
|
|
1431
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1432
|
-
interfaces.requests.IReq_CreateVpnClient
|
|
1433
|
-
>('/typedrequest', 'createVpnClient');
|
|
1434
|
-
|
|
1435
|
-
const response = await request.fire({
|
|
1436
|
-
identity: context.identity!,
|
|
1437
|
-
clientId: dataArg.clientId,
|
|
1438
|
-
targetProfileIds: dataArg.targetProfileIds,
|
|
1439
|
-
description: dataArg.description,
|
|
1440
|
-
|
|
1441
|
-
destinationAllowList: dataArg.destinationAllowList,
|
|
1442
|
-
destinationBlockList: dataArg.destinationBlockList,
|
|
1443
|
-
useHostIp: dataArg.useHostIp,
|
|
1444
|
-
useDhcp: dataArg.useDhcp,
|
|
1445
|
-
staticIp: dataArg.staticIp,
|
|
1446
|
-
forceVlan: dataArg.forceVlan,
|
|
1447
|
-
vlanId: dataArg.vlanId,
|
|
1448
|
-
});
|
|
1449
|
-
|
|
1450
|
-
if (!response.success) {
|
|
1451
|
-
return { ...currentState, error: response.message || 'Failed to create client' };
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
const refreshed = await actionContext!.dispatch(fetchVpnAction, null);
|
|
1455
|
-
return {
|
|
1456
|
-
...refreshed,
|
|
1457
|
-
newClientConfig: response.wireguardConfig || null,
|
|
1458
|
-
};
|
|
1459
|
-
} catch (error: unknown) {
|
|
1460
|
-
return {
|
|
1461
|
-
...currentState,
|
|
1462
|
-
error: error instanceof Error ? error.message : 'Failed to create VPN client',
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
});
|
|
1466
|
-
|
|
1467
|
-
export const deleteVpnClientAction = vpnStatePart.createAction<string>(
|
|
1468
|
-
async (statePartArg, clientId, actionContext): Promise<IVpnState> => {
|
|
1469
|
-
const context = getActionContext();
|
|
1470
|
-
const currentState = statePartArg.getState()!;
|
|
1471
|
-
|
|
1472
|
-
try {
|
|
1473
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1474
|
-
interfaces.requests.IReq_DeleteVpnClient
|
|
1475
|
-
>('/typedrequest', 'deleteVpnClient');
|
|
1476
|
-
|
|
1477
|
-
await request.fire({ identity: context.identity!, clientId });
|
|
1478
|
-
return await actionContext!.dispatch(fetchVpnAction, null);
|
|
1479
|
-
} catch (error: unknown) {
|
|
1480
|
-
return {
|
|
1481
|
-
...currentState,
|
|
1482
|
-
error: error instanceof Error ? error.message : 'Failed to delete VPN client',
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
},
|
|
1486
|
-
);
|
|
1487
|
-
|
|
1488
|
-
export const toggleVpnClientAction = vpnStatePart.createAction<{
|
|
1489
|
-
clientId: string;
|
|
1490
|
-
enabled: boolean;
|
|
1491
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
|
|
1492
|
-
const context = getActionContext();
|
|
1493
|
-
const currentState = statePartArg.getState()!;
|
|
1494
|
-
|
|
1495
|
-
try {
|
|
1496
|
-
const method = dataArg.enabled ? 'enableVpnClient' : 'disableVpnClient';
|
|
1497
|
-
type TReq = interfaces.requests.IReq_EnableVpnClient | interfaces.requests.IReq_DisableVpnClient;
|
|
1498
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<TReq>(
|
|
1499
|
-
'/typedrequest', method,
|
|
1500
|
-
);
|
|
1501
|
-
|
|
1502
|
-
await request.fire({ identity: context.identity!, clientId: dataArg.clientId });
|
|
1503
|
-
return await actionContext!.dispatch(fetchVpnAction, null);
|
|
1504
|
-
} catch (error: unknown) {
|
|
1505
|
-
return {
|
|
1506
|
-
...currentState,
|
|
1507
|
-
error: error instanceof Error ? error.message : 'Failed to toggle VPN client',
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
});
|
|
1511
|
-
|
|
1512
|
-
export const updateVpnClientAction = vpnStatePart.createAction<{
|
|
1513
|
-
clientId: string;
|
|
1514
|
-
description?: string;
|
|
1515
|
-
targetProfileIds?: string[];
|
|
1516
|
-
|
|
1517
|
-
destinationAllowList?: string[];
|
|
1518
|
-
destinationBlockList?: string[];
|
|
1519
|
-
useHostIp?: boolean;
|
|
1520
|
-
useDhcp?: boolean;
|
|
1521
|
-
staticIp?: string;
|
|
1522
|
-
forceVlan?: boolean;
|
|
1523
|
-
vlanId?: number;
|
|
1524
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IVpnState> => {
|
|
1525
|
-
const context = getActionContext();
|
|
1526
|
-
const currentState = statePartArg.getState()!;
|
|
1527
|
-
|
|
1528
|
-
try {
|
|
1529
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1530
|
-
interfaces.requests.IReq_UpdateVpnClient
|
|
1531
|
-
>('/typedrequest', 'updateVpnClient');
|
|
1532
|
-
|
|
1533
|
-
const response = await request.fire({
|
|
1534
|
-
identity: context.identity!,
|
|
1535
|
-
clientId: dataArg.clientId,
|
|
1536
|
-
description: dataArg.description,
|
|
1537
|
-
targetProfileIds: dataArg.targetProfileIds,
|
|
1538
|
-
|
|
1539
|
-
destinationAllowList: dataArg.destinationAllowList,
|
|
1540
|
-
destinationBlockList: dataArg.destinationBlockList,
|
|
1541
|
-
useHostIp: dataArg.useHostIp,
|
|
1542
|
-
useDhcp: dataArg.useDhcp,
|
|
1543
|
-
staticIp: dataArg.staticIp,
|
|
1544
|
-
forceVlan: dataArg.forceVlan,
|
|
1545
|
-
vlanId: dataArg.vlanId,
|
|
1546
|
-
});
|
|
1547
|
-
|
|
1548
|
-
if (!response.success) {
|
|
1549
|
-
return { ...currentState, error: response.message || 'Failed to update client' };
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
return await actionContext!.dispatch(fetchVpnAction, null);
|
|
1553
|
-
} catch (error: unknown) {
|
|
1554
|
-
return {
|
|
1555
|
-
...currentState,
|
|
1556
|
-
error: error instanceof Error ? error.message : 'Failed to update VPN client',
|
|
1557
|
-
};
|
|
1558
|
-
}
|
|
1559
|
-
});
|
|
1560
|
-
|
|
1561
|
-
export const clearNewClientConfigAction = vpnStatePart.createAction(
|
|
1562
|
-
async (statePartArg): Promise<IVpnState> => {
|
|
1563
|
-
return { ...statePartArg.getState()!, newClientConfig: null };
|
|
1564
|
-
},
|
|
1565
|
-
);
|
|
1566
|
-
|
|
1567
|
-
// ============================================================================
|
|
1568
|
-
// Target Profiles State
|
|
1569
|
-
// ============================================================================
|
|
1570
|
-
|
|
1571
|
-
export interface ITargetProfilesState {
|
|
1572
|
-
profiles: interfaces.data.ITargetProfile[];
|
|
1573
|
-
isLoading: boolean;
|
|
1574
|
-
error: string | null;
|
|
1575
|
-
lastUpdated: number;
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
export const targetProfilesStatePart = await appState.getStatePart<ITargetProfilesState>(
|
|
1579
|
-
'targetProfiles',
|
|
1580
|
-
{
|
|
1581
|
-
profiles: [],
|
|
1582
|
-
isLoading: false,
|
|
1583
|
-
error: null,
|
|
1584
|
-
lastUpdated: 0,
|
|
1585
|
-
},
|
|
1586
|
-
'soft'
|
|
1587
|
-
);
|
|
1588
|
-
|
|
1589
|
-
// ============================================================================
|
|
1590
|
-
// Target Profiles Actions
|
|
1591
|
-
// ============================================================================
|
|
1592
|
-
|
|
1593
|
-
export const fetchTargetProfilesAction = targetProfilesStatePart.createAction(
|
|
1594
|
-
async (statePartArg): Promise<ITargetProfilesState> => {
|
|
1595
|
-
const context = getActionContext();
|
|
1596
|
-
const currentState = statePartArg.getState()!;
|
|
1597
|
-
if (!context.identity) return currentState;
|
|
1598
|
-
|
|
1599
|
-
try {
|
|
1600
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1601
|
-
interfaces.requests.IReq_GetTargetProfiles
|
|
1602
|
-
>('/typedrequest', 'getTargetProfiles');
|
|
1603
|
-
|
|
1604
|
-
const response = await request.fire({ identity: context.identity });
|
|
1605
|
-
|
|
1606
|
-
return {
|
|
1607
|
-
profiles: response.profiles,
|
|
1608
|
-
isLoading: false,
|
|
1609
|
-
error: null,
|
|
1610
|
-
lastUpdated: Date.now(),
|
|
1611
|
-
};
|
|
1612
|
-
} catch (error) {
|
|
1613
|
-
return {
|
|
1614
|
-
...currentState,
|
|
1615
|
-
isLoading: false,
|
|
1616
|
-
error: error instanceof Error ? error.message : 'Failed to fetch target profiles',
|
|
1617
|
-
};
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
);
|
|
1621
|
-
|
|
1622
|
-
export const createTargetProfileAction = targetProfilesStatePart.createAction<{
|
|
1623
|
-
name: string;
|
|
1624
|
-
description?: string;
|
|
1625
|
-
domains?: string[];
|
|
1626
|
-
targets?: Array<{ ip: string; port: number }>;
|
|
1627
|
-
routeRefs?: string[];
|
|
1628
|
-
allowRoutesByClientSourceIp?: boolean;
|
|
1629
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<ITargetProfilesState> => {
|
|
1630
|
-
const context = getActionContext();
|
|
1631
|
-
try {
|
|
1632
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1633
|
-
interfaces.requests.IReq_CreateTargetProfile
|
|
1634
|
-
>('/typedrequest', 'createTargetProfile');
|
|
1635
|
-
const response = await request.fire({
|
|
1636
|
-
identity: context.identity!,
|
|
1637
|
-
name: dataArg.name,
|
|
1638
|
-
description: dataArg.description,
|
|
1639
|
-
domains: dataArg.domains,
|
|
1640
|
-
targets: dataArg.targets,
|
|
1641
|
-
routeRefs: dataArg.routeRefs,
|
|
1642
|
-
allowRoutesByClientSourceIp: dataArg.allowRoutesByClientSourceIp,
|
|
1643
|
-
});
|
|
1644
|
-
if (!response.success) {
|
|
1645
|
-
return {
|
|
1646
|
-
...statePartArg.getState()!,
|
|
1647
|
-
error: response.message || 'Failed to create target profile',
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
return await actionContext!.dispatch(fetchTargetProfilesAction, null);
|
|
1651
|
-
} catch (error: unknown) {
|
|
1652
|
-
return {
|
|
1653
|
-
...statePartArg.getState()!,
|
|
1654
|
-
error: error instanceof Error ? error.message : 'Failed to create target profile',
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
});
|
|
1658
|
-
|
|
1659
|
-
export const updateTargetProfileAction = targetProfilesStatePart.createAction<{
|
|
1660
|
-
id: string;
|
|
1661
|
-
name?: string;
|
|
1662
|
-
description?: string;
|
|
1663
|
-
domains?: string[];
|
|
1664
|
-
targets?: Array<{ ip: string; port: number }>;
|
|
1665
|
-
routeRefs?: string[];
|
|
1666
|
-
allowRoutesByClientSourceIp?: boolean;
|
|
1667
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<ITargetProfilesState> => {
|
|
1668
|
-
const context = getActionContext();
|
|
1669
|
-
try {
|
|
1670
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1671
|
-
interfaces.requests.IReq_UpdateTargetProfile
|
|
1672
|
-
>('/typedrequest', 'updateTargetProfile');
|
|
1673
|
-
const response = await request.fire({
|
|
1674
|
-
identity: context.identity!,
|
|
1675
|
-
id: dataArg.id,
|
|
1676
|
-
name: dataArg.name,
|
|
1677
|
-
description: dataArg.description,
|
|
1678
|
-
domains: dataArg.domains,
|
|
1679
|
-
targets: dataArg.targets,
|
|
1680
|
-
routeRefs: dataArg.routeRefs,
|
|
1681
|
-
allowRoutesByClientSourceIp: dataArg.allowRoutesByClientSourceIp,
|
|
1682
|
-
});
|
|
1683
|
-
if (!response.success) {
|
|
1684
|
-
return {
|
|
1685
|
-
...statePartArg.getState()!,
|
|
1686
|
-
error: response.message || 'Failed to update target profile',
|
|
1687
|
-
};
|
|
1688
|
-
}
|
|
1689
|
-
return await actionContext!.dispatch(fetchTargetProfilesAction, null);
|
|
1690
|
-
} catch (error: unknown) {
|
|
1691
|
-
return {
|
|
1692
|
-
...statePartArg.getState()!,
|
|
1693
|
-
error: error instanceof Error ? error.message : 'Failed to update target profile',
|
|
1694
|
-
};
|
|
1695
|
-
}
|
|
1696
|
-
});
|
|
1697
|
-
|
|
1698
|
-
export const deleteTargetProfileAction = targetProfilesStatePart.createAction<{
|
|
1699
|
-
id: string;
|
|
1700
|
-
force?: boolean;
|
|
1701
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<ITargetProfilesState> => {
|
|
1702
|
-
const context = getActionContext();
|
|
1703
|
-
try {
|
|
1704
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1705
|
-
interfaces.requests.IReq_DeleteTargetProfile
|
|
1706
|
-
>('/typedrequest', 'deleteTargetProfile');
|
|
1707
|
-
const response = await request.fire({
|
|
1708
|
-
identity: context.identity!,
|
|
1709
|
-
id: dataArg.id,
|
|
1710
|
-
force: dataArg.force,
|
|
1711
|
-
});
|
|
1712
|
-
if (!response.success) {
|
|
1713
|
-
return {
|
|
1714
|
-
...statePartArg.getState()!,
|
|
1715
|
-
error: response.message || 'Failed to delete target profile',
|
|
1716
|
-
};
|
|
1717
|
-
}
|
|
1718
|
-
return await actionContext!.dispatch(fetchTargetProfilesAction, null);
|
|
1719
|
-
} catch (error: unknown) {
|
|
1720
|
-
return {
|
|
1721
|
-
...statePartArg.getState()!,
|
|
1722
|
-
error: error instanceof Error ? error.message : 'Failed to delete target profile',
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
});
|
|
1726
|
-
|
|
1727
|
-
// ============================================================================
|
|
1728
|
-
// Source Profiles & Network Targets State
|
|
1729
|
-
// ============================================================================
|
|
1730
|
-
|
|
1731
|
-
export interface IProfilesTargetsState {
|
|
1732
|
-
profiles: interfaces.data.ISourceProfile[];
|
|
1733
|
-
targets: interfaces.data.INetworkTarget[];
|
|
1734
|
-
isLoading: boolean;
|
|
1735
|
-
error: string | null;
|
|
1736
|
-
lastUpdated: number;
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
|
-
export const profilesTargetsStatePart = await appState.getStatePart<IProfilesTargetsState>(
|
|
1740
|
-
'profilesTargets',
|
|
1741
|
-
{
|
|
1742
|
-
profiles: [],
|
|
1743
|
-
targets: [],
|
|
1744
|
-
isLoading: false,
|
|
1745
|
-
error: null,
|
|
1746
|
-
lastUpdated: 0,
|
|
1747
|
-
},
|
|
1748
|
-
'soft'
|
|
1749
|
-
);
|
|
1750
|
-
|
|
1751
|
-
// ============================================================================
|
|
1752
|
-
// Source Profiles & Network Targets Actions
|
|
1753
|
-
// ============================================================================
|
|
1754
|
-
|
|
1755
|
-
export const fetchProfilesAndTargetsAction = profilesTargetsStatePart.createAction(
|
|
1756
|
-
async (statePartArg): Promise<IProfilesTargetsState> => {
|
|
1757
|
-
const context = getActionContext();
|
|
1758
|
-
const currentState = statePartArg.getState()!;
|
|
1759
|
-
if (!context.identity) return currentState;
|
|
1760
|
-
|
|
1761
|
-
try {
|
|
1762
|
-
const profilesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1763
|
-
interfaces.requests.IReq_GetSourceProfiles
|
|
1764
|
-
>('/typedrequest', 'getSourceProfiles');
|
|
1765
|
-
|
|
1766
|
-
const targetsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1767
|
-
interfaces.requests.IReq_GetNetworkTargets
|
|
1768
|
-
>('/typedrequest', 'getNetworkTargets');
|
|
1769
|
-
|
|
1770
|
-
const [profilesResponse, targetsResponse] = await Promise.all([
|
|
1771
|
-
profilesRequest.fire({ identity: context.identity }),
|
|
1772
|
-
targetsRequest.fire({ identity: context.identity }),
|
|
1773
|
-
]);
|
|
1774
|
-
|
|
1775
|
-
return {
|
|
1776
|
-
profiles: profilesResponse.profiles,
|
|
1777
|
-
targets: targetsResponse.targets,
|
|
1778
|
-
isLoading: false,
|
|
1779
|
-
error: null,
|
|
1780
|
-
lastUpdated: Date.now(),
|
|
1781
|
-
};
|
|
1782
|
-
} catch (error) {
|
|
1783
|
-
return {
|
|
1784
|
-
...currentState,
|
|
1785
|
-
isLoading: false,
|
|
1786
|
-
error: error instanceof Error ? error.message : 'Failed to fetch profiles/targets',
|
|
1787
|
-
};
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
);
|
|
1791
|
-
|
|
1792
|
-
export const createProfileAction = profilesTargetsStatePart.createAction<{
|
|
1793
|
-
name: string;
|
|
1794
|
-
description?: string;
|
|
1795
|
-
security: any;
|
|
1796
|
-
extendsProfiles?: string[];
|
|
1797
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1798
|
-
const context = getActionContext();
|
|
1799
|
-
try {
|
|
1800
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1801
|
-
interfaces.requests.IReq_CreateSourceProfile
|
|
1802
|
-
>('/typedrequest', 'createSourceProfile');
|
|
1803
|
-
await request.fire({
|
|
1804
|
-
identity: context.identity!,
|
|
1805
|
-
name: dataArg.name,
|
|
1806
|
-
description: dataArg.description,
|
|
1807
|
-
security: dataArg.security,
|
|
1808
|
-
extendsProfiles: dataArg.extendsProfiles,
|
|
1809
|
-
});
|
|
1810
|
-
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1811
|
-
} catch (error: unknown) {
|
|
1812
|
-
return {
|
|
1813
|
-
...statePartArg.getState()!,
|
|
1814
|
-
error: error instanceof Error ? error.message : 'Failed to create profile',
|
|
1815
|
-
};
|
|
1816
|
-
}
|
|
1817
|
-
});
|
|
1818
|
-
|
|
1819
|
-
export const updateProfileAction = profilesTargetsStatePart.createAction<{
|
|
1820
|
-
id: string;
|
|
1821
|
-
name?: string;
|
|
1822
|
-
description?: string;
|
|
1823
|
-
security?: any;
|
|
1824
|
-
extendsProfiles?: string[];
|
|
1825
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1826
|
-
const context = getActionContext();
|
|
1827
|
-
try {
|
|
1828
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1829
|
-
interfaces.requests.IReq_UpdateSourceProfile
|
|
1830
|
-
>('/typedrequest', 'updateSourceProfile');
|
|
1831
|
-
await request.fire({
|
|
1832
|
-
identity: context.identity!,
|
|
1833
|
-
id: dataArg.id,
|
|
1834
|
-
name: dataArg.name,
|
|
1835
|
-
description: dataArg.description,
|
|
1836
|
-
security: dataArg.security,
|
|
1837
|
-
extendsProfiles: dataArg.extendsProfiles,
|
|
1838
|
-
});
|
|
1839
|
-
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1840
|
-
} catch (error: unknown) {
|
|
1841
|
-
return {
|
|
1842
|
-
...statePartArg.getState()!,
|
|
1843
|
-
error: error instanceof Error ? error.message : 'Failed to update profile',
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
});
|
|
1847
|
-
|
|
1848
|
-
export const deleteProfileAction = profilesTargetsStatePart.createAction<{
|
|
1849
|
-
id: string;
|
|
1850
|
-
force?: boolean;
|
|
1851
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1852
|
-
const context = getActionContext();
|
|
1853
|
-
try {
|
|
1854
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1855
|
-
interfaces.requests.IReq_DeleteSourceProfile
|
|
1856
|
-
>('/typedrequest', 'deleteSourceProfile');
|
|
1857
|
-
const response = await request.fire({
|
|
1858
|
-
identity: context.identity!,
|
|
1859
|
-
id: dataArg.id,
|
|
1860
|
-
force: dataArg.force,
|
|
1861
|
-
});
|
|
1862
|
-
if (!response.success) {
|
|
1863
|
-
return {
|
|
1864
|
-
...statePartArg.getState()!,
|
|
1865
|
-
error: response.message || 'Failed to delete profile',
|
|
1866
|
-
};
|
|
1867
|
-
}
|
|
1868
|
-
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1869
|
-
} catch (error: unknown) {
|
|
1870
|
-
return {
|
|
1871
|
-
...statePartArg.getState()!,
|
|
1872
|
-
error: error instanceof Error ? error.message : 'Failed to delete profile',
|
|
1873
|
-
};
|
|
1874
|
-
}
|
|
1875
|
-
});
|
|
1876
|
-
|
|
1877
|
-
export const createTargetAction = profilesTargetsStatePart.createAction<{
|
|
1878
|
-
name: string;
|
|
1879
|
-
description?: string;
|
|
1880
|
-
host: string | string[];
|
|
1881
|
-
port: number;
|
|
1882
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1883
|
-
const context = getActionContext();
|
|
1884
|
-
try {
|
|
1885
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1886
|
-
interfaces.requests.IReq_CreateNetworkTarget
|
|
1887
|
-
>('/typedrequest', 'createNetworkTarget');
|
|
1888
|
-
await request.fire({
|
|
1889
|
-
identity: context.identity!,
|
|
1890
|
-
name: dataArg.name,
|
|
1891
|
-
description: dataArg.description,
|
|
1892
|
-
host: dataArg.host,
|
|
1893
|
-
port: dataArg.port,
|
|
1894
|
-
});
|
|
1895
|
-
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1896
|
-
} catch (error: unknown) {
|
|
1897
|
-
return {
|
|
1898
|
-
...statePartArg.getState()!,
|
|
1899
|
-
error: error instanceof Error ? error.message : 'Failed to create target',
|
|
1900
|
-
};
|
|
1901
|
-
}
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
export const updateTargetAction = profilesTargetsStatePart.createAction<{
|
|
1905
|
-
id: string;
|
|
1906
|
-
name?: string;
|
|
1907
|
-
description?: string;
|
|
1908
|
-
host?: string | string[];
|
|
1909
|
-
port?: number;
|
|
1910
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1911
|
-
const context = getActionContext();
|
|
1912
|
-
try {
|
|
1913
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1914
|
-
interfaces.requests.IReq_UpdateNetworkTarget
|
|
1915
|
-
>('/typedrequest', 'updateNetworkTarget');
|
|
1916
|
-
await request.fire({
|
|
1917
|
-
identity: context.identity!,
|
|
1918
|
-
id: dataArg.id,
|
|
1919
|
-
name: dataArg.name,
|
|
1920
|
-
description: dataArg.description,
|
|
1921
|
-
host: dataArg.host,
|
|
1922
|
-
port: dataArg.port,
|
|
1923
|
-
});
|
|
1924
|
-
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1925
|
-
} catch (error: unknown) {
|
|
1926
|
-
return {
|
|
1927
|
-
...statePartArg.getState()!,
|
|
1928
|
-
error: error instanceof Error ? error.message : 'Failed to update target',
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
});
|
|
1932
|
-
|
|
1933
|
-
export const deleteTargetAction = profilesTargetsStatePart.createAction<{
|
|
1934
|
-
id: string;
|
|
1935
|
-
force?: boolean;
|
|
1936
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IProfilesTargetsState> => {
|
|
1937
|
-
const context = getActionContext();
|
|
1938
|
-
try {
|
|
1939
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1940
|
-
interfaces.requests.IReq_DeleteNetworkTarget
|
|
1941
|
-
>('/typedrequest', 'deleteNetworkTarget');
|
|
1942
|
-
const response = await request.fire({
|
|
1943
|
-
identity: context.identity!,
|
|
1944
|
-
id: dataArg.id,
|
|
1945
|
-
force: dataArg.force,
|
|
1946
|
-
});
|
|
1947
|
-
if (!response.success) {
|
|
1948
|
-
return {
|
|
1949
|
-
...statePartArg.getState()!,
|
|
1950
|
-
error: response.message || 'Failed to delete target',
|
|
1951
|
-
};
|
|
1952
|
-
}
|
|
1953
|
-
return await actionContext!.dispatch(fetchProfilesAndTargetsAction, null);
|
|
1954
|
-
} catch (error: unknown) {
|
|
1955
|
-
return {
|
|
1956
|
-
...statePartArg.getState()!,
|
|
1957
|
-
error: error instanceof Error ? error.message : 'Failed to delete target',
|
|
1958
|
-
};
|
|
1959
|
-
}
|
|
1960
|
-
});
|
|
1961
|
-
|
|
1962
|
-
// ============================================================================
|
|
1963
|
-
// Domains State (DNS providers + domains + records)
|
|
1964
|
-
// ============================================================================
|
|
1965
|
-
|
|
1966
|
-
export interface IDomainsState {
|
|
1967
|
-
providers: interfaces.data.IDnsProviderPublic[];
|
|
1968
|
-
domains: interfaces.data.IDomain[];
|
|
1969
|
-
records: interfaces.data.IDnsRecord[];
|
|
1970
|
-
/** id of the currently-selected domain in the DNS records subview. */
|
|
1971
|
-
selectedDomainId: string | null;
|
|
1972
|
-
isLoading: boolean;
|
|
1973
|
-
error: string | null;
|
|
1974
|
-
lastUpdated: number;
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
|
-
export const domainsStatePart = await appState.getStatePart<IDomainsState>(
|
|
1978
|
-
'domains',
|
|
1979
|
-
{
|
|
1980
|
-
providers: [],
|
|
1981
|
-
domains: [],
|
|
1982
|
-
records: [],
|
|
1983
|
-
selectedDomainId: null,
|
|
1984
|
-
isLoading: false,
|
|
1985
|
-
error: null,
|
|
1986
|
-
lastUpdated: 0,
|
|
1987
|
-
},
|
|
1988
|
-
'soft',
|
|
1989
|
-
);
|
|
1990
|
-
|
|
1991
|
-
export const fetchDomainsAndProvidersAction = domainsStatePart.createAction(
|
|
1992
|
-
async (statePartArg): Promise<IDomainsState> => {
|
|
1993
|
-
const context = getActionContext();
|
|
1994
|
-
const currentState = statePartArg.getState()!;
|
|
1995
|
-
if (!context.identity) return currentState;
|
|
1996
|
-
|
|
1997
|
-
try {
|
|
1998
|
-
const providersRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
1999
|
-
interfaces.requests.IReq_GetDnsProviders
|
|
2000
|
-
>('/typedrequest', 'getDnsProviders');
|
|
2001
|
-
const domainsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2002
|
-
interfaces.requests.IReq_GetDomains
|
|
2003
|
-
>('/typedrequest', 'getDomains');
|
|
2004
|
-
|
|
2005
|
-
const [providersResponse, domainsResponse] = await Promise.all([
|
|
2006
|
-
providersRequest.fire({ identity: context.identity }),
|
|
2007
|
-
domainsRequest.fire({ identity: context.identity }),
|
|
2008
|
-
]);
|
|
2009
|
-
|
|
2010
|
-
return {
|
|
2011
|
-
...currentState,
|
|
2012
|
-
providers: providersResponse.providers,
|
|
2013
|
-
domains: domainsResponse.domains,
|
|
2014
|
-
isLoading: false,
|
|
2015
|
-
error: null,
|
|
2016
|
-
lastUpdated: Date.now(),
|
|
2017
|
-
};
|
|
2018
|
-
} catch (error: unknown) {
|
|
2019
|
-
return {
|
|
2020
|
-
...currentState,
|
|
2021
|
-
isLoading: false,
|
|
2022
|
-
error: error instanceof Error ? error.message : 'Failed to fetch domains/providers',
|
|
2023
|
-
};
|
|
2024
|
-
}
|
|
2025
|
-
},
|
|
2026
|
-
);
|
|
2027
|
-
|
|
2028
|
-
export const fetchDnsRecordsForDomainAction = domainsStatePart.createAction<{ domainId: string }>(
|
|
2029
|
-
async (statePartArg, dataArg): Promise<IDomainsState> => {
|
|
2030
|
-
const context = getActionContext();
|
|
2031
|
-
const currentState = statePartArg.getState()!;
|
|
2032
|
-
if (!context.identity) return currentState;
|
|
2033
|
-
|
|
2034
|
-
try {
|
|
2035
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2036
|
-
interfaces.requests.IReq_GetDnsRecords
|
|
2037
|
-
>('/typedrequest', 'getDnsRecords');
|
|
2038
|
-
const response = await request.fire({
|
|
2039
|
-
identity: context.identity,
|
|
2040
|
-
domainId: dataArg.domainId,
|
|
2041
|
-
});
|
|
2042
|
-
return {
|
|
2043
|
-
...currentState,
|
|
2044
|
-
records: response.records,
|
|
2045
|
-
selectedDomainId: dataArg.domainId,
|
|
2046
|
-
error: null,
|
|
2047
|
-
};
|
|
2048
|
-
} catch (error: unknown) {
|
|
2049
|
-
return {
|
|
2050
|
-
...currentState,
|
|
2051
|
-
error: error instanceof Error ? error.message : 'Failed to fetch DNS records',
|
|
2052
|
-
};
|
|
2053
|
-
}
|
|
2054
|
-
},
|
|
2055
|
-
);
|
|
2056
|
-
|
|
2057
|
-
export const createDnsProviderAction = domainsStatePart.createAction<{
|
|
2058
|
-
name: string;
|
|
2059
|
-
type: interfaces.data.TDnsProviderType;
|
|
2060
|
-
credentials: interfaces.data.TDnsProviderCredentials;
|
|
2061
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2062
|
-
const context = getActionContext();
|
|
2063
|
-
try {
|
|
2064
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2065
|
-
interfaces.requests.IReq_CreateDnsProvider
|
|
2066
|
-
>('/typedrequest', 'createDnsProvider');
|
|
2067
|
-
const response = await request.fire({
|
|
2068
|
-
identity: context.identity!,
|
|
2069
|
-
name: dataArg.name,
|
|
2070
|
-
type: dataArg.type,
|
|
2071
|
-
credentials: dataArg.credentials,
|
|
2072
|
-
});
|
|
2073
|
-
if (!response.success) {
|
|
2074
|
-
return {
|
|
2075
|
-
...statePartArg.getState()!,
|
|
2076
|
-
error: response.message || 'Failed to create provider',
|
|
2077
|
-
};
|
|
2078
|
-
}
|
|
2079
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2080
|
-
} catch (error: unknown) {
|
|
2081
|
-
return {
|
|
2082
|
-
...statePartArg.getState()!,
|
|
2083
|
-
error: error instanceof Error ? error.message : 'Failed to create provider',
|
|
2084
|
-
};
|
|
2085
|
-
}
|
|
2086
|
-
});
|
|
2087
|
-
|
|
2088
|
-
export const updateDnsProviderAction = domainsStatePart.createAction<{
|
|
2089
|
-
id: string;
|
|
2090
|
-
name?: string;
|
|
2091
|
-
credentials?: interfaces.data.TDnsProviderCredentials;
|
|
2092
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2093
|
-
const context = getActionContext();
|
|
2094
|
-
try {
|
|
2095
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2096
|
-
interfaces.requests.IReq_UpdateDnsProvider
|
|
2097
|
-
>('/typedrequest', 'updateDnsProvider');
|
|
2098
|
-
const response = await request.fire({
|
|
2099
|
-
identity: context.identity!,
|
|
2100
|
-
id: dataArg.id,
|
|
2101
|
-
name: dataArg.name,
|
|
2102
|
-
credentials: dataArg.credentials,
|
|
2103
|
-
});
|
|
2104
|
-
if (!response.success) {
|
|
2105
|
-
return {
|
|
2106
|
-
...statePartArg.getState()!,
|
|
2107
|
-
error: response.message || 'Failed to update provider',
|
|
2108
|
-
};
|
|
2109
|
-
}
|
|
2110
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2111
|
-
} catch (error: unknown) {
|
|
2112
|
-
return {
|
|
2113
|
-
...statePartArg.getState()!,
|
|
2114
|
-
error: error instanceof Error ? error.message : 'Failed to update provider',
|
|
2115
|
-
};
|
|
2116
|
-
}
|
|
2117
|
-
});
|
|
2118
|
-
|
|
2119
|
-
export const deleteDnsProviderAction = domainsStatePart.createAction<{ id: string; force?: boolean }>(
|
|
2120
|
-
async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2121
|
-
const context = getActionContext();
|
|
2122
|
-
try {
|
|
2123
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2124
|
-
interfaces.requests.IReq_DeleteDnsProvider
|
|
2125
|
-
>('/typedrequest', 'deleteDnsProvider');
|
|
2126
|
-
const response = await request.fire({
|
|
2127
|
-
identity: context.identity!,
|
|
2128
|
-
id: dataArg.id,
|
|
2129
|
-
force: dataArg.force,
|
|
2130
|
-
});
|
|
2131
|
-
if (!response.success) {
|
|
2132
|
-
return {
|
|
2133
|
-
...statePartArg.getState()!,
|
|
2134
|
-
error: response.message || 'Failed to delete provider',
|
|
2135
|
-
};
|
|
2136
|
-
}
|
|
2137
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2138
|
-
} catch (error: unknown) {
|
|
2139
|
-
return {
|
|
2140
|
-
...statePartArg.getState()!,
|
|
2141
|
-
error: error instanceof Error ? error.message : 'Failed to delete provider',
|
|
2142
|
-
};
|
|
2143
|
-
}
|
|
2144
|
-
},
|
|
2145
|
-
);
|
|
2146
|
-
|
|
2147
|
-
export const testDnsProviderAction = domainsStatePart.createAction<{ id: string }>(
|
|
2148
|
-
async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2149
|
-
const context = getActionContext();
|
|
2150
|
-
try {
|
|
2151
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2152
|
-
interfaces.requests.IReq_TestDnsProvider
|
|
2153
|
-
>('/typedrequest', 'testDnsProvider');
|
|
2154
|
-
await request.fire({ identity: context.identity!, id: dataArg.id });
|
|
2155
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2156
|
-
} catch (error: unknown) {
|
|
2157
|
-
return {
|
|
2158
|
-
...statePartArg.getState()!,
|
|
2159
|
-
error: error instanceof Error ? error.message : 'Failed to test provider',
|
|
2160
|
-
};
|
|
2161
|
-
}
|
|
2162
|
-
},
|
|
2163
|
-
);
|
|
2164
|
-
|
|
2165
|
-
/** One-shot fetch for the import-domain modal. Does NOT modify state. */
|
|
2166
|
-
export async function fetchProviderDomains(
|
|
2167
|
-
providerId: string,
|
|
2168
|
-
): Promise<{ success: boolean; domains?: interfaces.data.IProviderDomainListing[]; message?: string }> {
|
|
2169
|
-
const context = getActionContext();
|
|
2170
|
-
if (!context.identity) return { success: false, message: 'Not authenticated' };
|
|
2171
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2172
|
-
interfaces.requests.IReq_ListProviderDomains
|
|
2173
|
-
>('/typedrequest', 'listProviderDomains');
|
|
2174
|
-
return await request.fire({ identity: context.identity, providerId });
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
export const createDcrouterDomainAction = domainsStatePart.createAction<{
|
|
2178
|
-
name: string;
|
|
2179
|
-
description?: string;
|
|
2180
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2181
|
-
const context = getActionContext();
|
|
2182
|
-
try {
|
|
2183
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2184
|
-
interfaces.requests.IReq_CreateDomain
|
|
2185
|
-
>('/typedrequest', 'createDomain');
|
|
2186
|
-
const response = await request.fire({
|
|
2187
|
-
identity: context.identity!,
|
|
2188
|
-
name: dataArg.name,
|
|
2189
|
-
description: dataArg.description,
|
|
2190
|
-
});
|
|
2191
|
-
if (!response.success) {
|
|
2192
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to create domain' };
|
|
2193
|
-
}
|
|
2194
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2195
|
-
} catch (error: unknown) {
|
|
2196
|
-
return {
|
|
2197
|
-
...statePartArg.getState()!,
|
|
2198
|
-
error: error instanceof Error ? error.message : 'Failed to create domain',
|
|
2199
|
-
};
|
|
2200
|
-
}
|
|
2201
|
-
});
|
|
2202
|
-
|
|
2203
|
-
export const importDomainsFromProviderAction = domainsStatePart.createAction<{
|
|
2204
|
-
providerId: string;
|
|
2205
|
-
domainNames: string[];
|
|
2206
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2207
|
-
const context = getActionContext();
|
|
2208
|
-
try {
|
|
2209
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2210
|
-
interfaces.requests.IReq_ImportDomain
|
|
2211
|
-
>('/typedrequest', 'importDomain');
|
|
2212
|
-
const response = await request.fire({
|
|
2213
|
-
identity: context.identity!,
|
|
2214
|
-
providerId: dataArg.providerId,
|
|
2215
|
-
domainNames: dataArg.domainNames,
|
|
2216
|
-
});
|
|
2217
|
-
if (!response.success) {
|
|
2218
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to import domains' };
|
|
2219
|
-
}
|
|
2220
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2221
|
-
} catch (error: unknown) {
|
|
2222
|
-
return {
|
|
2223
|
-
...statePartArg.getState()!,
|
|
2224
|
-
error: error instanceof Error ? error.message : 'Failed to import domains',
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
|
-
});
|
|
2228
|
-
|
|
2229
|
-
export const deleteDomainAction = domainsStatePart.createAction<{ id: string }>(
|
|
2230
|
-
async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2231
|
-
const context = getActionContext();
|
|
2232
|
-
try {
|
|
2233
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2234
|
-
interfaces.requests.IReq_DeleteDomain
|
|
2235
|
-
>('/typedrequest', 'deleteDomain');
|
|
2236
|
-
const response = await request.fire({ identity: context.identity!, id: dataArg.id });
|
|
2237
|
-
if (!response.success) {
|
|
2238
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to delete domain' };
|
|
2239
|
-
}
|
|
2240
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2241
|
-
} catch (error: unknown) {
|
|
2242
|
-
return {
|
|
2243
|
-
...statePartArg.getState()!,
|
|
2244
|
-
error: error instanceof Error ? error.message : 'Failed to delete domain',
|
|
2245
|
-
};
|
|
2246
|
-
}
|
|
2247
|
-
},
|
|
2248
|
-
);
|
|
2249
|
-
|
|
2250
|
-
export const syncDomainAction = domainsStatePart.createAction<{ id: string }>(
|
|
2251
|
-
async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2252
|
-
const context = getActionContext();
|
|
2253
|
-
try {
|
|
2254
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2255
|
-
interfaces.requests.IReq_SyncDomain
|
|
2256
|
-
>('/typedrequest', 'syncDomain');
|
|
2257
|
-
const response = await request.fire({ identity: context.identity!, id: dataArg.id });
|
|
2258
|
-
if (!response.success) {
|
|
2259
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to sync domain' };
|
|
2260
|
-
}
|
|
2261
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2262
|
-
} catch (error: unknown) {
|
|
2263
|
-
return {
|
|
2264
|
-
...statePartArg.getState()!,
|
|
2265
|
-
error: error instanceof Error ? error.message : 'Failed to sync domain',
|
|
2266
|
-
};
|
|
2267
|
-
}
|
|
2268
|
-
},
|
|
2269
|
-
);
|
|
2270
|
-
|
|
2271
|
-
export const migrateDomainAction = domainsStatePart.createAction<{
|
|
2272
|
-
id: string;
|
|
2273
|
-
targetSource: interfaces.data.TDomainSource;
|
|
2274
|
-
targetProviderId?: string;
|
|
2275
|
-
deleteExistingProviderRecords?: boolean;
|
|
2276
|
-
}>(
|
|
2277
|
-
async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2278
|
-
const context = getActionContext();
|
|
2279
|
-
try {
|
|
2280
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2281
|
-
interfaces.requests.IReq_MigrateDomain
|
|
2282
|
-
>('/typedrequest', 'migrateDomain');
|
|
2283
|
-
const response = await request.fire({ identity: context.identity!, ...dataArg });
|
|
2284
|
-
if (!response.success) {
|
|
2285
|
-
return { ...statePartArg.getState()!, error: response.message || 'Migration failed' };
|
|
2286
|
-
}
|
|
2287
|
-
return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
|
|
2288
|
-
} catch (error: unknown) {
|
|
2289
|
-
return {
|
|
2290
|
-
...statePartArg.getState()!,
|
|
2291
|
-
error: error instanceof Error ? error.message : 'Migration failed',
|
|
2292
|
-
};
|
|
2293
|
-
}
|
|
2294
|
-
},
|
|
2295
|
-
);
|
|
2296
|
-
|
|
2297
|
-
export const createDnsRecordAction = domainsStatePart.createAction<{
|
|
2298
|
-
domainId: string;
|
|
2299
|
-
name: string;
|
|
2300
|
-
type: interfaces.data.TDnsRecordType;
|
|
2301
|
-
value: string;
|
|
2302
|
-
ttl?: number;
|
|
2303
|
-
proxied?: boolean;
|
|
2304
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2305
|
-
const context = getActionContext();
|
|
2306
|
-
try {
|
|
2307
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2308
|
-
interfaces.requests.IReq_CreateDnsRecord
|
|
2309
|
-
>('/typedrequest', 'createDnsRecord');
|
|
2310
|
-
const response = await request.fire({
|
|
2311
|
-
identity: context.identity!,
|
|
2312
|
-
domainId: dataArg.domainId,
|
|
2313
|
-
name: dataArg.name,
|
|
2314
|
-
type: dataArg.type,
|
|
2315
|
-
value: dataArg.value,
|
|
2316
|
-
ttl: dataArg.ttl,
|
|
2317
|
-
proxied: dataArg.proxied,
|
|
2318
|
-
});
|
|
2319
|
-
if (!response.success) {
|
|
2320
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to create record' };
|
|
2321
|
-
}
|
|
2322
|
-
return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
|
|
2323
|
-
} catch (error: unknown) {
|
|
2324
|
-
return {
|
|
2325
|
-
...statePartArg.getState()!,
|
|
2326
|
-
error: error instanceof Error ? error.message : 'Failed to create record',
|
|
2327
|
-
};
|
|
2328
|
-
}
|
|
2329
|
-
});
|
|
2330
|
-
|
|
2331
|
-
export const updateDnsRecordAction = domainsStatePart.createAction<{
|
|
2332
|
-
id: string;
|
|
2333
|
-
domainId: string;
|
|
2334
|
-
name?: string;
|
|
2335
|
-
value?: string;
|
|
2336
|
-
ttl?: number;
|
|
2337
|
-
proxied?: boolean;
|
|
2338
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2339
|
-
const context = getActionContext();
|
|
2340
|
-
try {
|
|
2341
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2342
|
-
interfaces.requests.IReq_UpdateDnsRecord
|
|
2343
|
-
>('/typedrequest', 'updateDnsRecord');
|
|
2344
|
-
const response = await request.fire({
|
|
2345
|
-
identity: context.identity!,
|
|
2346
|
-
id: dataArg.id,
|
|
2347
|
-
name: dataArg.name,
|
|
2348
|
-
value: dataArg.value,
|
|
2349
|
-
ttl: dataArg.ttl,
|
|
2350
|
-
proxied: dataArg.proxied,
|
|
2351
|
-
});
|
|
2352
|
-
if (!response.success) {
|
|
2353
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to update record' };
|
|
2354
|
-
}
|
|
2355
|
-
return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
|
|
2356
|
-
} catch (error: unknown) {
|
|
2357
|
-
return {
|
|
2358
|
-
...statePartArg.getState()!,
|
|
2359
|
-
error: error instanceof Error ? error.message : 'Failed to update record',
|
|
2360
|
-
};
|
|
2361
|
-
}
|
|
2362
|
-
});
|
|
2363
|
-
|
|
2364
|
-
export const deleteDnsRecordAction = domainsStatePart.createAction<{ id: string; domainId: string }>(
|
|
2365
|
-
async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
|
2366
|
-
const context = getActionContext();
|
|
2367
|
-
try {
|
|
2368
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2369
|
-
interfaces.requests.IReq_DeleteDnsRecord
|
|
2370
|
-
>('/typedrequest', 'deleteDnsRecord');
|
|
2371
|
-
const response = await request.fire({ identity: context.identity!, id: dataArg.id });
|
|
2372
|
-
if (!response.success) {
|
|
2373
|
-
return { ...statePartArg.getState()!, error: response.message || 'Failed to delete record' };
|
|
2374
|
-
}
|
|
2375
|
-
return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
|
|
2376
|
-
} catch (error: unknown) {
|
|
2377
|
-
return {
|
|
2378
|
-
...statePartArg.getState()!,
|
|
2379
|
-
error: error instanceof Error ? error.message : 'Failed to delete record',
|
|
2380
|
-
};
|
|
2381
|
-
}
|
|
2382
|
-
},
|
|
2383
|
-
);
|
|
2384
|
-
|
|
2385
|
-
// ============================================================================
|
|
2386
|
-
// ACME Config Actions
|
|
2387
|
-
// ============================================================================
|
|
2388
|
-
|
|
2389
|
-
export const fetchAcmeConfigAction = acmeConfigStatePart.createAction(
|
|
2390
|
-
async (statePartArg): Promise<IAcmeConfigState> => {
|
|
2391
|
-
const context = getActionContext();
|
|
2392
|
-
const currentState = statePartArg.getState()!;
|
|
2393
|
-
if (!context.identity) return currentState;
|
|
2394
|
-
|
|
2395
|
-
try {
|
|
2396
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2397
|
-
interfaces.requests.IReq_GetAcmeConfig
|
|
2398
|
-
>('/typedrequest', 'getAcmeConfig');
|
|
2399
|
-
const response = await request.fire({ identity: context.identity });
|
|
2400
|
-
return {
|
|
2401
|
-
config: response.config,
|
|
2402
|
-
isLoading: false,
|
|
2403
|
-
error: null,
|
|
2404
|
-
lastUpdated: Date.now(),
|
|
2405
|
-
};
|
|
2406
|
-
} catch (error: unknown) {
|
|
2407
|
-
return {
|
|
2408
|
-
...currentState,
|
|
2409
|
-
isLoading: false,
|
|
2410
|
-
error: error instanceof Error ? error.message : 'Failed to fetch ACME config',
|
|
2411
|
-
};
|
|
2412
|
-
}
|
|
2413
|
-
},
|
|
2414
|
-
);
|
|
2415
|
-
|
|
2416
|
-
export const updateAcmeConfigAction = acmeConfigStatePart.createAction<{
|
|
2417
|
-
accountEmail?: string;
|
|
2418
|
-
enabled?: boolean;
|
|
2419
|
-
useProduction?: boolean;
|
|
2420
|
-
autoRenew?: boolean;
|
|
2421
|
-
renewThresholdDays?: number;
|
|
2422
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IAcmeConfigState> => {
|
|
2423
|
-
const context = getActionContext();
|
|
2424
|
-
try {
|
|
2425
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2426
|
-
interfaces.requests.IReq_UpdateAcmeConfig
|
|
2427
|
-
>('/typedrequest', 'updateAcmeConfig');
|
|
2428
|
-
const response = await request.fire({
|
|
2429
|
-
identity: context.identity!,
|
|
2430
|
-
accountEmail: dataArg.accountEmail,
|
|
2431
|
-
enabled: dataArg.enabled,
|
|
2432
|
-
useProduction: dataArg.useProduction,
|
|
2433
|
-
autoRenew: dataArg.autoRenew,
|
|
2434
|
-
renewThresholdDays: dataArg.renewThresholdDays,
|
|
2435
|
-
});
|
|
2436
|
-
if (!response.success) {
|
|
2437
|
-
return {
|
|
2438
|
-
...statePartArg.getState()!,
|
|
2439
|
-
error: response.message || 'Failed to update ACME config',
|
|
2440
|
-
};
|
|
2441
|
-
}
|
|
2442
|
-
return await actionContext!.dispatch(fetchAcmeConfigAction, null);
|
|
2443
|
-
} catch (error: unknown) {
|
|
2444
|
-
return {
|
|
2445
|
-
...statePartArg.getState()!,
|
|
2446
|
-
error: error instanceof Error ? error.message : 'Failed to update ACME config',
|
|
2447
|
-
};
|
|
2448
|
-
}
|
|
2449
|
-
});
|
|
2450
|
-
|
|
2451
|
-
// ============================================================================
|
|
2452
|
-
// Route Management Actions
|
|
2453
|
-
// ============================================================================
|
|
2454
|
-
|
|
2455
|
-
export const fetchMergedRoutesAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
|
|
2456
|
-
const context = getActionContext();
|
|
2457
|
-
const currentState = statePartArg.getState()!;
|
|
2458
|
-
if (!context.identity) return currentState;
|
|
2459
|
-
|
|
2460
|
-
try {
|
|
2461
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2462
|
-
interfaces.requests.IReq_GetMergedRoutes
|
|
2463
|
-
>('/typedrequest', 'getMergedRoutes');
|
|
2464
|
-
|
|
2465
|
-
const response = await request.fire({
|
|
2466
|
-
identity: context.identity,
|
|
2467
|
-
});
|
|
2468
|
-
|
|
2469
|
-
return {
|
|
2470
|
-
...currentState,
|
|
2471
|
-
mergedRoutes: response.routes,
|
|
2472
|
-
warnings: response.warnings,
|
|
2473
|
-
isLoading: false,
|
|
2474
|
-
error: null,
|
|
2475
|
-
lastUpdated: Date.now(),
|
|
2476
|
-
};
|
|
2477
|
-
} catch (error) {
|
|
2478
|
-
return {
|
|
2479
|
-
...currentState,
|
|
2480
|
-
isLoading: false,
|
|
2481
|
-
error: error instanceof Error ? error.message : 'Failed to fetch routes',
|
|
2482
|
-
};
|
|
2483
|
-
}
|
|
2484
|
-
});
|
|
2485
|
-
|
|
2486
|
-
export const fetchHttpRedirectsAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
|
|
2487
|
-
const context = getActionContext();
|
|
2488
|
-
const currentState = statePartArg.getState()!;
|
|
2489
|
-
if (!context.identity) return currentState;
|
|
2490
|
-
|
|
2491
|
-
try {
|
|
2492
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2493
|
-
interfaces.requests.IReq_GetHttpRedirects
|
|
2494
|
-
>('/typedrequest', 'getHttpRedirects');
|
|
2495
|
-
|
|
2496
|
-
const response = await request.fire({
|
|
2497
|
-
identity: context.identity,
|
|
2498
|
-
});
|
|
2499
|
-
|
|
2500
|
-
return {
|
|
2501
|
-
...currentState,
|
|
2502
|
-
httpRedirects: response.redirects,
|
|
2503
|
-
isLoading: false,
|
|
2504
|
-
error: null,
|
|
2505
|
-
lastUpdated: Date.now(),
|
|
2506
|
-
};
|
|
2507
|
-
} catch (error) {
|
|
2508
|
-
return {
|
|
2509
|
-
...currentState,
|
|
2510
|
-
isLoading: false,
|
|
2511
|
-
error: error instanceof Error ? error.message : 'Failed to fetch HTTP redirects',
|
|
2512
|
-
};
|
|
2513
|
-
}
|
|
2514
|
-
});
|
|
2515
|
-
|
|
2516
|
-
export const createRouteAction = routeManagementStatePart.createAction<{
|
|
2517
|
-
route: any;
|
|
2518
|
-
enabled?: boolean;
|
|
2519
|
-
metadata?: any;
|
|
2520
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
|
|
2521
|
-
const context = getActionContext();
|
|
2522
|
-
const currentState = statePartArg.getState()!;
|
|
2523
|
-
|
|
2524
|
-
try {
|
|
2525
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2526
|
-
interfaces.requests.IReq_CreateRoute
|
|
2527
|
-
>('/typedrequest', 'createRoute');
|
|
2528
|
-
|
|
2529
|
-
await request.fire({
|
|
2530
|
-
identity: context.identity!,
|
|
2531
|
-
route: dataArg.route,
|
|
2532
|
-
enabled: dataArg.enabled,
|
|
2533
|
-
metadata: dataArg.metadata,
|
|
2534
|
-
});
|
|
2535
|
-
|
|
2536
|
-
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
|
|
2537
|
-
} catch (error: unknown) {
|
|
2538
|
-
return {
|
|
2539
|
-
...currentState,
|
|
2540
|
-
error: error instanceof Error ? error.message : 'Failed to create route',
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
});
|
|
2544
|
-
|
|
2545
|
-
export const updateRouteAction = routeManagementStatePart.createAction<{
|
|
2546
|
-
id: string;
|
|
2547
|
-
route?: any;
|
|
2548
|
-
enabled?: boolean;
|
|
2549
|
-
metadata?: any;
|
|
2550
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
|
|
2551
|
-
const context = getActionContext();
|
|
2552
|
-
const currentState = statePartArg.getState()!;
|
|
2553
|
-
|
|
2554
|
-
try {
|
|
2555
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2556
|
-
interfaces.requests.IReq_UpdateRoute
|
|
2557
|
-
>('/typedrequest', 'updateRoute');
|
|
2558
|
-
|
|
2559
|
-
const response = await request.fire({
|
|
2560
|
-
identity: context.identity!,
|
|
2561
|
-
id: dataArg.id,
|
|
2562
|
-
route: dataArg.route,
|
|
2563
|
-
enabled: dataArg.enabled,
|
|
2564
|
-
metadata: dataArg.metadata,
|
|
2565
|
-
});
|
|
2566
|
-
|
|
2567
|
-
if (!response.success) {
|
|
2568
|
-
throw new Error(response.message || 'Failed to update route');
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
|
|
2572
|
-
} catch (error: unknown) {
|
|
2573
|
-
return {
|
|
2574
|
-
...currentState,
|
|
2575
|
-
error: error instanceof Error ? error.message : 'Failed to update route',
|
|
2576
|
-
};
|
|
2577
|
-
}
|
|
2578
|
-
});
|
|
2579
|
-
|
|
2580
|
-
export const deleteRouteAction = routeManagementStatePart.createAction<string>(
|
|
2581
|
-
async (statePartArg, routeId, actionContext): Promise<IRouteManagementState> => {
|
|
2582
|
-
const context = getActionContext();
|
|
2583
|
-
const currentState = statePartArg.getState()!;
|
|
2584
|
-
|
|
2585
|
-
try {
|
|
2586
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2587
|
-
interfaces.requests.IReq_DeleteRoute
|
|
2588
|
-
>('/typedrequest', 'deleteRoute');
|
|
2589
|
-
|
|
2590
|
-
const response = await request.fire({
|
|
2591
|
-
identity: context.identity!,
|
|
2592
|
-
id: routeId,
|
|
2593
|
-
});
|
|
2594
|
-
|
|
2595
|
-
if (!response.success) {
|
|
2596
|
-
throw new Error(response.message || 'Failed to delete route');
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
|
|
2600
|
-
} catch (error: unknown) {
|
|
2601
|
-
return {
|
|
2602
|
-
...currentState,
|
|
2603
|
-
error: error instanceof Error ? error.message : 'Failed to delete route',
|
|
2604
|
-
};
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
);
|
|
2608
|
-
|
|
2609
|
-
export const toggleRouteAction = routeManagementStatePart.createAction<{
|
|
2610
|
-
id: string;
|
|
2611
|
-
enabled: boolean;
|
|
2612
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
|
|
2613
|
-
const context = getActionContext();
|
|
2614
|
-
const currentState = statePartArg.getState()!;
|
|
2615
|
-
|
|
2616
|
-
try {
|
|
2617
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2618
|
-
interfaces.requests.IReq_ToggleRoute
|
|
2619
|
-
>('/typedrequest', 'toggleRoute');
|
|
2620
|
-
|
|
2621
|
-
const response = await request.fire({
|
|
2622
|
-
identity: context.identity!,
|
|
2623
|
-
id: dataArg.id,
|
|
2624
|
-
enabled: dataArg.enabled,
|
|
2625
|
-
});
|
|
2626
|
-
|
|
2627
|
-
if (!response.success) {
|
|
2628
|
-
throw new Error(response.message || 'Failed to toggle route');
|
|
2629
|
-
}
|
|
2630
|
-
|
|
2631
|
-
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
|
|
2632
|
-
} catch (error: unknown) {
|
|
2633
|
-
return {
|
|
2634
|
-
...currentState,
|
|
2635
|
-
error: error instanceof Error ? error.message : 'Failed to toggle route',
|
|
2636
|
-
};
|
|
2637
|
-
}
|
|
2638
|
-
});
|
|
2639
|
-
|
|
2640
|
-
// ============================================================================
|
|
2641
|
-
// API Token Actions
|
|
2642
|
-
// ============================================================================
|
|
2643
|
-
|
|
2644
|
-
export const fetchApiTokensAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
|
|
2645
|
-
const context = getActionContext();
|
|
2646
|
-
const currentState = statePartArg.getState()!;
|
|
2647
|
-
if (!context.identity) return currentState;
|
|
2648
|
-
|
|
2649
|
-
try {
|
|
2650
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2651
|
-
interfaces.requests.IReq_ListApiTokens
|
|
2652
|
-
>('/typedrequest', 'listApiTokens');
|
|
2653
|
-
|
|
2654
|
-
const response = await request.fire({
|
|
2655
|
-
identity: context.identity,
|
|
2656
|
-
});
|
|
2657
|
-
|
|
2658
|
-
return {
|
|
2659
|
-
...currentState,
|
|
2660
|
-
apiTokens: response.tokens,
|
|
2661
|
-
};
|
|
2662
|
-
} catch (error) {
|
|
2663
|
-
return {
|
|
2664
|
-
...currentState,
|
|
2665
|
-
error: error instanceof Error ? error.message : 'Failed to fetch tokens',
|
|
2666
|
-
};
|
|
2667
|
-
}
|
|
2668
|
-
});
|
|
2669
|
-
|
|
2670
|
-
export const fetchGatewayClientsAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
|
|
2671
|
-
const context = getActionContext();
|
|
2672
|
-
const currentState = statePartArg.getState()!;
|
|
2673
|
-
if (!context.identity) return currentState;
|
|
2674
|
-
|
|
2675
|
-
try {
|
|
2676
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2677
|
-
interfaces.requests.IReq_ListGatewayClients
|
|
2678
|
-
>('/typedrequest', 'listGatewayClients');
|
|
2679
|
-
const response = await request.fire({ identity: context.identity });
|
|
2680
|
-
return {
|
|
2681
|
-
...currentState,
|
|
2682
|
-
gatewayClients: response.gatewayClients,
|
|
2683
|
-
error: null,
|
|
2684
|
-
lastUpdated: Date.now(),
|
|
2685
|
-
};
|
|
2686
|
-
} catch (error) {
|
|
2687
|
-
return {
|
|
2688
|
-
...currentState,
|
|
2689
|
-
error: error instanceof Error ? error.message : 'Failed to fetch gateway clients',
|
|
2690
|
-
};
|
|
2691
|
-
}
|
|
2692
|
-
});
|
|
2693
|
-
|
|
2694
|
-
export async function createGatewayClient(data: {
|
|
2695
|
-
id?: string;
|
|
2696
|
-
type: interfaces.data.IGatewayClient['type'];
|
|
2697
|
-
name: string;
|
|
2698
|
-
description?: string;
|
|
2699
|
-
hostnamePatterns?: string[];
|
|
2700
|
-
allowedRouteTargets?: interfaces.data.IGatewayClient['allowedRouteTargets'];
|
|
2701
|
-
}) {
|
|
2702
|
-
const context = getActionContext();
|
|
2703
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2704
|
-
interfaces.requests.IReq_CreateGatewayClient
|
|
2705
|
-
>('/typedrequest', 'createGatewayClient');
|
|
2706
|
-
return request.fire({
|
|
2707
|
-
identity: context.identity!,
|
|
2708
|
-
capabilities: {
|
|
2709
|
-
readDomains: true,
|
|
2710
|
-
readDnsRecords: true,
|
|
2711
|
-
syncRoutes: true,
|
|
2712
|
-
syncDnsRecords: false,
|
|
2713
|
-
requestCertificates: false,
|
|
2714
|
-
},
|
|
2715
|
-
...data,
|
|
2716
|
-
});
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
export const updateGatewayClientAction = routeManagementStatePart.createAction<{
|
|
2720
|
-
id: string;
|
|
2721
|
-
name?: string;
|
|
2722
|
-
description?: string;
|
|
2723
|
-
hostnamePatterns?: string[];
|
|
2724
|
-
allowedRouteTargets?: interfaces.data.IGatewayClient['allowedRouteTargets'];
|
|
2725
|
-
enabled?: boolean;
|
|
2726
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
|
|
2727
|
-
const context = getActionContext();
|
|
2728
|
-
const currentState = statePartArg.getState()!;
|
|
2729
|
-
try {
|
|
2730
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2731
|
-
interfaces.requests.IReq_UpdateGatewayClient
|
|
2732
|
-
>('/typedrequest', 'updateGatewayClient');
|
|
2733
|
-
await request.fire({ identity: context.identity!, ...dataArg });
|
|
2734
|
-
return await actionContext!.dispatch(fetchGatewayClientsAction, null);
|
|
2735
|
-
} catch (error) {
|
|
2736
|
-
return {
|
|
2737
|
-
...currentState,
|
|
2738
|
-
error: error instanceof Error ? error.message : 'Failed to update gateway client',
|
|
2739
|
-
};
|
|
2740
|
-
}
|
|
2741
|
-
});
|
|
2742
|
-
|
|
2743
|
-
export const deleteGatewayClientAction = routeManagementStatePart.createAction<string>(
|
|
2744
|
-
async (statePartArg, gatewayClientId, actionContext): Promise<IRouteManagementState> => {
|
|
2745
|
-
const context = getActionContext();
|
|
2746
|
-
const currentState = statePartArg.getState()!;
|
|
2747
|
-
try {
|
|
2748
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2749
|
-
interfaces.requests.IReq_DeleteGatewayClient
|
|
2750
|
-
>('/typedrequest', 'deleteGatewayClient');
|
|
2751
|
-
await request.fire({ identity: context.identity!, id: gatewayClientId });
|
|
2752
|
-
return await actionContext!.dispatch(fetchGatewayClientsAction, null);
|
|
2753
|
-
} catch (error) {
|
|
2754
|
-
return {
|
|
2755
|
-
...currentState,
|
|
2756
|
-
error: error instanceof Error ? error.message : 'Failed to delete gateway client',
|
|
2757
|
-
};
|
|
2758
|
-
}
|
|
2759
|
-
},
|
|
2760
|
-
);
|
|
2761
|
-
|
|
2762
|
-
export async function createGatewayClientToken(
|
|
2763
|
-
gatewayClientId: string,
|
|
2764
|
-
name?: string,
|
|
2765
|
-
expiresInDays?: number | null,
|
|
2766
|
-
) {
|
|
2767
|
-
const context = getActionContext();
|
|
2768
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2769
|
-
interfaces.requests.IReq_CreateGatewayClientToken
|
|
2770
|
-
>('/typedrequest', 'createGatewayClientToken');
|
|
2771
|
-
return request.fire({
|
|
2772
|
-
identity: context.identity!,
|
|
2773
|
-
gatewayClientId,
|
|
2774
|
-
name,
|
|
2775
|
-
expiresInDays,
|
|
2776
|
-
});
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
// Users
|
|
2780
|
-
export const fetchUsersAction = usersStatePart.createAction(async (statePartArg): Promise<IUsersState> => {
|
|
2781
|
-
const context = getActionContext();
|
|
2782
|
-
const currentState = statePartArg.getState()!;
|
|
2783
|
-
if (!context.identity) return currentState;
|
|
2784
|
-
|
|
2785
|
-
try {
|
|
2786
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2787
|
-
interfaces.requests.IReq_ListUsers
|
|
2788
|
-
>('/typedrequest', 'listUsers');
|
|
2789
|
-
|
|
2790
|
-
const response = await request.fire({
|
|
2791
|
-
identity: context.identity,
|
|
2792
|
-
});
|
|
2793
|
-
|
|
2794
|
-
return {
|
|
2795
|
-
...currentState,
|
|
2796
|
-
users: response.users,
|
|
2797
|
-
error: null,
|
|
2798
|
-
lastUpdated: Date.now(),
|
|
2799
|
-
};
|
|
2800
|
-
} catch (error) {
|
|
2801
|
-
return {
|
|
2802
|
-
...currentState,
|
|
2803
|
-
error: error instanceof Error ? error.message : 'Failed to fetch users',
|
|
2804
|
-
};
|
|
2805
|
-
}
|
|
2806
|
-
});
|
|
2807
|
-
|
|
2808
|
-
export const createUserAction = usersStatePart.createAction<{
|
|
2809
|
-
email: string;
|
|
2810
|
-
name?: string;
|
|
2811
|
-
role: interfaces.requests.TUserManagementRole;
|
|
2812
|
-
password: string;
|
|
2813
|
-
enableIdpGlobalAuth?: boolean;
|
|
2814
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IUsersState> => {
|
|
2815
|
-
const context = getActionContext();
|
|
2816
|
-
const currentState = statePartArg.getState()!;
|
|
2817
|
-
if (!context.identity) return currentState;
|
|
2818
|
-
|
|
2819
|
-
try {
|
|
2820
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2821
|
-
interfaces.requests.IReq_CreateUser
|
|
2822
|
-
>('/typedrequest', 'createUser');
|
|
2823
|
-
|
|
2824
|
-
const response = await request.fire({
|
|
2825
|
-
identity: context.identity,
|
|
2826
|
-
email: dataArg.email,
|
|
2827
|
-
name: dataArg.name,
|
|
2828
|
-
role: dataArg.role,
|
|
2829
|
-
password: dataArg.password,
|
|
2830
|
-
enableIdpGlobalAuth: dataArg.enableIdpGlobalAuth,
|
|
2831
|
-
});
|
|
2832
|
-
|
|
2833
|
-
if (!response.success) {
|
|
2834
|
-
throw new Error(response.message || 'Failed to create user');
|
|
2835
|
-
}
|
|
2836
|
-
|
|
2837
|
-
return await actionContext!.dispatch(fetchUsersAction, null);
|
|
2838
|
-
} catch (error) {
|
|
2839
|
-
return {
|
|
2840
|
-
...currentState,
|
|
2841
|
-
error: error instanceof Error ? error.message : 'Failed to create user',
|
|
2842
|
-
};
|
|
2843
|
-
}
|
|
2844
|
-
});
|
|
2845
|
-
|
|
2846
|
-
export const deleteUserAction = usersStatePart.createAction<string>(
|
|
2847
|
-
async (statePartArg, userIdArg, actionContext): Promise<IUsersState> => {
|
|
2848
|
-
const context = getActionContext();
|
|
2849
|
-
const currentState = statePartArg.getState()!;
|
|
2850
|
-
if (!context.identity) return currentState;
|
|
2851
|
-
|
|
2852
|
-
try {
|
|
2853
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2854
|
-
interfaces.requests.IReq_DeleteUser
|
|
2855
|
-
>('/typedrequest', 'deleteUser');
|
|
2856
|
-
|
|
2857
|
-
const response = await request.fire({
|
|
2858
|
-
identity: context.identity,
|
|
2859
|
-
id: userIdArg,
|
|
2860
|
-
});
|
|
2861
|
-
|
|
2862
|
-
if (!response.success) {
|
|
2863
|
-
throw new Error(response.message || 'Failed to delete user');
|
|
2864
|
-
}
|
|
2865
|
-
|
|
2866
|
-
return await actionContext!.dispatch(fetchUsersAction, null);
|
|
2867
|
-
} catch (error) {
|
|
2868
|
-
return {
|
|
2869
|
-
...currentState,
|
|
2870
|
-
error: error instanceof Error ? error.message : 'Failed to delete user',
|
|
2871
|
-
};
|
|
2872
|
-
}
|
|
2873
|
-
},
|
|
2874
|
-
);
|
|
2875
|
-
|
|
2876
|
-
export async function createApiToken(
|
|
2877
|
-
name: string,
|
|
2878
|
-
scopes: interfaces.data.TApiTokenScope[],
|
|
2879
|
-
expiresInDays?: number | null,
|
|
2880
|
-
policy?: any,
|
|
2881
|
-
) {
|
|
2882
|
-
const context = getActionContext();
|
|
2883
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2884
|
-
interfaces.requests.IReq_CreateApiToken
|
|
2885
|
-
>('/typedrequest', 'createApiToken');
|
|
2886
|
-
|
|
2887
|
-
return request.fire({
|
|
2888
|
-
identity: context.identity!,
|
|
2889
|
-
name,
|
|
2890
|
-
scopes,
|
|
2891
|
-
policy,
|
|
2892
|
-
expiresInDays,
|
|
2893
|
-
});
|
|
2894
|
-
}
|
|
2895
|
-
|
|
2896
|
-
export async function rollApiToken(id: string) {
|
|
2897
|
-
const context = getActionContext();
|
|
2898
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2899
|
-
interfaces.requests.IReq_RollApiToken
|
|
2900
|
-
>('/typedrequest', 'rollApiToken');
|
|
2901
|
-
|
|
2902
|
-
return request.fire({
|
|
2903
|
-
identity: context.identity!,
|
|
2904
|
-
id,
|
|
2905
|
-
});
|
|
2906
|
-
}
|
|
2907
|
-
|
|
2908
|
-
export const revokeApiTokenAction = routeManagementStatePart.createAction<string>(
|
|
2909
|
-
async (statePartArg, tokenId, actionContext): Promise<IRouteManagementState> => {
|
|
2910
|
-
const context = getActionContext();
|
|
2911
|
-
const currentState = statePartArg.getState()!;
|
|
2912
|
-
|
|
2913
|
-
try {
|
|
2914
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2915
|
-
interfaces.requests.IReq_RevokeApiToken
|
|
2916
|
-
>('/typedrequest', 'revokeApiToken');
|
|
2917
|
-
|
|
2918
|
-
await request.fire({
|
|
2919
|
-
identity: context.identity!,
|
|
2920
|
-
id: tokenId,
|
|
2921
|
-
});
|
|
2922
|
-
|
|
2923
|
-
return await actionContext!.dispatch(fetchApiTokensAction, null);
|
|
2924
|
-
} catch (error: unknown) {
|
|
2925
|
-
return {
|
|
2926
|
-
...currentState,
|
|
2927
|
-
error: error instanceof Error ? error.message : 'Failed to revoke token',
|
|
2928
|
-
};
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
);
|
|
2932
|
-
|
|
2933
|
-
export const toggleApiTokenAction = routeManagementStatePart.createAction<{
|
|
2934
|
-
id: string;
|
|
2935
|
-
enabled: boolean;
|
|
2936
|
-
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
|
|
2937
|
-
const context = getActionContext();
|
|
2938
|
-
const currentState = statePartArg.getState()!;
|
|
2939
|
-
|
|
2940
|
-
try {
|
|
2941
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2942
|
-
interfaces.requests.IReq_ToggleApiToken
|
|
2943
|
-
>('/typedrequest', 'toggleApiToken');
|
|
2944
|
-
|
|
2945
|
-
await request.fire({
|
|
2946
|
-
identity: context.identity!,
|
|
2947
|
-
id: dataArg.id,
|
|
2948
|
-
enabled: dataArg.enabled,
|
|
2949
|
-
});
|
|
2950
|
-
|
|
2951
|
-
return await actionContext!.dispatch(fetchApiTokensAction, null);
|
|
2952
|
-
} catch (error: unknown) {
|
|
2953
|
-
return {
|
|
2954
|
-
...currentState,
|
|
2955
|
-
error: error instanceof Error ? error.message : 'Failed to toggle token',
|
|
2956
|
-
};
|
|
2957
|
-
}
|
|
2958
|
-
});
|
|
2959
|
-
|
|
2960
|
-
// ============================================================================
|
|
2961
|
-
// Email Domains State
|
|
2962
|
-
// ============================================================================
|
|
2963
|
-
|
|
2964
|
-
export interface IEmailDomainsState {
|
|
2965
|
-
domains: interfaces.data.IEmailDomain[];
|
|
2966
|
-
settings: interfaces.data.IEmailServerSettings | null;
|
|
2967
|
-
isLoading: boolean;
|
|
2968
|
-
lastUpdated: number;
|
|
2969
|
-
}
|
|
2970
|
-
|
|
2971
|
-
export const emailDomainsStatePart = await appState.getStatePart<IEmailDomainsState>(
|
|
2972
|
-
'emailDomains',
|
|
2973
|
-
{
|
|
2974
|
-
domains: [],
|
|
2975
|
-
settings: null,
|
|
2976
|
-
isLoading: false,
|
|
2977
|
-
lastUpdated: 0,
|
|
2978
|
-
},
|
|
2979
|
-
'soft',
|
|
2980
|
-
);
|
|
2981
|
-
|
|
2982
|
-
export const fetchEmailDomainsAction = emailDomainsStatePart.createAction(
|
|
2983
|
-
async (statePartArg): Promise<IEmailDomainsState> => {
|
|
2984
|
-
const context = getActionContext();
|
|
2985
|
-
const currentState = statePartArg.getState()!;
|
|
2986
|
-
if (!context.identity) return currentState;
|
|
2987
|
-
|
|
2988
|
-
try {
|
|
2989
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2990
|
-
interfaces.requests.IReq_GetEmailDomains
|
|
2991
|
-
>('/typedrequest', 'getEmailDomains');
|
|
2992
|
-
const settingsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2993
|
-
interfaces.requests.IReq_GetEmailServerSettings
|
|
2994
|
-
>('/typedrequest', 'getEmailServerSettings');
|
|
2995
|
-
const response = await request.fire({ identity: context.identity });
|
|
2996
|
-
const settingsResponse = await settingsRequest.fire({ identity: context.identity });
|
|
2997
|
-
return {
|
|
2998
|
-
...currentState,
|
|
2999
|
-
domains: response.domains,
|
|
3000
|
-
settings: settingsResponse.settings,
|
|
3001
|
-
isLoading: false,
|
|
3002
|
-
lastUpdated: Date.now(),
|
|
3003
|
-
};
|
|
3004
|
-
} catch {
|
|
3005
|
-
return { ...currentState, isLoading: false };
|
|
3006
|
-
}
|
|
3007
|
-
},
|
|
3008
|
-
);
|
|
3009
|
-
|
|
3010
|
-
export const createEmailDomainAction = emailDomainsStatePart.createAction<{
|
|
3011
|
-
linkedDomainId: string;
|
|
3012
|
-
subdomain?: string;
|
|
3013
|
-
dkimSelector?: string;
|
|
3014
|
-
dkimKeySize?: number;
|
|
3015
|
-
rotateKeys?: boolean;
|
|
3016
|
-
rotationIntervalDays?: number;
|
|
3017
|
-
}>(async (statePartArg, args, actionContext) => {
|
|
3018
|
-
const context = getActionContext();
|
|
3019
|
-
const currentState = statePartArg.getState()!;
|
|
3020
|
-
try {
|
|
3021
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3022
|
-
interfaces.requests.IReq_CreateEmailDomain
|
|
3023
|
-
>('/typedrequest', 'createEmailDomain');
|
|
3024
|
-
await request.fire({ identity: context.identity!, ...args });
|
|
3025
|
-
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
|
|
3026
|
-
} catch {
|
|
3027
|
-
return currentState;
|
|
3028
|
-
}
|
|
3029
|
-
});
|
|
3030
|
-
|
|
3031
|
-
export const updateEmailServerSettingsAction = emailDomainsStatePart.createAction<
|
|
3032
|
-
interfaces.data.TEmailServerSettingsUpdate
|
|
3033
|
-
>(async (statePartArg, settings, actionContext) => {
|
|
3034
|
-
const context = getActionContext();
|
|
3035
|
-
const currentState = statePartArg.getState()!;
|
|
3036
|
-
try {
|
|
3037
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3038
|
-
interfaces.requests.IReq_UpdateEmailServerSettings
|
|
3039
|
-
>('/typedrequest', 'updateEmailServerSettings');
|
|
3040
|
-
const response = await request.fire({ identity: context.identity!, settings });
|
|
3041
|
-
if (!response.success) {
|
|
3042
|
-
return currentState;
|
|
3043
|
-
}
|
|
3044
|
-
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
|
|
3045
|
-
} catch {
|
|
3046
|
-
return currentState;
|
|
3047
|
-
}
|
|
3048
|
-
});
|
|
3049
|
-
|
|
3050
|
-
export const deleteEmailDomainAction = emailDomainsStatePart.createAction<string>(
|
|
3051
|
-
async (statePartArg, id, actionContext) => {
|
|
3052
|
-
const context = getActionContext();
|
|
3053
|
-
const currentState = statePartArg.getState()!;
|
|
3054
|
-
try {
|
|
3055
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3056
|
-
interfaces.requests.IReq_DeleteEmailDomain
|
|
3057
|
-
>('/typedrequest', 'deleteEmailDomain');
|
|
3058
|
-
await request.fire({ identity: context.identity!, id });
|
|
3059
|
-
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
|
|
3060
|
-
} catch {
|
|
3061
|
-
return currentState;
|
|
3062
|
-
}
|
|
3063
|
-
},
|
|
3064
|
-
);
|
|
3065
|
-
|
|
3066
|
-
export const validateEmailDomainAction = emailDomainsStatePart.createAction<string>(
|
|
3067
|
-
async (statePartArg, id, actionContext) => {
|
|
3068
|
-
const context = getActionContext();
|
|
3069
|
-
const currentState = statePartArg.getState()!;
|
|
3070
|
-
try {
|
|
3071
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3072
|
-
interfaces.requests.IReq_ValidateEmailDomain
|
|
3073
|
-
>('/typedrequest', 'validateEmailDomain');
|
|
3074
|
-
await request.fire({ identity: context.identity!, id });
|
|
3075
|
-
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
|
|
3076
|
-
} catch {
|
|
3077
|
-
return currentState;
|
|
3078
|
-
}
|
|
3079
|
-
},
|
|
3080
|
-
);
|
|
3081
|
-
|
|
3082
|
-
export const provisionEmailDomainDnsAction = emailDomainsStatePart.createAction<string>(
|
|
3083
|
-
async (statePartArg, id, actionContext) => {
|
|
3084
|
-
const context = getActionContext();
|
|
3085
|
-
const currentState = statePartArg.getState()!;
|
|
3086
|
-
try {
|
|
3087
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3088
|
-
interfaces.requests.IReq_ProvisionEmailDomainDns
|
|
3089
|
-
>('/typedrequest', 'provisionEmailDomainDns');
|
|
3090
|
-
await request.fire({ identity: context.identity!, id });
|
|
3091
|
-
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
|
|
3092
|
-
} catch {
|
|
3093
|
-
return currentState;
|
|
3094
|
-
}
|
|
3095
|
-
},
|
|
3096
|
-
);
|
|
3097
|
-
|
|
3098
|
-
// ============================================================================
|
|
3099
|
-
// Email Domain Standalone Functions
|
|
3100
|
-
// ============================================================================
|
|
3101
|
-
|
|
3102
|
-
export async function fetchEmailDomainDnsRecords(id: string) {
|
|
3103
|
-
const context = getActionContext();
|
|
3104
|
-
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3105
|
-
interfaces.requests.IReq_GetEmailDomainDnsRecords
|
|
3106
|
-
>('/typedrequest', 'getEmailDomainDnsRecords');
|
|
3107
|
-
return request.fire({ identity: context.identity!, id });
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
// ============================================================================
|
|
3111
|
-
// TypedSocket Client for Real-time Log Streaming
|
|
3112
|
-
// ============================================================================
|
|
3113
|
-
|
|
3114
|
-
let socketClient: plugins.typedsocket.TypedSocket | null = null;
|
|
3115
|
-
const socketRouter = new plugins.domtools.plugins.typedrequest.TypedRouter();
|
|
3116
|
-
|
|
3117
|
-
// Batched log entry handler — buffers incoming entries and flushes once per animation frame
|
|
3118
|
-
let logEntryBuffer: interfaces.data.ILogEntry[] = [];
|
|
3119
|
-
let logFlushScheduled = false;
|
|
3120
|
-
|
|
3121
|
-
function flushLogEntries() {
|
|
3122
|
-
logFlushScheduled = false;
|
|
3123
|
-
if (logEntryBuffer.length === 0) return;
|
|
3124
|
-
const current = logStatePart.getState()!;
|
|
3125
|
-
const updated = [...current.recentLogs, ...logEntryBuffer];
|
|
3126
|
-
logEntryBuffer = [];
|
|
3127
|
-
// Cap at 2000 entries
|
|
3128
|
-
if (updated.length > 2000) {
|
|
3129
|
-
updated.splice(0, updated.length - 2000);
|
|
3130
|
-
}
|
|
3131
|
-
logStatePart.setState({ ...current, recentLogs: updated } as ILogState);
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
|
-
// Register handler for pushed log entries from the server
|
|
3135
|
-
socketRouter.addTypedHandler(
|
|
3136
|
-
new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushLogEntry>(
|
|
3137
|
-
'pushLogEntry',
|
|
3138
|
-
async (dataArg) => {
|
|
3139
|
-
logEntryBuffer.push(dataArg.entry);
|
|
3140
|
-
if (!logFlushScheduled) {
|
|
3141
|
-
logFlushScheduled = true;
|
|
3142
|
-
requestAnimationFrame(flushLogEntries);
|
|
3143
|
-
}
|
|
3144
|
-
return {};
|
|
3145
|
-
}
|
|
3146
|
-
)
|
|
3147
|
-
);
|
|
3148
|
-
|
|
3149
|
-
async function connectSocket() {
|
|
3150
|
-
if (socketClient) return;
|
|
3151
|
-
try {
|
|
3152
|
-
socketClient = await plugins.typedsocket.TypedSocket.createClient(
|
|
3153
|
-
socketRouter,
|
|
3154
|
-
plugins.typedsocket.TypedSocket.useWindowLocationOriginUrl(),
|
|
3155
|
-
);
|
|
3156
|
-
await socketClient.setTag('role', 'ops_dashboard');
|
|
3157
|
-
} catch (err) {
|
|
3158
|
-
console.error('TypedSocket connection failed:', err);
|
|
3159
|
-
socketClient = null;
|
|
3160
|
-
}
|
|
3161
|
-
}
|
|
3162
|
-
|
|
3163
|
-
async function disconnectSocket() {
|
|
3164
|
-
if (socketClient) {
|
|
3165
|
-
try {
|
|
3166
|
-
await socketClient.stop();
|
|
3167
|
-
} catch {
|
|
3168
|
-
// ignore disconnect errors
|
|
3169
|
-
}
|
|
3170
|
-
socketClient = null;
|
|
3171
|
-
}
|
|
3172
|
-
}
|
|
3173
|
-
|
|
3174
|
-
// In-flight guard to prevent concurrent refresh requests
|
|
3175
|
-
let isRefreshing = false;
|
|
3176
|
-
|
|
3177
|
-
// Combined refresh action for efficient polling
|
|
3178
|
-
async function dispatchCombinedRefreshAction() {
|
|
3179
|
-
if (isRefreshing) return;
|
|
3180
|
-
isRefreshing = true;
|
|
3181
|
-
try {
|
|
3182
|
-
await dispatchCombinedRefreshActionInner();
|
|
3183
|
-
} finally {
|
|
3184
|
-
isRefreshing = false;
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
|
|
3188
|
-
async function dispatchCombinedRefreshActionInner() {
|
|
3189
|
-
const context = getActionContext();
|
|
3190
|
-
if (!context.identity) return;
|
|
3191
|
-
const currentView = uiStatePart.getState()!.activeView;
|
|
3192
|
-
const currentSubview = uiStatePart.getState()!.activeSubview;
|
|
3193
|
-
|
|
3194
|
-
try {
|
|
3195
|
-
// Always fetch basic stats for dashboard widgets
|
|
3196
|
-
const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
3197
|
-
interfaces.requests.IReq_GetCombinedMetrics
|
|
3198
|
-
>('/typedrequest', 'getCombinedMetrics');
|
|
3199
|
-
|
|
3200
|
-
const combinedResponse = await combinedRequest.fire({
|
|
3201
|
-
identity: context.identity,
|
|
3202
|
-
sections: {
|
|
3203
|
-
server: true,
|
|
3204
|
-
email: true,
|
|
3205
|
-
dns: true,
|
|
3206
|
-
security: true,
|
|
3207
|
-
network: currentView === 'network' && currentSubview === 'activity',
|
|
3208
|
-
radius: true,
|
|
3209
|
-
vpn: true,
|
|
3210
|
-
},
|
|
3211
|
-
});
|
|
3212
|
-
|
|
3213
|
-
// Update all stats from combined response
|
|
3214
|
-
const currentStatsState = statsStatePart.getState()!;
|
|
3215
|
-
statsStatePart.setState({
|
|
3216
|
-
...currentStatsState,
|
|
3217
|
-
serverStats: combinedResponse.metrics.server || currentStatsState.serverStats,
|
|
3218
|
-
emailStats: combinedResponse.metrics.email || currentStatsState.emailStats,
|
|
3219
|
-
dnsStats: combinedResponse.metrics.dns || currentStatsState.dnsStats,
|
|
3220
|
-
securityMetrics: combinedResponse.metrics.security || currentStatsState.securityMetrics,
|
|
3221
|
-
radiusStats: combinedResponse.metrics.radius || currentStatsState.radiusStats,
|
|
3222
|
-
vpnStats: combinedResponse.metrics.vpn || currentStatsState.vpnStats,
|
|
3223
|
-
lastUpdated: Date.now(),
|
|
3224
|
-
isLoading: false,
|
|
3225
|
-
error: null,
|
|
3226
|
-
});
|
|
3227
|
-
|
|
3228
|
-
// Update network stats if included
|
|
3229
|
-
if (combinedResponse.metrics.network && currentView === 'network') {
|
|
3230
|
-
const network = combinedResponse.metrics.network;
|
|
3231
|
-
const connectionsByIP: { [ip: string]: number } = {};
|
|
3232
|
-
|
|
3233
|
-
// Build connectionsByIP from connectionDetails (now populated with real per-IP data)
|
|
3234
|
-
network.connectionDetails.forEach(conn => {
|
|
3235
|
-
connectionsByIP[conn.remoteAddress] = (connectionsByIP[conn.remoteAddress] || 0) + (conn.connectionCount || 1);
|
|
3236
|
-
});
|
|
3237
|
-
|
|
3238
|
-
// Build connections from connectionDetails (real per-IP aggregates)
|
|
3239
|
-
const connections: interfaces.data.IConnectionInfo[] = network.connectionDetails.map((conn, i) => ({
|
|
3240
|
-
id: `ip-${conn.remoteAddress}`,
|
|
3241
|
-
remoteAddress: conn.remoteAddress,
|
|
3242
|
-
localAddress: 'server',
|
|
3243
|
-
startTime: conn.startTime,
|
|
3244
|
-
protocol: conn.protocol as any,
|
|
3245
|
-
state: conn.state as any,
|
|
3246
|
-
bytesReceived: conn.bytesIn,
|
|
3247
|
-
bytesSent: conn.bytesOut,
|
|
3248
|
-
connectionCount: conn.connectionCount,
|
|
3249
|
-
}));
|
|
3250
|
-
|
|
3251
|
-
networkStatePart.setState({
|
|
3252
|
-
...networkStatePart.getState()!,
|
|
3253
|
-
connections,
|
|
3254
|
-
connectionsByIP,
|
|
3255
|
-
throughputRate: {
|
|
3256
|
-
bytesInPerSecond: network.totalBandwidth.in,
|
|
3257
|
-
bytesOutPerSecond: network.totalBandwidth.out,
|
|
3258
|
-
},
|
|
3259
|
-
totalBytes: network.totalBytes || { in: 0, out: 0 },
|
|
3260
|
-
topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.connections })),
|
|
3261
|
-
topIPsByBandwidth: (network.topEndpointsByBandwidth || []).map(e => ({
|
|
3262
|
-
ip: e.endpoint,
|
|
3263
|
-
count: e.connections,
|
|
3264
|
-
bwIn: e.bandwidth?.in || 0,
|
|
3265
|
-
bwOut: e.bandwidth?.out || 0,
|
|
3266
|
-
})),
|
|
3267
|
-
topASNs: network.topASNs || [],
|
|
3268
|
-
throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
|
|
3269
|
-
domainActivity: network.domainActivity || [],
|
|
3270
|
-
throughputHistory: network.throughputHistory || [],
|
|
3271
|
-
requestsPerSecond: network.requestsPerSecond || 0,
|
|
3272
|
-
requestsTotal: network.requestsTotal || 0,
|
|
3273
|
-
backends: network.backends || [],
|
|
3274
|
-
frontendProtocols: network.frontendProtocols || null,
|
|
3275
|
-
backendProtocols: network.backendProtocols || null,
|
|
3276
|
-
lastUpdated: Date.now(),
|
|
3277
|
-
isLoading: false,
|
|
3278
|
-
error: null,
|
|
3279
|
-
});
|
|
3280
|
-
|
|
3281
|
-
refreshNetworkIpIntelligence(context.identity, [
|
|
3282
|
-
...network.connectionDetails.map((conn) => conn.remoteAddress),
|
|
3283
|
-
...network.topEndpoints.map((endpoint) => endpoint.endpoint),
|
|
3284
|
-
...(network.topEndpointsByBandwidth || []).map((endpoint) => endpoint.endpoint),
|
|
3285
|
-
]);
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
|
-
if (currentView === 'security') {
|
|
3289
|
-
runBackgroundRefresh('securityPolicy', 'Security policy refresh failed:', async () => {
|
|
3290
|
-
await securityPolicyStatePart.dispatchAction(fetchSecurityPolicyAction, null);
|
|
3291
|
-
});
|
|
3292
|
-
}
|
|
3293
|
-
|
|
3294
|
-
// Refresh certificate data if on Domains > Certificates subview
|
|
3295
|
-
if (currentView === 'domains' && currentSubview === 'certificates') {
|
|
3296
|
-
runBackgroundRefresh('certificates', 'Certificate refresh failed:', async () => {
|
|
3297
|
-
await certificateStatePart.dispatchAction(fetchCertificateOverviewAction, null);
|
|
3298
|
-
});
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
|
-
// Refresh remote ingress data if on the Network → Remote Ingress subview
|
|
3302
|
-
if (currentView === 'network' && currentSubview === 'remoteingress') {
|
|
3303
|
-
runBackgroundRefresh('remoteIngress', 'Remote ingress refresh failed:', async () => {
|
|
3304
|
-
await remoteIngressStatePart.dispatchAction(fetchRemoteIngressAction, null);
|
|
3305
|
-
});
|
|
3306
|
-
}
|
|
3307
|
-
|
|
3308
|
-
// Refresh VPN data if on the Network → VPN subview
|
|
3309
|
-
if (currentView === 'network' && currentSubview === 'vpn') {
|
|
3310
|
-
runBackgroundRefresh('vpn', 'VPN refresh failed:', async () => {
|
|
3311
|
-
await vpnStatePart.dispatchAction(fetchVpnAction, null);
|
|
3312
|
-
});
|
|
3313
|
-
}
|
|
3314
|
-
} catch (error) {
|
|
3315
|
-
console.error('Combined refresh failed:', error);
|
|
3316
|
-
// If the error looks like an auth failure (invalid JWT), force re-login
|
|
3317
|
-
const errMsg = String(error);
|
|
3318
|
-
if (errMsg.includes('invalid') || errMsg.includes('unauthorized') || errMsg.includes('401')) {
|
|
3319
|
-
await loginStatePart.dispatchAction(logoutAction, null);
|
|
3320
|
-
window.location.reload();
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
|
-
|
|
3325
|
-
// Create a proper action for the combined refresh so we can use createScheduledAction
|
|
3326
|
-
const combinedRefreshAction = statsStatePart.createAction<void>(async (statePartArg) => {
|
|
3327
|
-
await dispatchCombinedRefreshAction();
|
|
3328
|
-
// Return current state — dispatchCombinedRefreshAction already updates all state parts directly
|
|
3329
|
-
return statePartArg.getState()!;
|
|
3330
|
-
});
|
|
3331
|
-
|
|
3332
|
-
// Scheduled refresh process with autoPause: 'visibility' — automatically pauses when tab is hidden
|
|
3333
|
-
let refreshProcess: ReturnType<typeof statsStatePart.createScheduledAction> | null = null;
|
|
3334
|
-
|
|
3335
|
-
const startAutoRefresh = () => {
|
|
3336
|
-
const uiState = uiStatePart.getState()!;
|
|
3337
|
-
const loginState = loginStatePart.getState()!;
|
|
3338
|
-
|
|
3339
|
-
if (uiState.autoRefresh && loginState.isLoggedIn) {
|
|
3340
|
-
// Dispose old process if interval changed or not running
|
|
3341
|
-
if (refreshProcess) {
|
|
3342
|
-
refreshProcess.dispose();
|
|
3343
|
-
refreshProcess = null;
|
|
3344
|
-
}
|
|
3345
|
-
refreshProcess = statsStatePart.createScheduledAction({
|
|
3346
|
-
action: combinedRefreshAction,
|
|
3347
|
-
payload: undefined,
|
|
3348
|
-
intervalMs: uiState.refreshInterval,
|
|
3349
|
-
autoPause: 'visibility',
|
|
3350
|
-
});
|
|
3351
|
-
} else {
|
|
3352
|
-
if (refreshProcess) {
|
|
3353
|
-
refreshProcess.dispose();
|
|
3354
|
-
refreshProcess = null;
|
|
3355
|
-
}
|
|
3356
|
-
}
|
|
3357
|
-
};
|
|
3358
|
-
|
|
3359
|
-
// Watch for relevant changes
|
|
3360
|
-
let previousAutoRefresh = uiStatePart.getState()!.autoRefresh;
|
|
3361
|
-
let previousRefreshInterval = uiStatePart.getState()!.refreshInterval;
|
|
3362
|
-
let previousIsLoggedIn = loginStatePart.getState()!.isLoggedIn;
|
|
3363
|
-
|
|
3364
|
-
uiStatePart.select((s) => ({ autoRefresh: s.autoRefresh, refreshInterval: s.refreshInterval }))
|
|
3365
|
-
.subscribe((state) => {
|
|
3366
|
-
if (state.autoRefresh !== previousAutoRefresh ||
|
|
3367
|
-
state.refreshInterval !== previousRefreshInterval) {
|
|
3368
|
-
previousAutoRefresh = state.autoRefresh;
|
|
3369
|
-
previousRefreshInterval = state.refreshInterval;
|
|
3370
|
-
startAutoRefresh();
|
|
3371
|
-
}
|
|
3372
|
-
});
|
|
3373
|
-
|
|
3374
|
-
loginStatePart.select((s) => s.isLoggedIn).subscribe((isLoggedIn) => {
|
|
3375
|
-
if (isLoggedIn !== previousIsLoggedIn) {
|
|
3376
|
-
previousIsLoggedIn = isLoggedIn;
|
|
3377
|
-
startAutoRefresh();
|
|
3378
|
-
|
|
3379
|
-
// Connect/disconnect TypedSocket based on login state
|
|
3380
|
-
if (isLoggedIn) {
|
|
3381
|
-
connectSocket();
|
|
3382
|
-
} else {
|
|
3383
|
-
disconnectSocket();
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
});
|
|
3387
|
-
|
|
3388
|
-
// Pause/resume WebSocket when tab visibility changes
|
|
3389
|
-
document.addEventListener('visibilitychange', () => {
|
|
3390
|
-
if (document.hidden) {
|
|
3391
|
-
disconnectSocket();
|
|
3392
|
-
} else if (loginStatePart.getState()!.isLoggedIn) {
|
|
3393
|
-
connectSocket();
|
|
3394
|
-
}
|
|
3395
|
-
});
|
|
3396
|
-
|
|
3397
|
-
// Initial start
|
|
3398
|
-
startAutoRefresh();
|
|
3399
|
-
|
|
3400
|
-
// Connect TypedSocket if already logged in (e.g., persistent session)
|
|
3401
|
-
if (loginStatePart.getState()!.isLoggedIn) {
|
|
3402
|
-
connectSocket();
|
|
3403
|
-
}
|
|
1
|
+
// State management for the dcrouter Ops UI, split into per-domain modules.
|
|
2
|
+
// This barrel preserves the historical import path for all elements.
|
|
3
|
+
export * from './appstate/shared.js';
|
|
4
|
+
export * from './appstate/login.js';
|
|
5
|
+
export * from './appstate/stats.js';
|
|
6
|
+
export * from './appstate/config.js';
|
|
7
|
+
export * from './appstate/logs.js';
|
|
8
|
+
export * from './appstate/network.js';
|
|
9
|
+
export * from './appstate/security.js';
|
|
10
|
+
export * from './appstate/ui.js';
|
|
11
|
+
export * from './appstate/email-ops.js';
|
|
12
|
+
export * from './appstate/certificates.js';
|
|
13
|
+
export * from './appstate/acme.js';
|
|
14
|
+
export * from './appstate/remoteingress.js';
|
|
15
|
+
export * from './appstate/vpn.js';
|
|
16
|
+
export * from './appstate/target-profiles.js';
|
|
17
|
+
export * from './appstate/profiles-targets.js';
|
|
18
|
+
export * from './appstate/domains.js';
|
|
19
|
+
export * from './appstate/routes.js';
|
|
20
|
+
export * from './appstate/users.js';
|
|
21
|
+
export * from './appstate/email-domains.js';
|
|
22
|
+
|
|
23
|
+
// Side-effect module: auto-refresh scheduling and TypedSocket log streaming.
|
|
24
|
+
import './appstate/runtime.js';
|