@serve.zone/dcrouter 13.7.1 → 13.9.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/dist_serve/bundle.js +1763 -1519
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/acme/index.d.ts +1 -0
- package/dist_ts/acme/index.js +2 -0
- package/dist_ts/acme/manager.acme-config.d.ts +48 -0
- package/dist_ts/acme/manager.acme-config.js +156 -0
- package/dist_ts/classes.dcrouter.d.ts +2 -0
- package/dist_ts/classes.dcrouter.js +60 -21
- package/dist_ts/db/documents/classes.acme-config.doc.d.ts +22 -0
- package/dist_ts/db/documents/classes.acme-config.doc.js +121 -0
- package/dist_ts/db/documents/index.d.ts +1 -0
- package/dist_ts/db/documents/index.js +3 -1
- package/dist_ts/dns/manager.dns.d.ts +17 -15
- package/dist_ts/dns/manager.dns.js +33 -27
- package/dist_ts/dns/providers/factory.js +10 -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/acme-config.handler.d.ts +16 -0
- package/dist_ts/opsserver/handlers/acme-config.handler.js +77 -0
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +42 -5
- package/dist_ts/opsserver/handlers/domain.handler.js +3 -3
- package/dist_ts/opsserver/handlers/index.d.ts +1 -0
- package/dist_ts/opsserver/handlers/index.js +2 -1
- package/dist_ts_interfaces/data/acme-config.d.ts +25 -0
- package/dist_ts_interfaces/data/acme-config.js +2 -0
- package/dist_ts_interfaces/data/dns-provider.d.ts +28 -4
- package/dist_ts_interfaces/data/dns-provider.js +15 -1
- package/dist_ts_interfaces/data/dns-record.d.ts +9 -7
- package/dist_ts_interfaces/data/domain.d.ts +8 -7
- 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 +1 -1
- package/dist_ts_interfaces/requests/acme-config.d.ts +42 -0
- package/dist_ts_interfaces/requests/acme-config.js +2 -0
- package/dist_ts_interfaces/requests/dns-records.d.ts +1 -1
- package/dist_ts_interfaces/requests/domains.d.ts +3 -3
- package/dist_ts_interfaces/requests/index.d.ts +1 -0
- package/dist_ts_interfaces/requests/index.js +2 -1
- package/dist_ts_migrations/index.js +17 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +16 -1
- package/dist_ts_web/appstate.js +61 -2
- package/dist_ts_web/elements/domains/dns-provider-form.d.ts +4 -2
- package/dist_ts_web/elements/domains/dns-provider-form.js +11 -5
- package/dist_ts_web/elements/domains/ops-view-certificates.d.ts +3 -0
- package/dist_ts_web/elements/domains/ops-view-certificates.js +208 -4
- package/dist_ts_web/elements/domains/ops-view-dns.js +3 -3
- package/dist_ts_web/elements/domains/ops-view-domains.d.ts +1 -1
- package/dist_ts_web/elements/domains/ops-view-domains.js +10 -10
- package/dist_ts_web/elements/domains/ops-view-providers.js +19 -5
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/acme/index.ts +1 -0
- package/ts/acme/manager.acme-config.ts +182 -0
- package/ts/classes.dcrouter.ts +74 -26
- package/ts/db/documents/classes.acme-config.doc.ts +49 -0
- package/ts/db/documents/index.ts +3 -0
- package/ts/dns/manager.dns.ts +38 -27
- package/ts/dns/providers/factory.ts +11 -0
- package/ts/opsserver/classes.opsserver.ts +2 -0
- package/ts/opsserver/handlers/acme-config.handler.ts +94 -0
- package/ts/opsserver/handlers/dns-provider.handler.ts +41 -3
- package/ts/opsserver/handlers/domain.handler.ts +2 -2
- package/ts/opsserver/handlers/index.ts +2 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +89 -1
- package/ts_web/elements/domains/dns-provider-form.ts +12 -4
- package/ts_web/elements/domains/ops-view-certificates.ts +205 -2
- package/ts_web/elements/domains/ops-view-dns.ts +2 -2
- package/ts_web/elements/domains/ops-view-domains.ts +9 -9
- package/ts_web/elements/domains/ops-view-providers.ts +18 -4
|
@@ -23,17 +23,25 @@ export class OpsViewCertificates extends DeesElement {
|
|
|
23
23
|
@state()
|
|
24
24
|
accessor certState: appstate.ICertificateState = appstate.certificateStatePart.getState()!;
|
|
25
25
|
|
|
26
|
+
@state()
|
|
27
|
+
accessor acmeState: appstate.IAcmeConfigState = appstate.acmeConfigStatePart.getState()!;
|
|
28
|
+
|
|
26
29
|
constructor() {
|
|
27
30
|
super();
|
|
28
|
-
const
|
|
31
|
+
const certSub = appstate.certificateStatePart.select().subscribe((newState) => {
|
|
29
32
|
this.certState = newState;
|
|
30
33
|
});
|
|
31
|
-
this.rxSubscriptions.push(
|
|
34
|
+
this.rxSubscriptions.push(certSub);
|
|
35
|
+
const acmeSub = appstate.acmeConfigStatePart.select().subscribe((newState) => {
|
|
36
|
+
this.acmeState = newState;
|
|
37
|
+
});
|
|
38
|
+
this.rxSubscriptions.push(acmeSub);
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
async connectedCallback() {
|
|
35
42
|
await super.connectedCallback();
|
|
36
43
|
await appstate.certificateStatePart.dispatchAction(appstate.fetchCertificateOverviewAction, null);
|
|
44
|
+
await appstate.acmeConfigStatePart.dispatchAction(appstate.fetchAcmeConfigAction, null);
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
public static styles = [
|
|
@@ -46,6 +54,62 @@ export class OpsViewCertificates extends DeesElement {
|
|
|
46
54
|
gap: 24px;
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
.acmeCard {
|
|
58
|
+
padding: 16px 20px;
|
|
59
|
+
background: ${cssManager.bdTheme('#f9fafb', '#111827')};
|
|
60
|
+
border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')};
|
|
61
|
+
border-radius: 8px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.acmeCard.acmeCardEmpty {
|
|
65
|
+
background: ${cssManager.bdTheme('#fffbeb', '#1c1917')};
|
|
66
|
+
border-color: ${cssManager.bdTheme('#fde68a', '#78350f')};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.acmeCardHeader {
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: space-between;
|
|
72
|
+
align-items: center;
|
|
73
|
+
margin-bottom: 12px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.acmeCardTitle {
|
|
77
|
+
font-size: 14px;
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
color: ${cssManager.bdTheme('#111827', '#f3f4f6')};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.acmeGrid {
|
|
83
|
+
display: grid;
|
|
84
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
85
|
+
gap: 12px 24px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.acmeField {
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
gap: 2px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.acmeLabel {
|
|
95
|
+
font-size: 11px;
|
|
96
|
+
text-transform: uppercase;
|
|
97
|
+
letter-spacing: 0.03em;
|
|
98
|
+
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.acmeValue {
|
|
102
|
+
font-size: 13px;
|
|
103
|
+
color: ${cssManager.bdTheme('#111827', '#f3f4f6')};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.acmeEmptyHint {
|
|
107
|
+
margin: 0;
|
|
108
|
+
font-size: 13px;
|
|
109
|
+
line-height: 1.5;
|
|
110
|
+
color: ${cssManager.bdTheme('#78350f', '#fde68a')};
|
|
111
|
+
}
|
|
112
|
+
|
|
49
113
|
.statusBadge {
|
|
50
114
|
display: inline-flex;
|
|
51
115
|
align-items: center;
|
|
@@ -162,12 +226,151 @@ export class OpsViewCertificates extends DeesElement {
|
|
|
162
226
|
<dees-heading level="3">Certificates</dees-heading>
|
|
163
227
|
|
|
164
228
|
<div class="certificatesContainer">
|
|
229
|
+
${this.renderAcmeSettingsCard()}
|
|
165
230
|
${this.renderStatsTiles(summary)}
|
|
166
231
|
${this.renderCertificateTable()}
|
|
167
232
|
</div>
|
|
168
233
|
`;
|
|
169
234
|
}
|
|
170
235
|
|
|
236
|
+
private renderAcmeSettingsCard(): TemplateResult {
|
|
237
|
+
const config = this.acmeState.config;
|
|
238
|
+
|
|
239
|
+
if (!config) {
|
|
240
|
+
return html`
|
|
241
|
+
<div class="acmeCard acmeCardEmpty">
|
|
242
|
+
<div class="acmeCardHeader">
|
|
243
|
+
<span class="acmeCardTitle">ACME Settings</span>
|
|
244
|
+
<dees-button
|
|
245
|
+
eventName="edit-acme"
|
|
246
|
+
@click=${() => this.showEditAcmeDialog()}
|
|
247
|
+
.type=${'highlighted'}
|
|
248
|
+
>Configure</dees-button>
|
|
249
|
+
</div>
|
|
250
|
+
<p class="acmeEmptyHint">
|
|
251
|
+
No ACME configuration yet. Click <strong>Configure</strong> to set up automated TLS
|
|
252
|
+
certificate issuance via Let's Encrypt. You'll also need at least one DNS provider
|
|
253
|
+
under <strong>Domains > Providers</strong>.
|
|
254
|
+
</p>
|
|
255
|
+
</div>
|
|
256
|
+
`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return html`
|
|
260
|
+
<div class="acmeCard">
|
|
261
|
+
<div class="acmeCardHeader">
|
|
262
|
+
<span class="acmeCardTitle">ACME Settings</span>
|
|
263
|
+
<dees-button eventName="edit-acme" @click=${() => this.showEditAcmeDialog()}>Edit</dees-button>
|
|
264
|
+
</div>
|
|
265
|
+
<div class="acmeGrid">
|
|
266
|
+
<div class="acmeField">
|
|
267
|
+
<span class="acmeLabel">Account email</span>
|
|
268
|
+
<span class="acmeValue">${config.accountEmail || '(not set)'}</span>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="acmeField">
|
|
271
|
+
<span class="acmeLabel">Status</span>
|
|
272
|
+
<span class="acmeValue">
|
|
273
|
+
<span class="statusBadge ${config.enabled ? 'valid' : 'unknown'}">
|
|
274
|
+
${config.enabled ? 'enabled' : 'disabled'}
|
|
275
|
+
</span>
|
|
276
|
+
</span>
|
|
277
|
+
</div>
|
|
278
|
+
<div class="acmeField">
|
|
279
|
+
<span class="acmeLabel">Mode</span>
|
|
280
|
+
<span class="acmeValue">
|
|
281
|
+
<span class="statusBadge ${config.useProduction ? 'valid' : 'provisioning'}">
|
|
282
|
+
${config.useProduction ? 'production' : 'staging'}
|
|
283
|
+
</span>
|
|
284
|
+
</span>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="acmeField">
|
|
287
|
+
<span class="acmeLabel">Auto-renew</span>
|
|
288
|
+
<span class="acmeValue">${config.autoRenew ? 'on' : 'off'}</span>
|
|
289
|
+
</div>
|
|
290
|
+
<div class="acmeField">
|
|
291
|
+
<span class="acmeLabel">Renewal threshold</span>
|
|
292
|
+
<span class="acmeValue">${config.renewThresholdDays} days</span>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private async showEditAcmeDialog() {
|
|
300
|
+
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
|
301
|
+
const current = this.acmeState.config;
|
|
302
|
+
|
|
303
|
+
DeesModal.createAndShow({
|
|
304
|
+
heading: current ? 'Edit ACME Settings' : 'Configure ACME',
|
|
305
|
+
content: html`
|
|
306
|
+
<dees-form>
|
|
307
|
+
<dees-input-text
|
|
308
|
+
.key=${'accountEmail'}
|
|
309
|
+
.label=${'Account email'}
|
|
310
|
+
.value=${current?.accountEmail ?? ''}
|
|
311
|
+
.required=${true}
|
|
312
|
+
></dees-input-text>
|
|
313
|
+
<dees-input-checkbox
|
|
314
|
+
.key=${'enabled'}
|
|
315
|
+
.label=${'Enabled'}
|
|
316
|
+
.value=${current?.enabled ?? true}
|
|
317
|
+
></dees-input-checkbox>
|
|
318
|
+
<dees-input-checkbox
|
|
319
|
+
.key=${'useProduction'}
|
|
320
|
+
.label=${"Use Let's Encrypt production (uncheck for staging)"}
|
|
321
|
+
.value=${current?.useProduction ?? true}
|
|
322
|
+
></dees-input-checkbox>
|
|
323
|
+
<dees-input-checkbox
|
|
324
|
+
.key=${'autoRenew'}
|
|
325
|
+
.label=${'Auto-renew certificates'}
|
|
326
|
+
.value=${current?.autoRenew ?? true}
|
|
327
|
+
></dees-input-checkbox>
|
|
328
|
+
<dees-input-text
|
|
329
|
+
.key=${'renewThresholdDays'}
|
|
330
|
+
.label=${'Renewal threshold (days)'}
|
|
331
|
+
.value=${String(current?.renewThresholdDays ?? 30)}
|
|
332
|
+
></dees-input-text>
|
|
333
|
+
</dees-form>
|
|
334
|
+
<p style="margin-top: 12px; font-size: 12px; opacity: 0.7;">
|
|
335
|
+
Most fields take effect on the next dcrouter restart (SmartAcme is instantiated once at
|
|
336
|
+
startup). Changing the account email creates a new Let's Encrypt account — only do this
|
|
337
|
+
if you know what you're doing.
|
|
338
|
+
</p>
|
|
339
|
+
`,
|
|
340
|
+
menuOptions: [
|
|
341
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
342
|
+
{
|
|
343
|
+
name: 'Save',
|
|
344
|
+
action: async (modalArg: any) => {
|
|
345
|
+
const form = modalArg.shadowRoot
|
|
346
|
+
?.querySelector('.content')
|
|
347
|
+
?.querySelector('dees-form');
|
|
348
|
+
if (!form) return;
|
|
349
|
+
const data = await form.collectFormData();
|
|
350
|
+
const email = String(data.accountEmail ?? '').trim();
|
|
351
|
+
if (!email) {
|
|
352
|
+
DeesToast.show({
|
|
353
|
+
message: 'Account email is required',
|
|
354
|
+
type: 'warning',
|
|
355
|
+
duration: 2500,
|
|
356
|
+
});
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const threshold = parseInt(String(data.renewThresholdDays ?? '30'), 10);
|
|
360
|
+
await appstate.acmeConfigStatePart.dispatchAction(appstate.updateAcmeConfigAction, {
|
|
361
|
+
accountEmail: email,
|
|
362
|
+
enabled: Boolean(data.enabled),
|
|
363
|
+
useProduction: Boolean(data.useProduction),
|
|
364
|
+
autoRenew: Boolean(data.autoRenew),
|
|
365
|
+
renewThresholdDays: Number.isFinite(threshold) ? threshold : 30,
|
|
366
|
+
});
|
|
367
|
+
modalArg.destroy();
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
171
374
|
private renderStatsTiles(summary: appstate.ICertificateState['summary']): TemplateResult {
|
|
172
375
|
const tiles: IStatsTile[] = [
|
|
173
376
|
{
|
|
@@ -80,7 +80,7 @@ export class OpsViewDns extends DeesElement {
|
|
|
80
80
|
font-weight: 500;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
.sourceBadge.
|
|
83
|
+
.sourceBadge.local {
|
|
84
84
|
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
|
85
85
|
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
|
86
86
|
}
|
|
@@ -184,7 +184,7 @@ export class OpsViewDns extends DeesElement {
|
|
|
184
184
|
private domainHint(domainId: string): string {
|
|
185
185
|
const domain = this.domainsState.domains.find((d) => d.id === domainId);
|
|
186
186
|
if (!domain) return '';
|
|
187
|
-
if (domain.source === '
|
|
187
|
+
if (domain.source === 'dcrouter') {
|
|
188
188
|
return 'Records are served by dcrouter (authoritative).';
|
|
189
189
|
}
|
|
190
190
|
return 'Records are stored at the provider — changes here are pushed via the provider API.';
|
|
@@ -55,7 +55,7 @@ export class OpsViewDomains extends DeesElement {
|
|
|
55
55
|
font-weight: 500;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
.sourceBadge.
|
|
58
|
+
.sourceBadge.dcrouter {
|
|
59
59
|
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
|
60
60
|
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
|
61
61
|
}
|
|
@@ -76,7 +76,7 @@ export class OpsViewDomains extends DeesElement {
|
|
|
76
76
|
<div class="domainsContainer">
|
|
77
77
|
<dees-table
|
|
78
78
|
.heading1=${'Domains'}
|
|
79
|
-
.heading2=${'Domains under management —
|
|
79
|
+
.heading2=${'Domains under management — served by dcrouter (authoritative) or imported from a provider'}
|
|
80
80
|
.data=${domains}
|
|
81
81
|
.showColumnFilters=${true}
|
|
82
82
|
.displayFunction=${(d: interfaces.data.IDomain) => ({
|
|
@@ -90,11 +90,11 @@ export class OpsViewDomains extends DeesElement {
|
|
|
90
90
|
})}
|
|
91
91
|
.dataActions=${[
|
|
92
92
|
{
|
|
93
|
-
name: 'Add
|
|
93
|
+
name: 'Add DcRouter Domain',
|
|
94
94
|
iconName: 'lucide:plus',
|
|
95
95
|
type: ['header' as const],
|
|
96
96
|
actionFunc: async () => {
|
|
97
|
-
await this.
|
|
97
|
+
await this.showCreateDcrouterDialog();
|
|
98
98
|
},
|
|
99
99
|
},
|
|
100
100
|
{
|
|
@@ -168,17 +168,17 @@ export class OpsViewDomains extends DeesElement {
|
|
|
168
168
|
d: interfaces.data.IDomain,
|
|
169
169
|
providersById: Map<string, interfaces.data.IDnsProviderPublic>,
|
|
170
170
|
): TemplateResult {
|
|
171
|
-
if (d.source === '
|
|
172
|
-
return html`<span class="sourceBadge
|
|
171
|
+
if (d.source === 'dcrouter') {
|
|
172
|
+
return html`<span class="sourceBadge dcrouter">DcRouter</span>`;
|
|
173
173
|
}
|
|
174
174
|
const provider = d.providerId ? providersById.get(d.providerId) : undefined;
|
|
175
175
|
return html`<span class="sourceBadge provider">${provider?.name || 'Provider'}</span>`;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
private async
|
|
178
|
+
private async showCreateDcrouterDialog() {
|
|
179
179
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
180
180
|
DeesModal.createAndShow({
|
|
181
|
-
heading: 'Add
|
|
181
|
+
heading: 'Add DcRouter Domain',
|
|
182
182
|
content: html`
|
|
183
183
|
<dees-form>
|
|
184
184
|
<dees-input-text .key=${'name'} .label=${'FQDN (e.g. example.com)'} .required=${true}></dees-input-text>
|
|
@@ -199,7 +199,7 @@ export class OpsViewDomains extends DeesElement {
|
|
|
199
199
|
?.querySelector('dees-form');
|
|
200
200
|
if (!form) return;
|
|
201
201
|
const data = await form.collectFormData();
|
|
202
|
-
await appstate.domainsStatePart.dispatchAction(appstate.
|
|
202
|
+
await appstate.domainsStatePart.dispatchAction(appstate.createDcrouterDomainAction, {
|
|
203
203
|
name: String(data.name),
|
|
204
204
|
description: data.description ? String(data.description) : undefined,
|
|
205
205
|
});
|
|
@@ -71,6 +71,11 @@ export class OpsViewProviders extends DeesElement {
|
|
|
71
71
|
background: ${cssManager.bdTheme('#f3f4f6', '#1f2937')};
|
|
72
72
|
color: ${cssManager.bdTheme('#4b5563', '#9ca3af')};
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
.statusBadge.builtin {
|
|
76
|
+
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
|
77
|
+
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
|
78
|
+
}
|
|
74
79
|
`,
|
|
75
80
|
];
|
|
76
81
|
|
|
@@ -82,15 +87,21 @@ export class OpsViewProviders extends DeesElement {
|
|
|
82
87
|
<div class="providersContainer">
|
|
83
88
|
<dees-table
|
|
84
89
|
.heading1=${'Providers'}
|
|
85
|
-
.heading2=${'
|
|
90
|
+
.heading2=${'Built-in dcrouter + external DNS provider accounts'}
|
|
86
91
|
.data=${providers}
|
|
87
92
|
.showColumnFilters=${true}
|
|
88
93
|
.displayFunction=${(p: interfaces.data.IDnsProviderPublic) => ({
|
|
89
94
|
Name: p.name,
|
|
90
95
|
Type: this.providerTypeLabel(p.type),
|
|
91
|
-
Status:
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
Status: p.builtIn
|
|
97
|
+
? html`<span class="statusBadge builtin">built-in</span>`
|
|
98
|
+
: this.renderStatusBadge(p.status),
|
|
99
|
+
'Last Tested': p.builtIn
|
|
100
|
+
? '—'
|
|
101
|
+
: p.lastTestedAt
|
|
102
|
+
? new Date(p.lastTestedAt).toLocaleString()
|
|
103
|
+
: 'never',
|
|
104
|
+
Error: p.builtIn ? '—' : p.lastError || '-',
|
|
94
105
|
})}
|
|
95
106
|
.dataActions=${[
|
|
96
107
|
{
|
|
@@ -116,6 +127,7 @@ export class OpsViewProviders extends DeesElement {
|
|
|
116
127
|
name: 'Test Connection',
|
|
117
128
|
iconName: 'lucide:plug',
|
|
118
129
|
type: ['inRow', 'contextmenu'] as any,
|
|
130
|
+
actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
|
|
119
131
|
actionFunc: async (actionData: any) => {
|
|
120
132
|
const provider = actionData.item as interfaces.data.IDnsProviderPublic;
|
|
121
133
|
await this.testProvider(provider);
|
|
@@ -125,6 +137,7 @@ export class OpsViewProviders extends DeesElement {
|
|
|
125
137
|
name: 'Edit',
|
|
126
138
|
iconName: 'lucide:pencil',
|
|
127
139
|
type: ['inRow', 'contextmenu'] as any,
|
|
140
|
+
actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
|
|
128
141
|
actionFunc: async (actionData: any) => {
|
|
129
142
|
const provider = actionData.item as interfaces.data.IDnsProviderPublic;
|
|
130
143
|
await this.showEditDialog(provider);
|
|
@@ -134,6 +147,7 @@ export class OpsViewProviders extends DeesElement {
|
|
|
134
147
|
name: 'Delete',
|
|
135
148
|
iconName: 'lucide:trash2',
|
|
136
149
|
type: ['inRow', 'contextmenu'] as any,
|
|
150
|
+
actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
|
|
137
151
|
actionFunc: async (actionData: any) => {
|
|
138
152
|
const provider = actionData.item as interfaces.data.IDnsProviderPublic;
|
|
139
153
|
await this.deleteProvider(provider);
|