@serve.zone/dcrouter 17.2.4 → 17.4.0
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 +2024 -1758
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +11 -1
- package/dist_ts/classes.dcrouter.js +63 -3
- package/dist_ts/config/classes.api-token-manager.d.ts +20 -2
- package/dist_ts/config/classes.api-token-manager.js +201 -6
- package/dist_ts/config/classes.gateway-client-manager.d.ts +36 -2
- package/dist_ts/config/classes.gateway-client-manager.js +391 -27
- package/dist_ts/db/documents/classes.api-token.doc.d.ts +2 -1
- package/dist_ts/db/documents/classes.api-token.doc.js +8 -2
- package/dist_ts/db/documents/classes.cached.email.d.ts +10 -1
- package/dist_ts/db/documents/classes.cached.email.js +27 -2
- package/dist_ts/db/documents/classes.gateway-client.doc.d.ts +25 -0
- package/dist_ts/db/documents/classes.gateway-client.doc.js +73 -2
- package/dist_ts/db/documents/classes.ops-config-event.doc.d.ts +55 -0
- package/dist_ts/db/documents/classes.ops-config-event.doc.js +296 -0
- package/dist_ts/db/documents/index.d.ts +1 -0
- package/dist_ts/db/documents/index.js +2 -1
- package/dist_ts/dns/classes.gateway-route-dns-reconciler.d.ts +52 -0
- package/dist_ts/dns/classes.gateway-route-dns-reconciler.js +609 -0
- package/dist_ts/dns/index.d.ts +1 -0
- package/dist_ts/dns/index.js +2 -1
- package/dist_ts/dns/manager.dns.d.ts +19 -0
- package/dist_ts/dns/manager.dns.js +25 -1
- package/dist_ts/email/classes.accepted-email-spool.d.ts +8 -1
- package/dist_ts/email/classes.accepted-email-spool.js +93 -1
- package/dist_ts/email/classes.smartmta-storage-manager.d.ts +7 -1
- package/dist_ts/email/classes.smartmta-storage-manager.js +46 -1
- package/dist_ts/email/smartmta-storage-policy.js +3 -1
- package/dist_ts/monitoring/classes.ops-event-manager.d.ts +32 -0
- package/dist_ts/monitoring/classes.ops-event-manager.js +71 -0
- package/dist_ts/monitoring/index.d.ts +1 -0
- package/dist_ts/monitoring/index.js +2 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +1 -0
- package/dist_ts/opsserver/classes.opsserver.js +3 -1
- package/dist_ts/opsserver/handlers/certificate.handler.d.ts +1 -0
- package/dist_ts/opsserver/handlers/certificate.handler.js +35 -5
- package/dist_ts/opsserver/handlers/config-event.handler.d.ts +12 -0
- package/dist_ts/opsserver/handlers/config-event.handler.js +47 -0
- package/dist_ts/opsserver/handlers/dns-record.handler.d.ts +1 -0
- package/dist_ts/opsserver/handlers/dns-record.handler.js +82 -3
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +6 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.js +40 -9
- package/dist_ts/opsserver/handlers/gatewayclient.handler.d.ts +9 -1
- package/dist_ts/opsserver/handlers/gatewayclient.handler.js +714 -162
- package/dist_ts/opsserver/handlers/index.d.ts +1 -0
- package/dist_ts/opsserver/handlers/index.js +2 -1
- package/dist_ts/opsserver/helpers/gateway-client-auth.d.ts +25 -0
- package/dist_ts/opsserver/helpers/gateway-client-auth.js +168 -0
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts_apiclient/classes.gatewayclient.d.ts +13 -1
- package/dist_ts_apiclient/classes.gatewayclient.js +18 -2
- package/dist_ts_interfaces/data/config-events.d.ts +34 -0
- package/dist_ts_interfaces/data/config-events.js +8 -0
- package/dist_ts_interfaces/data/index.d.ts +1 -0
- package/dist_ts_interfaces/data/index.js +2 -1
- package/dist_ts_interfaces/data/route-management.d.ts +28 -1
- package/dist_ts_interfaces/data/route-management.js +3 -1
- package/dist_ts_interfaces/data/workhoster.d.ts +1 -0
- package/dist_ts_interfaces/requests/config-events.d.ts +35 -0
- package/dist_ts_interfaces/requests/config-events.js +4 -0
- package/dist_ts_interfaces/requests/index.d.ts +1 -0
- package/dist_ts_interfaces/requests/index.js +2 -1
- package/dist_ts_interfaces/requests/realtime.d.ts +1 -1
- package/dist_ts_interfaces/requests/realtime.js +2 -1
- package/dist_ts_migrations/index.js +148 -9
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate/config-events.d.ts +19 -0
- package/dist_ts_web/appstate/config-events.js +68 -0
- package/dist_ts_web/appstate/runtime.js +7 -1
- package/dist_ts_web/appstate.d.ts +1 -0
- package/dist_ts_web/appstate.js +2 -1
- package/dist_ts_web/elements/ops-platform-events.d.ts +21 -0
- package/dist_ts_web/elements/ops-platform-events.js +361 -0
- package/dist_ts_web/elements/ops-view-logs.d.ts +4 -0
- package/dist_ts_web/elements/ops-view-logs.js +96 -9
- package/package.json +4 -4
- package/readme.plan.md +22 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +72 -2
- package/ts/config/classes.api-token-manager.ts +237 -4
- package/ts/config/classes.gateway-client-manager.ts +463 -19
- package/ts/db/documents/classes.api-token.doc.ts +8 -1
- package/ts/db/documents/classes.cached.email.ts +21 -1
- package/ts/db/documents/classes.gateway-client.doc.ts +86 -0
- package/ts/db/documents/classes.ops-config-event.doc.ts +257 -0
- package/ts/db/documents/index.ts +1 -0
- package/ts/dns/classes.gateway-route-dns-reconciler.ts +801 -0
- package/ts/dns/index.ts +1 -0
- package/ts/dns/manager.dns.ts +36 -0
- package/ts/email/classes.accepted-email-spool.ts +111 -0
- package/ts/email/classes.smartmta-storage-manager.ts +57 -1
- package/ts/email/smartmta-storage-policy.ts +2 -0
- package/ts/monitoring/classes.ops-event-manager.ts +99 -0
- package/ts/monitoring/index.ts +2 -1
- package/ts/opsserver/classes.opsserver.ts +2 -0
- package/ts/opsserver/handlers/certificate.handler.ts +51 -4
- package/ts/opsserver/handlers/config-event.handler.ts +62 -0
- package/ts/opsserver/handlers/dns-record.handler.ts +101 -2
- package/ts/opsserver/handlers/email-ops.handler.ts +42 -8
- package/ts/opsserver/handlers/gatewayclient.handler.ts +804 -136
- package/ts/opsserver/handlers/index.ts +1 -0
- package/ts/opsserver/helpers/gateway-client-auth.ts +198 -0
- package/ts/plugins.ts +2 -0
- package/ts_apiclient/classes.gatewayclient.ts +45 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate/config-events.ts +98 -0
- package/ts_web/appstate/runtime.ts +8 -1
- package/ts_web/appstate.ts +1 -0
- package/ts_web/elements/ops-platform-events.ts +315 -0
- package/ts_web/elements/ops-view-logs.ts +84 -7
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { OpsServer } from '../classes.opsserver.js';
|
|
3
|
+
import * as interfaces from '../../../ts_interfaces/index.js';
|
|
4
|
+
import { requireOpsAuth } from './auth.js';
|
|
5
|
+
|
|
6
|
+
export interface IGatewayMachineAuthContext {
|
|
7
|
+
userId: string;
|
|
8
|
+
isAdmin: boolean;
|
|
9
|
+
token?: interfaces.data.IStoredApiToken;
|
|
10
|
+
credentialId?: string;
|
|
11
|
+
policy?: interfaces.data.IApiTokenPolicy;
|
|
12
|
+
gatewayClient?: interfaces.data.IGatewayClient;
|
|
13
|
+
managedZoneNames: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type TGatewayCredentialState = 'candidate' | 'active' | 'manual';
|
|
17
|
+
|
|
18
|
+
export interface IRequireGatewayMachineAuthOptions {
|
|
19
|
+
allowCandidate?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getGatewayCredentialState(
|
|
23
|
+
tokenArg: interfaces.data.IStoredApiToken,
|
|
24
|
+
): TGatewayCredentialState {
|
|
25
|
+
const lifecycle = tokenArg.gatewayCredentialLifecycle;
|
|
26
|
+
if (lifecycle == null) return 'manual';
|
|
27
|
+
if (lifecycle.source === 'provisioned'
|
|
28
|
+
&& (lifecycle.state === 'candidate' || lifecycle.state === 'active')) {
|
|
29
|
+
return lifecycle.state;
|
|
30
|
+
}
|
|
31
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential lifecycle is invalid');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function normalizeGatewayHostname(hostnameArg: string): string {
|
|
35
|
+
const candidate = String(hostnameArg || '').trim().toLowerCase().replace(/\.+$/, '');
|
|
36
|
+
if (!candidate || candidate.includes('*')) {
|
|
37
|
+
throw new plugins.typedrequest.TypedResponseError(`invalid hostname: ${hostnameArg}`);
|
|
38
|
+
}
|
|
39
|
+
const ascii = plugins.url.domainToASCII(candidate);
|
|
40
|
+
if (!ascii) {
|
|
41
|
+
throw new plugins.typedrequest.TypedResponseError(`invalid hostname: ${hostnameArg}`);
|
|
42
|
+
}
|
|
43
|
+
return ascii;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function normalizeGatewayTargetHost(hostArg: string): string {
|
|
47
|
+
const candidate = String(hostArg || '').trim().toLowerCase().replace(/\.+$/, '');
|
|
48
|
+
if (plugins.net.isIP(candidate)) return candidate;
|
|
49
|
+
const ascii = plugins.url.domainToASCII(candidate);
|
|
50
|
+
if (!ascii) {
|
|
51
|
+
throw new plugins.typedrequest.TypedResponseError(`invalid route target host: ${hostArg}`);
|
|
52
|
+
}
|
|
53
|
+
return ascii;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isHostnameInManagedZone(hostnameArg: string, managedZoneNamesArg: string[]): boolean {
|
|
57
|
+
const hostname = normalizeGatewayHostname(hostnameArg);
|
|
58
|
+
return managedZoneNamesArg.some((zoneArg) => {
|
|
59
|
+
const zone = normalizeGatewayHostname(zoneArg);
|
|
60
|
+
return hostname === zone || hostname.endsWith(`.${zone}`);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function matchesGatewayHostnamePatterns(
|
|
65
|
+
hostnameArg: string,
|
|
66
|
+
patternsArg: string[],
|
|
67
|
+
managedZoneNamesArg: string[],
|
|
68
|
+
): boolean {
|
|
69
|
+
const hostname = normalizeGatewayHostname(hostnameArg);
|
|
70
|
+
for (const patternArg of patternsArg) {
|
|
71
|
+
const rawPattern = String(patternArg || '').trim().toLowerCase().replace(/\.+$/, '');
|
|
72
|
+
if (!rawPattern) continue;
|
|
73
|
+
if (rawPattern === '*') {
|
|
74
|
+
if (isHostnameInManagedZone(hostname, managedZoneNamesArg)) return true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const wildcard = rawPattern.startsWith('*.');
|
|
78
|
+
const normalizedPattern = normalizeGatewayHostname(wildcard ? rawPattern.slice(2) : rawPattern);
|
|
79
|
+
if (!wildcard && hostname === normalizedPattern) return true;
|
|
80
|
+
if (!wildcard || !hostname.endsWith(`.${normalizedPattern}`)) continue;
|
|
81
|
+
const label = hostname.slice(0, -(normalizedPattern.length + 1));
|
|
82
|
+
if (label && !label.includes('.')) return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function hasOwnedEnabledGatewayRoute(
|
|
88
|
+
opsServerRefArg: OpsServer,
|
|
89
|
+
authArg: IGatewayMachineAuthContext,
|
|
90
|
+
hostnameArg: string,
|
|
91
|
+
): boolean {
|
|
92
|
+
if (authArg.isAdmin) return true;
|
|
93
|
+
const liveClient = authArg.gatewayClient;
|
|
94
|
+
if (!liveClient) return false;
|
|
95
|
+
const hostname = normalizeGatewayHostname(hostnameArg);
|
|
96
|
+
const routes = opsServerRefArg.dcRouterRef.routeConfigManager?.getMergedRoutes().routes || [];
|
|
97
|
+
return routes.some((routeArg) => {
|
|
98
|
+
if (!routeArg.enabled || routeArg.metadata?.ownerType !== 'gatewayClient') return false;
|
|
99
|
+
if (routeArg.metadata.gatewayClientId !== liveClient.id) return false;
|
|
100
|
+
if ((routeArg.metadata.gatewayClientType || 'custom') !== liveClient.type) return false;
|
|
101
|
+
const domains = routeArg.route.match?.domains;
|
|
102
|
+
const values = Array.isArray(domains)
|
|
103
|
+
? domains
|
|
104
|
+
: typeof domains === 'string'
|
|
105
|
+
? domains.split(',')
|
|
106
|
+
: [];
|
|
107
|
+
return values.some((domainArg) => {
|
|
108
|
+
try {
|
|
109
|
+
return normalizeGatewayHostname(domainArg) === hostname;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function requireGatewayMachineAuth(
|
|
118
|
+
opsServerRefArg: OpsServer,
|
|
119
|
+
requestArg: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
|
120
|
+
scopeArg: 'gateway-clients:read' | 'gateway-clients:write',
|
|
121
|
+
optionsArg: IRequireGatewayMachineAuthOptions = {},
|
|
122
|
+
): Promise<IGatewayMachineAuthContext> {
|
|
123
|
+
const auth = await requireOpsAuth(opsServerRefArg, requestArg, {
|
|
124
|
+
scope: scopeArg,
|
|
125
|
+
requireAdminIdentity: false,
|
|
126
|
+
});
|
|
127
|
+
const managedZoneNames = (await opsServerRefArg.dcRouterRef.dnsManager?.listDomains() || [])
|
|
128
|
+
.map((domainArg) => domainArg.name);
|
|
129
|
+
|
|
130
|
+
if (auth.isAdmin) {
|
|
131
|
+
return {
|
|
132
|
+
userId: auth.userId,
|
|
133
|
+
isAdmin: true,
|
|
134
|
+
token: auth.token,
|
|
135
|
+
policy: auth.token?.policy,
|
|
136
|
+
managedZoneNames,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (auth.type !== 'apiToken' || auth.token?.policy?.role !== 'gatewayClient') {
|
|
140
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential required');
|
|
141
|
+
}
|
|
142
|
+
const credentialState = getGatewayCredentialState(auth.token);
|
|
143
|
+
if (credentialState === 'candidate' && !optionsArg.allowCandidate) {
|
|
144
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential is awaiting finalization');
|
|
145
|
+
}
|
|
146
|
+
const credentialLifecycle = auth.token.gatewayCredentialLifecycle;
|
|
147
|
+
const tokenBinding = auth.token.policy.gatewayClient;
|
|
148
|
+
if (!tokenBinding?.id || !tokenBinding.type || !Number.isSafeInteger(tokenBinding.policyGeneration)) {
|
|
149
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential binding is incomplete');
|
|
150
|
+
}
|
|
151
|
+
const manager = opsServerRefArg.dcRouterRef.gatewayClientManager;
|
|
152
|
+
if (!manager) {
|
|
153
|
+
throw new plugins.typedrequest.TypedResponseError('gateway client management not initialized');
|
|
154
|
+
}
|
|
155
|
+
// Deliberately load the durable policy on every request. Token snapshots are
|
|
156
|
+
// identity bindings only and never provide current authorization.
|
|
157
|
+
if (credentialLifecycle
|
|
158
|
+
&& credentialLifecycle.policyGeneration !== tokenBinding.policyGeneration) {
|
|
159
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential binding is inconsistent');
|
|
160
|
+
}
|
|
161
|
+
const liveClient = credentialLifecycle
|
|
162
|
+
? await manager.getClientForProvisionedCredential(
|
|
163
|
+
tokenBinding.id,
|
|
164
|
+
tokenBinding.type,
|
|
165
|
+
credentialLifecycle,
|
|
166
|
+
)
|
|
167
|
+
: await manager.getClient(tokenBinding.id);
|
|
168
|
+
if (!liveClient || !liveClient.enabled) {
|
|
169
|
+
throw new plugins.typedrequest.TypedResponseError('gateway client is missing or disabled');
|
|
170
|
+
}
|
|
171
|
+
if (liveClient.type !== tokenBinding.type) {
|
|
172
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential type does not match live policy');
|
|
173
|
+
}
|
|
174
|
+
if (liveClient.policyGeneration !== tokenBinding.policyGeneration) {
|
|
175
|
+
throw new plugins.typedrequest.TypedResponseError('gateway-client credential policy generation is stale');
|
|
176
|
+
}
|
|
177
|
+
const livePolicy: interfaces.data.IApiTokenPolicy = {
|
|
178
|
+
role: 'gatewayClient',
|
|
179
|
+
scopes: ['gateway-clients:read', 'gateway-clients:write'],
|
|
180
|
+
gatewayClient: {
|
|
181
|
+
type: liveClient.type,
|
|
182
|
+
id: liveClient.id,
|
|
183
|
+
policyGeneration: liveClient.policyGeneration,
|
|
184
|
+
},
|
|
185
|
+
hostnamePatterns: structuredClone(liveClient.hostnamePatterns),
|
|
186
|
+
allowedRouteTargets: structuredClone(liveClient.allowedRouteTargets),
|
|
187
|
+
capabilities: structuredClone(liveClient.capabilities),
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
userId: auth.userId,
|
|
191
|
+
isAdmin: false,
|
|
192
|
+
token: auth.token,
|
|
193
|
+
credentialId: auth.token.id,
|
|
194
|
+
policy: livePolicy,
|
|
195
|
+
gatewayClient: liveClient,
|
|
196
|
+
managedZoneNames,
|
|
197
|
+
};
|
|
198
|
+
}
|
package/ts/plugins.ts
CHANGED
|
@@ -9,6 +9,7 @@ import * as os from 'node:os';
|
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
import * as tls from 'node:tls';
|
|
11
11
|
import * as util from 'node:util';
|
|
12
|
+
import * as url from 'node:url';
|
|
12
13
|
|
|
13
14
|
export {
|
|
14
15
|
dns,
|
|
@@ -21,6 +22,7 @@ export {
|
|
|
21
22
|
path,
|
|
22
23
|
tls,
|
|
23
24
|
util,
|
|
25
|
+
url,
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
// @serve.zone scope
|
|
@@ -20,6 +20,35 @@ export class GatewayClientApi {
|
|
|
20
20
|
return response.context;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
public async provisionCredential(options: {
|
|
24
|
+
provisioning: servezoneInterfaces.data.IGatewayClientProvisioningSpec;
|
|
25
|
+
credentialName?: string;
|
|
26
|
+
expiresInDays?: number | null;
|
|
27
|
+
}): Promise<servezoneInterfaces.requests.gateway.IReq_ProvisionGatewayClientCredential['response']> {
|
|
28
|
+
return await this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_ProvisionGatewayClientCredential>(
|
|
29
|
+
'provisionGatewayClientCredential',
|
|
30
|
+
this.clientRef.buildRequestPayload(options) as any,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async finalizeCredential(
|
|
35
|
+
credential: Pick<servezoneInterfaces.data.IGatewayClientCredential, 'tokenId' | 'tokenValue'>,
|
|
36
|
+
): Promise<servezoneInterfaces.requests.gateway.IReq_FinalizeGatewayClientCredential['response']> {
|
|
37
|
+
return await this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_FinalizeGatewayClientCredential>(
|
|
38
|
+
'finalizeGatewayClientCredential',
|
|
39
|
+
{ apiToken: credential.tokenValue, tokenId: credential.tokenId },
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async getMailOverview(
|
|
44
|
+
limit?: number,
|
|
45
|
+
): Promise<servezoneInterfaces.requests.gateway.IReq_GetGatewayClientMailOverview['response']> {
|
|
46
|
+
return await this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_GetGatewayClientMailOverview>(
|
|
47
|
+
'getGatewayClientMailOverview',
|
|
48
|
+
this.clientRef.buildRequestPayload({ limit }) as any,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
23
52
|
public async getDomains(): Promise<servezoneInterfaces.data.IGatewayDomain[]> {
|
|
24
53
|
const response = await this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_GetGatewayClientDomains>(
|
|
25
54
|
'getGatewayClientDomains',
|
|
@@ -36,10 +65,20 @@ export class GatewayClientApi {
|
|
|
36
65
|
return response.records;
|
|
37
66
|
}
|
|
38
67
|
|
|
68
|
+
public async getRoutes(): Promise<servezoneInterfaces.data.IGatewayClientRoute[]> {
|
|
69
|
+
const response = await this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_GetGatewayClientRoutes>(
|
|
70
|
+
'getGatewayClientRoutes',
|
|
71
|
+
this.clientRef.buildRequestPayload() as any,
|
|
72
|
+
);
|
|
73
|
+
return response.routes;
|
|
74
|
+
}
|
|
75
|
+
|
|
39
76
|
public async syncRoute(options: {
|
|
40
77
|
ownership: servezoneInterfaces.data.IGatewayClientOwnership;
|
|
41
78
|
route: servezoneInterfaces.data.IGatewayRouteConfig;
|
|
42
79
|
enabled?: boolean;
|
|
80
|
+
dnsMode?: servezoneInterfaces.data.TGatewayRouteDnsMode;
|
|
81
|
+
sourceProfileRef?: string;
|
|
43
82
|
}): Promise<servezoneInterfaces.data.IGatewayRouteSyncResult> {
|
|
44
83
|
return this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_SyncGatewayClientRoute>(
|
|
45
84
|
'syncGatewayClientRoute',
|
|
@@ -47,18 +86,24 @@ export class GatewayClientApi {
|
|
|
47
86
|
ownership: options.ownership,
|
|
48
87
|
route: options.route,
|
|
49
88
|
enabled: options.enabled,
|
|
89
|
+
dnsMode: options.dnsMode,
|
|
90
|
+
sourceProfileRef: options.sourceProfileRef,
|
|
50
91
|
}) as any,
|
|
51
92
|
);
|
|
52
93
|
}
|
|
53
94
|
|
|
54
95
|
public async deleteRoute(
|
|
55
96
|
ownership: servezoneInterfaces.data.IGatewayClientOwnership,
|
|
97
|
+
options: {
|
|
98
|
+
dnsMode?: servezoneInterfaces.data.TGatewayRouteDnsMode;
|
|
99
|
+
} = {},
|
|
56
100
|
): Promise<servezoneInterfaces.data.IGatewayRouteSyncResult> {
|
|
57
101
|
return this.clientRef.request<servezoneInterfaces.requests.gateway.IReq_SyncGatewayClientRoute>(
|
|
58
102
|
'syncGatewayClientRoute',
|
|
59
103
|
this.clientRef.buildRequestPayload({
|
|
60
104
|
ownership,
|
|
61
105
|
delete: true,
|
|
106
|
+
dnsMode: options.dnsMode,
|
|
62
107
|
}) as any,
|
|
63
108
|
);
|
|
64
109
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import * as interfaces from '../../ts_interfaces/index.js';
|
|
3
|
+
import { appState } from './shared.js';
|
|
4
|
+
import { getActionContext } from './login.js';
|
|
5
|
+
|
|
6
|
+
export interface IConfigEventState {
|
|
7
|
+
events: interfaces.data.IOpsConfigEvent[];
|
|
8
|
+
total: number;
|
|
9
|
+
unacknowledgedCount: number;
|
|
10
|
+
nextCursor?: interfaces.data.IOpsConfigEventCursor;
|
|
11
|
+
showAcknowledged: boolean;
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const configEventStatePart = await appState.getStatePart<IConfigEventState>(
|
|
16
|
+
'configEvents',
|
|
17
|
+
{
|
|
18
|
+
events: [],
|
|
19
|
+
total: 0,
|
|
20
|
+
unacknowledgedCount: 0,
|
|
21
|
+
nextCursor: undefined,
|
|
22
|
+
showAcknowledged: false,
|
|
23
|
+
isLoading: false,
|
|
24
|
+
},
|
|
25
|
+
'soft'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const fetchConfigEventsAction = configEventStatePart.createAction<{
|
|
29
|
+
showAcknowledged?: boolean;
|
|
30
|
+
cursor?: interfaces.data.IOpsConfigEventCursor;
|
|
31
|
+
append?: boolean;
|
|
32
|
+
} | null>(async (statePartArg, dataArg): Promise<IConfigEventState> => {
|
|
33
|
+
const context = getActionContext();
|
|
34
|
+
const currentState = statePartArg.getState()!;
|
|
35
|
+
if (!context.identity) return currentState;
|
|
36
|
+
|
|
37
|
+
const showAcknowledged = dataArg?.showAcknowledged ?? currentState.showAcknowledged;
|
|
38
|
+
const listRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
39
|
+
interfaces.requests.IReq_ListConfigEvents
|
|
40
|
+
>('/typedrequest', 'listConfigEvents');
|
|
41
|
+
const response = await listRequest.fire({
|
|
42
|
+
identity: context.identity,
|
|
43
|
+
acknowledged: showAcknowledged ? undefined : false,
|
|
44
|
+
limit: 200,
|
|
45
|
+
cursor: dataArg?.cursor,
|
|
46
|
+
});
|
|
47
|
+
const append = dataArg?.append === true && Boolean(dataArg.cursor);
|
|
48
|
+
const events = append
|
|
49
|
+
? Array.from(new Map([
|
|
50
|
+
...currentState.events,
|
|
51
|
+
...(response.events || []),
|
|
52
|
+
].map((eventArg) => [eventArg.id, eventArg])).values())
|
|
53
|
+
: response.events || [];
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
...currentState,
|
|
57
|
+
events,
|
|
58
|
+
total: response.total || 0,
|
|
59
|
+
unacknowledgedCount: response.unacknowledgedCount || 0,
|
|
60
|
+
nextCursor: response.nextCursor,
|
|
61
|
+
showAcknowledged,
|
|
62
|
+
isLoading: false,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const acknowledgeConfigEventsAction = configEventStatePart.createAction<{
|
|
67
|
+
ids: string[];
|
|
68
|
+
}>(async (statePartArg, dataArg): Promise<IConfigEventState> => {
|
|
69
|
+
const context = getActionContext();
|
|
70
|
+
const currentState = statePartArg.getState()!;
|
|
71
|
+
if (!context.identity || dataArg.ids.length === 0) return currentState;
|
|
72
|
+
|
|
73
|
+
const ackRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
74
|
+
interfaces.requests.IReq_AcknowledgeConfigEvents
|
|
75
|
+
>('/typedrequest', 'acknowledgeConfigEvents');
|
|
76
|
+
await ackRequest.fire({
|
|
77
|
+
identity: context.identity,
|
|
78
|
+
ids: dataArg.ids,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const listRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
82
|
+
interfaces.requests.IReq_ListConfigEvents
|
|
83
|
+
>('/typedrequest', 'listConfigEvents');
|
|
84
|
+
const response = await listRequest.fire({
|
|
85
|
+
identity: context.identity,
|
|
86
|
+
acknowledged: currentState.showAcknowledged ? undefined : false,
|
|
87
|
+
limit: 200,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
...currentState,
|
|
92
|
+
events: response.events || [],
|
|
93
|
+
total: response.total || 0,
|
|
94
|
+
unacknowledgedCount: response.unacknowledgedCount || 0,
|
|
95
|
+
nextCursor: response.nextCursor,
|
|
96
|
+
isLoading: false,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
@@ -39,7 +39,8 @@ type TRealtimeRefreshGroup =
|
|
|
39
39
|
| 'dns'
|
|
40
40
|
| 'routes'
|
|
41
41
|
| 'edges'
|
|
42
|
-
| 'certificates'
|
|
42
|
+
| 'certificates'
|
|
43
|
+
| 'configEvents';
|
|
43
44
|
|
|
44
45
|
const pendingRealtimeRefreshes = new Set<TRealtimeRefreshGroup>();
|
|
45
46
|
let realtimeRefreshLoopRunning = false;
|
|
@@ -55,6 +56,7 @@ const resourceRefreshGroups: Record<
|
|
|
55
56
|
routes: 'routes',
|
|
56
57
|
edges: 'edges',
|
|
57
58
|
certificates: 'certificates',
|
|
59
|
+
configEvents: 'configEvents',
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
const refreshRealtimeGroup = async (groupArg: TRealtimeRefreshGroup): Promise<void> => {
|
|
@@ -88,6 +90,11 @@ const refreshRealtimeGroup = async (groupArg: TRealtimeRefreshGroup): Promise<vo
|
|
|
88
90
|
case 'certificates':
|
|
89
91
|
await certificateStatePart.dispatchAction(fetchCertificateOverviewAction, null);
|
|
90
92
|
break;
|
|
93
|
+
case 'configEvents': {
|
|
94
|
+
const { configEventStatePart, fetchConfigEventsAction } = await import('./config-events.js');
|
|
95
|
+
await configEventStatePart.dispatchAction(fetchConfigEventsAction, null);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
};
|
|
93
100
|
|
package/ts_web/appstate.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './appstate/login.js';
|
|
|
5
5
|
export * from './appstate/stats.js';
|
|
6
6
|
export * from './appstate/config.js';
|
|
7
7
|
export * from './appstate/logs.js';
|
|
8
|
+
export * from './appstate/config-events.js';
|
|
8
9
|
export * from './appstate/network.js';
|
|
9
10
|
export * from './appstate/security.js';
|
|
10
11
|
export * from './appstate/ui.js';
|