@rapidmx/web-client 0.37.1 → 0.39.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/apps/admin/configuration-encryption/index.tsx +17 -0
- package/apps/admin/domains/[uid].tsx +17 -4
- package/apps/admin/integrations/dns-providers/[uid].tsx +205 -0
- package/apps/admin/integrations/dns-providers/index.tsx +164 -0
- package/apps/admin/integrations/dns-providers/new/index.tsx +55 -0
- package/apps/admin/integrations/dns-resolver/index.tsx +16 -0
- package/apps/admin/integrations/giphy/index.tsx +16 -0
- package/apps/admin/integrations/index.tsx +31 -0
- package/apps/admin/integrations/plugin-registry/index.tsx +16 -0
- package/apps/admin/integrations/signing-ca/index.tsx +16 -0
- package/apps/admin/integrations/stun/index.tsx +16 -0
- package/apps/shared/components/admin/configEncryption/ConfigKeysManager.tsx +529 -0
- package/apps/shared/components/admin/configEncryption/keyringParts.ts +37 -0
- package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +20 -4
- package/apps/shared/components/admin/diagnostics/InformationPanel.tsx +23 -6
- package/apps/shared/components/admin/diagnostics/KeyringCard.tsx +144 -0
- package/apps/shared/components/admin/diagnostics/SettingsTable.tsx +21 -6
- package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +15 -3
- package/apps/shared/components/admin/integrations/DnsProviderForm.tsx +354 -0
- package/apps/shared/components/admin/integrations/DnsProviderHosts.tsx +197 -0
- package/apps/shared/components/admin/integrations/DnsProviderOperations.tsx +115 -0
- package/apps/shared/components/admin/integrations/DnsResolverForm.tsx +61 -0
- package/apps/shared/components/admin/integrations/GiphyForm.tsx +59 -0
- package/apps/shared/components/admin/integrations/IntegrationSectionForm.tsx +234 -0
- package/apps/shared/components/admin/integrations/IntegrationSectionPage.tsx +40 -0
- package/apps/shared/components/admin/integrations/IntegrationsLayout.tsx +72 -0
- package/apps/shared/components/admin/integrations/PluginRegistryForm.tsx +230 -0
- package/apps/shared/components/admin/integrations/SecretField.tsx +91 -0
- package/apps/shared/components/admin/integrations/SigningCaForm.tsx +95 -0
- package/apps/shared/components/admin/integrations/StunForm.tsx +63 -0
- package/apps/shared/components/admin/integrations/dnsProviderParts.tsx +72 -0
- package/apps/shared/components/admin/layout/AdminShell.tsx +12 -1
- package/apps/shared/components/admin/settings/CopyableValue.tsx +16 -0
- package/apps/shared/components/admin/settings/DomainApexTxt.tsx +319 -0
- package/apps/shared/components/admin/settings/DomainDkimRotation.tsx +408 -0
- package/apps/shared/components/admin/settings/DomainDnsProvider.tsx +147 -0
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +174 -17
- package/apps/shared/components/admin/setup/SetupWizard.tsx +39 -6
- package/dist/apps/admin/configuration-encryption/index.d.ts +4 -0
- package/dist/apps/admin/configuration-encryption/index.js +7 -0
- package/dist/apps/admin/domains/[uid].js +11 -4
- package/dist/apps/admin/integrations/dns-providers/[uid].d.ts +7 -0
- package/dist/apps/admin/integrations/dns-providers/[uid].js +117 -0
- package/dist/apps/admin/integrations/dns-providers/index.d.ts +3 -0
- package/dist/apps/admin/integrations/dns-providers/index.js +68 -0
- package/dist/apps/admin/integrations/dns-providers/new/index.d.ts +3 -0
- package/dist/apps/admin/integrations/dns-providers/new/index.js +31 -0
- package/dist/apps/admin/integrations/dns-resolver/index.d.ts +3 -0
- package/dist/apps/admin/integrations/dns-resolver/index.js +6 -0
- package/dist/apps/admin/integrations/giphy/index.d.ts +3 -0
- package/dist/apps/admin/integrations/giphy/index.js +6 -0
- package/dist/apps/admin/integrations/index.d.ts +4 -0
- package/dist/apps/admin/integrations/index.js +7 -0
- package/dist/apps/admin/integrations/plugin-registry/index.d.ts +3 -0
- package/dist/apps/admin/integrations/plugin-registry/index.js +6 -0
- package/dist/apps/admin/integrations/signing-ca/index.d.ts +3 -0
- package/dist/apps/admin/integrations/signing-ca/index.js +6 -0
- package/dist/apps/admin/integrations/stun/index.d.ts +3 -0
- package/dist/apps/admin/integrations/stun/index.js +6 -0
- package/dist/apps/shared/components/admin/configEncryption/ConfigKeysManager.d.ts +9 -0
- package/dist/apps/shared/components/admin/configEncryption/ConfigKeysManager.js +187 -0
- package/dist/apps/shared/components/admin/configEncryption/keyringParts.d.ts +10 -0
- package/dist/apps/shared/components/admin/configEncryption/keyringParts.js +26 -0
- package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +20 -4
- package/dist/apps/shared/components/admin/diagnostics/InformationPanel.d.ts +8 -4
- package/dist/apps/shared/components/admin/diagnostics/InformationPanel.js +7 -5
- package/dist/apps/shared/components/admin/diagnostics/KeyringCard.d.ts +18 -0
- package/dist/apps/shared/components/admin/diagnostics/KeyringCard.js +51 -0
- package/dist/apps/shared/components/admin/diagnostics/SettingsTable.d.ts +9 -1
- package/dist/apps/shared/components/admin/diagnostics/SettingsTable.js +10 -3
- package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.d.ts +13 -3
- package/dist/apps/shared/components/admin/integrations/DnsProviderForm.d.ts +17 -0
- package/dist/apps/shared/components/admin/integrations/DnsProviderForm.js +147 -0
- package/dist/apps/shared/components/admin/integrations/DnsProviderHosts.d.ts +10 -0
- package/dist/apps/shared/components/admin/integrations/DnsProviderHosts.js +101 -0
- package/dist/apps/shared/components/admin/integrations/DnsProviderOperations.d.ts +8 -0
- package/dist/apps/shared/components/admin/integrations/DnsProviderOperations.js +60 -0
- package/dist/apps/shared/components/admin/integrations/DnsResolverForm.d.ts +7 -0
- package/dist/apps/shared/components/admin/integrations/DnsResolverForm.js +15 -0
- package/dist/apps/shared/components/admin/integrations/GiphyForm.d.ts +7 -0
- package/dist/apps/shared/components/admin/integrations/GiphyForm.js +11 -0
- package/dist/apps/shared/components/admin/integrations/IntegrationSectionForm.d.ts +54 -0
- package/dist/apps/shared/components/admin/integrations/IntegrationSectionForm.js +131 -0
- package/dist/apps/shared/components/admin/integrations/IntegrationSectionPage.d.ts +12 -0
- package/dist/apps/shared/components/admin/integrations/IntegrationSectionPage.js +21 -0
- package/dist/apps/shared/components/admin/integrations/IntegrationsLayout.d.ts +14 -0
- package/dist/apps/shared/components/admin/integrations/IntegrationsLayout.js +46 -0
- package/dist/apps/shared/components/admin/integrations/PluginRegistryForm.d.ts +7 -0
- package/dist/apps/shared/components/admin/integrations/PluginRegistryForm.js +90 -0
- package/dist/apps/shared/components/admin/integrations/SecretField.d.ts +25 -0
- package/dist/apps/shared/components/admin/integrations/SecretField.js +25 -0
- package/dist/apps/shared/components/admin/integrations/SigningCaForm.d.ts +7 -0
- package/dist/apps/shared/components/admin/integrations/SigningCaForm.js +25 -0
- package/dist/apps/shared/components/admin/integrations/StunForm.d.ts +9 -0
- package/dist/apps/shared/components/admin/integrations/StunForm.js +26 -0
- package/dist/apps/shared/components/admin/integrations/dnsProviderParts.d.ts +28 -0
- package/dist/apps/shared/components/admin/integrations/dnsProviderParts.js +43 -0
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +8 -1
- package/dist/apps/shared/components/admin/settings/CopyableValue.d.ts +6 -0
- package/dist/apps/shared/components/admin/settings/CopyableValue.js +6 -0
- package/dist/apps/shared/components/admin/settings/DomainApexTxt.d.ts +13 -0
- package/dist/apps/shared/components/admin/settings/DomainApexTxt.js +148 -0
- package/dist/apps/shared/components/admin/settings/DomainDkimRotation.d.ts +15 -0
- package/dist/apps/shared/components/admin/settings/DomainDkimRotation.js +200 -0
- package/dist/apps/shared/components/admin/settings/DomainDnsProvider.d.ts +12 -0
- package/dist/apps/shared/components/admin/settings/DomainDnsProvider.js +70 -0
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +3 -3
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +61 -8
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +9 -1
- package/dist/lib/admin/configKeysApi.d.ts +96 -0
- package/dist/lib/admin/configKeysApi.js +34 -0
- package/dist/lib/admin/dnsProvidersApi.d.ts +172 -0
- package/dist/lib/admin/dnsProvidersApi.js +70 -0
- package/dist/lib/admin/domainsApi.d.ts +116 -1
- package/dist/lib/admin/domainsApi.js +33 -0
- package/dist/lib/admin/integrationsApi.d.ts +75 -0
- package/dist/lib/admin/integrationsApi.js +31 -0
- package/lib/admin/configKeysApi.ts +117 -0
- package/lib/admin/dnsProvidersApi.ts +241 -0
- package/lib/admin/domainsApi.ts +145 -1
- package/lib/admin/integrationsApi.ts +107 -0
- package/package.json +38 -37
package/lib/admin/domainsApi.ts
CHANGED
|
@@ -31,6 +31,24 @@ export interface Domain {
|
|
|
31
31
|
/** When set, this domain is a pure alias of the domain named here - it has no mailboxes of its own; mail
|
|
32
32
|
* addressed to it is delivered to the matching mailbox on the domain it aliases instead. */
|
|
33
33
|
aliasOf?: string;
|
|
34
|
+
/** Where the domain came from: `"seed"` for one the server added at start-up from the installation's own list, `"console"` for one added through
|
|
35
|
+
* the API. Unset on a domain from before this was recorded, which counts as added through the console. Server-managed. */
|
|
36
|
+
origin?: "seed" | "console" | null;
|
|
37
|
+
/** The DNS provider config (`system/dns-providers`) that publishes this domain's records, `null` or unset for "manual": the administrator adds
|
|
38
|
+
* them by hand. Changes only through `assignDnsProvider()`. */
|
|
39
|
+
dnsProviderUid?: string | null;
|
|
40
|
+
/** Whether RapidMX owns the domain's root TXT records (to publish SPF there) and which other values it keeps in them. Unset means not managed;
|
|
41
|
+
* `keep` and `spf` stay after an opt-out. Changes only through `updateApexTxt()`. */
|
|
42
|
+
apexTxt?: ApexTxtSettings | null;
|
|
43
|
+
/** The DKIM key rotation in progress, if any (`getDkimRotation()` is the console's view of it). Server-managed: changes only through `rotateDkim()`,
|
|
44
|
+
* `cancelDkimRotation()` and `setDkimRotationPaused()`. */
|
|
45
|
+
dkimRotation?: DkimRotationState | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ApexTxtSettings {
|
|
49
|
+
managed: boolean;
|
|
50
|
+
keep: string[];
|
|
51
|
+
spf?: string;
|
|
34
52
|
}
|
|
35
53
|
|
|
36
54
|
/** `client`, given by every function below, is an explicit `ApiClient` from `createApiClient()` (e.g. one
|
|
@@ -90,7 +108,36 @@ export function verifyDomain(uid: string, client?: ApiClient): Promise<Domain> {
|
|
|
90
108
|
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/verify`, { method: "POST" });
|
|
91
109
|
}
|
|
92
110
|
|
|
93
|
-
export type DnsRecordType =
|
|
111
|
+
export type DnsRecordType =
|
|
112
|
+
| "ownership"
|
|
113
|
+
| "mx"
|
|
114
|
+
| "spf"
|
|
115
|
+
| "dkim"
|
|
116
|
+
| "dkim_next"
|
|
117
|
+
| "dkim_previous"
|
|
118
|
+
| "dmarc"
|
|
119
|
+
| "openpgpkey_cname"
|
|
120
|
+
| "autodiscover_cname"
|
|
121
|
+
| "autodiscover_srv";
|
|
122
|
+
|
|
123
|
+
/** How a record is published: `published` (the DNS controller applied it and the live lookup sees it), `pending`, `conflict` (a record this deployment
|
|
124
|
+
* doesn't own is there) or `manual` (the administrator adds it, see `DnsManualReason`). */
|
|
125
|
+
export type DnsPublicationState = "published" | "pending" | "conflict" | "manual";
|
|
126
|
+
|
|
127
|
+
/** Why a published record is in conflict: a record of the same type is there that this deployment doesn't own (`not_owned`, which a takeover can
|
|
128
|
+
* overwrite), or records of another type that can't stand next to it (`type_mismatch`: a CNAME wanted where other records are, or other records wanted
|
|
129
|
+
* where a CNAME is), which RapidMX never deletes or takes over and the administrator deletes at their DNS host. */
|
|
130
|
+
export type DnsConflictReason = "not_owned" | "type_mismatch";
|
|
131
|
+
|
|
132
|
+
/** A live record in the way of a published one (`type_mismatch`): its type and its value as text. */
|
|
133
|
+
export interface DnsExistingRecord {
|
|
134
|
+
type: "A" | "AAAA" | "CNAME" | "MX" | "TXT" | "SRV";
|
|
135
|
+
value: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Why a record is `manual`: no provider is assigned, the provider can't hold the record's kind, the name lies outside the provider's zones, or the
|
|
139
|
+
* record belongs to the root TXT set, which RapidMX manages only on the administrator's opt-in. */
|
|
140
|
+
export type DnsManualReason = "no_provider" | "kind_unsupported" | "outside_zones" | "apex_txt_unmanaged";
|
|
94
141
|
|
|
95
142
|
/** One mail-related DNS record this server recommends for a domain, live-checked against real DNS —
|
|
96
143
|
* purely diagnostic, never mutates anything (unlike `verifyDomain`). The two `autodiscover_*` types are only
|
|
@@ -106,8 +153,105 @@ export interface DnsRecordCheck {
|
|
|
106
153
|
found: boolean;
|
|
107
154
|
matches: boolean;
|
|
108
155
|
actualValue?: string;
|
|
156
|
+
/** Whether, and how, the DNS controller publishes this record. Present only on a server that publishes, and only for a configured record. */
|
|
157
|
+
publication?: DnsPublicationState;
|
|
158
|
+
manualReason?: DnsManualReason;
|
|
159
|
+
/** With `publication: "conflict"`: why. A server that doesn't say means `not_owned`. */
|
|
160
|
+
conflictReason?: DnsConflictReason;
|
|
161
|
+
/** With `conflictReason: "type_mismatch"`: the live records the administrator has to delete. */
|
|
162
|
+
existing?: DnsExistingRecord[];
|
|
163
|
+
/** `ownership` only: the token is (still) at the domain's root, where it lived before `_rapidmx-verify.<domain>`. */
|
|
164
|
+
legacyApex?: boolean;
|
|
109
165
|
}
|
|
110
166
|
|
|
111
167
|
export function getDnsSetup(uid: string, client?: ApiClient): Promise<DnsRecordCheck[]> {
|
|
112
168
|
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/dns-setup`);
|
|
113
169
|
}
|
|
170
|
+
|
|
171
|
+
/** Assigns the domain to a DNS provider config, or back to manual (`null`). Needs an elevated token; the provider must cover the domain more specifically
|
|
172
|
+
* than any other (a 400 otherwise). Answers the domain. */
|
|
173
|
+
export function assignDnsProvider(uid: string, dnsProviderUid: string | null, client?: ApiClient): Promise<Domain> {
|
|
174
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/dns-provider`, {
|
|
175
|
+
method: "PUT",
|
|
176
|
+
body: JSON.stringify({ dnsProviderUid }),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** What `GET /:id/apex-txt` answers: the managed state and what the opt-in form starts from. */
|
|
181
|
+
export interface ApexTxtView {
|
|
182
|
+
managed: boolean;
|
|
183
|
+
keep: string[];
|
|
184
|
+
spf?: string;
|
|
185
|
+
/** The values at the domain's root now, as a live lookup sees them. */
|
|
186
|
+
current: string[];
|
|
187
|
+
/** `current` without SPF records and RapidMX ownership tokens: what the list of values to keep starts as. */
|
|
188
|
+
suggestedKeep: string[];
|
|
189
|
+
/** The lookup couldn't be answered, so `current` and `suggestedKeep` say nothing about what is there: the opt-in must not be offered. */
|
|
190
|
+
lookupFailed?: true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function getApexTxt(uid: string, client?: ApiClient): Promise<ApexTxtView> {
|
|
194
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/apex-txt`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Opts the domain's root TXT records into RapidMX's management (`managed: true` with the values to keep), edits the kept values while managed, or
|
|
198
|
+
* opts out (`managed: false`). Needs an elevated token. Answers the domain. */
|
|
199
|
+
export function updateApexTxt(uid: string, input: { managed: boolean; keep?: string[] }, client?: ApiClient): Promise<Domain> {
|
|
200
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/apex-txt`, { method: "PUT", body: JSON.stringify(input) });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Why a DKIM key rotation began: the schedule, an administrator's "Rotate now", or a key reported compromised (no publish wait, no overlap). */
|
|
204
|
+
export type DkimRotationReason = "scheduled" | "manual" | "compromised";
|
|
205
|
+
|
|
206
|
+
/** A domain's DKIM key rotation, as `Domain.dkimRotation` holds it. `idle`: none in progress. `pending`: the new key (`next`, whose `publicKey` is `""`
|
|
207
|
+
* until it is generated) is published and signing waits for it to be live (`liveSince`) and the publish wait to pass. `switching`: signing switched to the
|
|
208
|
+
* new key (`switchedAt`) and the mail server hasn't confirmed it yet. `overlap`: confirmed; the old key (`previous`) stays published until its
|
|
209
|
+
* `retireAt`. */
|
|
210
|
+
export interface DkimRotationState {
|
|
211
|
+
phase: "idle" | "pending" | "switching" | "overlap";
|
|
212
|
+
next?: { selector: string; publicKey: string; createdAt: string; liveSince?: string; reason: DkimRotationReason };
|
|
213
|
+
previous?: { selector: string; publicKey: string; retireAt?: string };
|
|
214
|
+
/** The reason of the rotation that switched, kept until it is retired. */
|
|
215
|
+
reason?: DkimRotationReason;
|
|
216
|
+
switchedAt?: string;
|
|
217
|
+
lastRotatedAt?: string;
|
|
218
|
+
lastCancelledAt?: string;
|
|
219
|
+
/** Scheduled rotation is paused for this domain. */
|
|
220
|
+
paused?: boolean;
|
|
221
|
+
/** What is holding the rotation up: no confirmation from the mail server, a key that couldn't be generated. */
|
|
222
|
+
lastError?: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Why a domain's key can't rotate: the key provider doesn't rotate, outbound mail is signed by SES, the domain has no key yet, or its key was imported by
|
|
226
|
+
* hand or can't be told from one. */
|
|
227
|
+
export type DkimRotationBlocker = "unsupported" | "ses" | "no_key" | "imported" | "unknown_key";
|
|
228
|
+
|
|
229
|
+
/** What `GET /:id/dkim/rotation` answers. */
|
|
230
|
+
export interface DkimRotationView {
|
|
231
|
+
rotation: DkimRotationState | null;
|
|
232
|
+
rotatable: boolean;
|
|
233
|
+
blocker?: DkimRotationBlocker;
|
|
234
|
+
/** When the next scheduled rotation starts; only while none is in progress, the domain isn't paused and scheduled rotation is on. */
|
|
235
|
+
nextScheduledAt?: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function getDkimRotation(uid: string, client?: ApiClient): Promise<DkimRotationView> {
|
|
239
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/dkim/rotation`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Starts a rotation of the domain's DKIM key ("Rotate now"). With `compromised`, the publish wait and the overlap are skipped, and during a rotation it
|
|
243
|
+
* reports the key being replaced as compromised. Needs an elevated token. Answers the domain. */
|
|
244
|
+
export function rotateDkim(uid: string, input: { compromised?: boolean } = {}, client?: ApiClient): Promise<Domain> {
|
|
245
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/dkim/rotate`, { method: "POST", body: JSON.stringify(input) });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Abandons a rotation the mail server hasn't confirmed (a switch not yet confirmed is reverted); a 409 once it has. Needs an elevated token. Answers the
|
|
249
|
+
* domain. */
|
|
250
|
+
export function cancelDkimRotation(uid: string, client?: ApiClient): Promise<Domain> {
|
|
251
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/dkim/rotation/cancel`, { method: "POST" });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Pauses or resumes scheduled rotation for the domain; a rotation in progress still finishes. Needs an elevated token. Answers the domain. */
|
|
255
|
+
export function setDkimRotationPaused(uid: string, paused: boolean, client?: ApiClient): Promise<Domain> {
|
|
256
|
+
return withClient(client, `/mail/domains/${encodeURIComponent(uid)}/dkim/rotation`, { method: "PUT", body: JSON.stringify({ paused }) });
|
|
257
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Typed wrappers over `@rapidmx/restapi`'s `BaseIntegrationsRoute` (mounted at `system/integrations`): the third-party services an administrator
|
|
7
|
+
* chooses (Giphy, STUN, the S/MIME signing CA, the DNS resolver, the plugin registry), each section overriding some of the deployment's configuration.
|
|
8
|
+
* `GET` needs a trusted role; every write a trusted role and an elevated token. Secrets are write-only: the server answers them only as the mask.
|
|
9
|
+
*/
|
|
10
|
+
import { ApiClient, withClient } from "../util/api.js";
|
|
11
|
+
|
|
12
|
+
/** What the server shows in place of a secret that is set (restapi `SecretMask`); sent back, it means "keep the saved value". `unreadable` is the
|
|
13
|
+
* server saying that this copy can't decrypt it (a key missing from its keyring, after a restore with an older one): never sent back. */
|
|
14
|
+
export interface SecretMask {
|
|
15
|
+
secret: true;
|
|
16
|
+
unreadable?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Whether `value` is the mask: an object whose keys are `secret`, set to `true`, and at most `unreadable`, a flag. */
|
|
20
|
+
export function isSecretMask(value: unknown): value is SecretMask {
|
|
21
|
+
if (typeof value !== "object" || value === null || (value as SecretMask).secret !== true) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const unreadable: unknown = (value as SecretMask).unreadable;
|
|
25
|
+
return Object.keys(value).length === (unreadable === undefined ? 1 : 2) && (unreadable === undefined || typeof unreadable === "boolean");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Whether `value` is the mask of a secret that is set but that the server can't read: it must be entered again. */
|
|
29
|
+
export function isUnreadableSecret(value: unknown): boolean {
|
|
30
|
+
return isSecretMask(value) && value.unreadable === true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type IntegrationSectionName = "giphy" | "stun" | "signingCa" | "dnsResolver" | "pluginRegistry";
|
|
34
|
+
|
|
35
|
+
/** Where a field's value comes from: saved by an administrator here, or the deployment's configuration. */
|
|
36
|
+
export type IntegrationSource = "admin" | "deployment";
|
|
37
|
+
|
|
38
|
+
/** One section as the server shows it (restapi `IntegrationSectionView`): the values in effect, the deployment's own values (what a reset puts a field
|
|
39
|
+
* back to) and where each value comes from. A secret is the mask when set, else `null`. */
|
|
40
|
+
export interface IntegrationSectionView<V> {
|
|
41
|
+
values: V;
|
|
42
|
+
defaults: V;
|
|
43
|
+
source: Record<keyof V, IntegrationSource>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface GiphyIntegration {
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
apiKey: SecretMask | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface StunIntegration {
|
|
52
|
+
/** `null`: the meet plugin's built-in list. */
|
|
53
|
+
urls: string[] | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type SigningCaBackend = "rfc8823" | "manual" | "none";
|
|
57
|
+
|
|
58
|
+
export interface SigningCaIntegration {
|
|
59
|
+
backend: SigningCaBackend;
|
|
60
|
+
directoryUrl: string;
|
|
61
|
+
contactEmail: string | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type DnsResolverMode = "node" | "doh-dnssec";
|
|
65
|
+
|
|
66
|
+
export interface DnsResolverIntegration {
|
|
67
|
+
mode: DnsResolverMode;
|
|
68
|
+
dohEndpoint: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A plugin namespace as the view shows it: its token only as the mask. Sent back, `token` is the mask (keep), a new value, or left out (none). */
|
|
72
|
+
export interface IntegrationPluginNamespace {
|
|
73
|
+
name: string;
|
|
74
|
+
registry?: string;
|
|
75
|
+
token?: SecretMask | string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface PluginRegistryIntegration {
|
|
79
|
+
url: string;
|
|
80
|
+
token: SecretMask | null;
|
|
81
|
+
namespaces: IntegrationPluginNamespace[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** What `GET /system/integrations` and every write answer (restapi `IntegrationSettingsResponse`). */
|
|
85
|
+
export interface IntegrationSettings {
|
|
86
|
+
giphy: IntegrationSectionView<GiphyIntegration>;
|
|
87
|
+
stun: IntegrationSectionView<StunIntegration>;
|
|
88
|
+
signingCa: IntegrationSectionView<SigningCaIntegration>;
|
|
89
|
+
dnsResolver: IntegrationSectionView<DnsResolverIntegration>;
|
|
90
|
+
pluginRegistry: IntegrationSectionView<PluginRegistryIntegration>;
|
|
91
|
+
/** The saved settings' version (0 before anything was saved), sent back with a change so one made against an older version is refused (409). */
|
|
92
|
+
version: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** A section's change: a field left out is unchanged, `null` puts it back to the deployment's value, a secret sent back as the mask is kept. */
|
|
96
|
+
export type IntegrationSectionPatch = Record<string, unknown> & { version?: number };
|
|
97
|
+
|
|
98
|
+
/** `client`, given by every function below, is an explicit `ApiClient` from `createApiClient()` (e.g. one
|
|
99
|
+
* account of a multi-account app) to call instead of the default global `apiFetch()` - see `withClient()`'s
|
|
100
|
+
* own doc comment in `util/api.ts`. Omitted (the default), every function here behaves exactly as before. */
|
|
101
|
+
export function getIntegrations(client?: ApiClient): Promise<IntegrationSettings> {
|
|
102
|
+
return withClient(client, "/system/integrations");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function updateIntegrationSection(section: IntegrationSectionName, patch: IntegrationSectionPatch, client?: ApiClient): Promise<IntegrationSettings> {
|
|
106
|
+
return withClient(client, `/system/integrations/${section}`, { method: "PUT", body: JSON.stringify(patch) });
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rapidmx/web-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "RapidMX's webmail and admin console React UI for web-based clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/rapidmx/web-client",
|
|
@@ -44,58 +44,59 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@dnd-kit/core": "^6.3.1",
|
|
46
46
|
"@emoji-mart/data": "^1.2.1",
|
|
47
|
-
"@journeyapps/wa-sqlite": "^2.0.
|
|
48
|
-
"@peculiar/
|
|
49
|
-
"@
|
|
50
|
-
"@tiptap/
|
|
51
|
-
"@tiptap/extension-
|
|
52
|
-
"@tiptap/extension-
|
|
53
|
-
"@tiptap/extension-
|
|
54
|
-
"@tiptap/extension-
|
|
55
|
-
"@tiptap/extension-text-
|
|
56
|
-
"@tiptap/
|
|
57
|
-
"@tiptap/
|
|
58
|
-
"@tiptap/
|
|
59
|
-
"@tiptap/
|
|
60
|
-
"
|
|
47
|
+
"@journeyapps/wa-sqlite": "^2.0.10",
|
|
48
|
+
"@peculiar/asn1-schema": "^3.0.0",
|
|
49
|
+
"@peculiar/x509": "^3.1.0",
|
|
50
|
+
"@tiptap/core": "^3.32.2",
|
|
51
|
+
"@tiptap/extension-highlight": "^3.32.2",
|
|
52
|
+
"@tiptap/extension-image": "^3.32.2",
|
|
53
|
+
"@tiptap/extension-placeholder": "^3.32.2",
|
|
54
|
+
"@tiptap/extension-table": "^3.32.2",
|
|
55
|
+
"@tiptap/extension-text-align": "^3.32.2",
|
|
56
|
+
"@tiptap/extension-text-style": "^3.32.2",
|
|
57
|
+
"@tiptap/extensions": "^3.32.2",
|
|
58
|
+
"@tiptap/pm": "^3.32.2",
|
|
59
|
+
"@tiptap/react": "^3.32.2",
|
|
60
|
+
"@tiptap/starter-kit": "^3.32.2",
|
|
61
|
+
"asn1js": "^4.0.0",
|
|
61
62
|
"date-fns": "^4.4.0",
|
|
62
63
|
"dompurify": "^3.4.17",
|
|
63
64
|
"hash-wasm": "^4.12.0",
|
|
64
65
|
"openpgp": "^6.3.2",
|
|
65
|
-
"pkijs": "^
|
|
66
|
+
"pkijs": "^4.0.1",
|
|
66
67
|
"react-icons": "^5.7.0",
|
|
67
68
|
"reflect-metadata": "^0.2.2",
|
|
68
69
|
"rrule": "^2.8.1"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@openpgp/web-stream-tools": "~0.3.1",
|
|
72
|
-
"@rapidrest/react": "^2.
|
|
73
|
-
"@tailwindcss/vite": "^4.3.
|
|
74
|
-
"@testing-library/dom": "^10.4.
|
|
73
|
+
"@rapidrest/react": "^2.3.0",
|
|
74
|
+
"@tailwindcss/vite": "^4.3.4",
|
|
75
|
+
"@testing-library/dom": "^10.4.2",
|
|
75
76
|
"@testing-library/jest-dom": "^7.0.1",
|
|
76
77
|
"@testing-library/react": "^16.3.3",
|
|
77
|
-
"@testing-library/user-event": "^14.6.
|
|
78
|
-
"@types/node": "^26.
|
|
79
|
-
"@types/react": "^19.
|
|
80
|
-
"@types/react-dom": "^19.
|
|
81
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
82
|
-
"@typescript-eslint/parser": "^8.
|
|
83
|
-
"@vitejs/plugin-react": "^6.
|
|
84
|
-
"@vitest/coverage-v8": "^5.
|
|
85
|
-
"eslint": "^10.
|
|
78
|
+
"@testing-library/user-event": "^14.6.8",
|
|
79
|
+
"@types/node": "^26.6.6",
|
|
80
|
+
"@types/react": "^19.3.0",
|
|
81
|
+
"@types/react-dom": "^19.3.0",
|
|
82
|
+
"@typescript-eslint/eslint-plugin": "^8.72.1",
|
|
83
|
+
"@typescript-eslint/parser": "^8.72.1",
|
|
84
|
+
"@vitejs/plugin-react": "^6.2.1",
|
|
85
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
86
|
+
"eslint": "^10.14.0",
|
|
86
87
|
"eslint-plugin-import": "^2.32.0",
|
|
87
|
-
"eslint-plugin-jsdoc": "^
|
|
88
|
-
"globals": "^17.
|
|
89
|
-
"jsdom": "^30.
|
|
90
|
-
"react": "^19.
|
|
91
|
-
"react-dom": "^19.
|
|
92
|
-
"rimraf": "^6.1.
|
|
88
|
+
"eslint-plugin-jsdoc": "^65.4.3",
|
|
89
|
+
"globals": "^17.14.0",
|
|
90
|
+
"jsdom": "^30.1.3",
|
|
91
|
+
"react": "^19.3.1",
|
|
92
|
+
"react-dom": "^19.3.1",
|
|
93
|
+
"rimraf": "^6.1.4",
|
|
93
94
|
"shx": "^0.4.0",
|
|
94
|
-
"tailwindcss": "^4.3.
|
|
95
|
-
"tsx": "^4.23.
|
|
95
|
+
"tailwindcss": "^4.3.4",
|
|
96
|
+
"tsx": "^4.23.15",
|
|
96
97
|
"typescript": "^6.0.3",
|
|
97
98
|
"vite": "^8.3.5",
|
|
98
|
-
"vitest": "^5.
|
|
99
|
+
"vitest": "^5.1.0"
|
|
99
100
|
},
|
|
100
101
|
"resolutions": {
|
|
101
102
|
"brace-expansion@npm:^1.1.7": "^1.1.21",
|