@serve.zone/dcrouter 13.5.0 → 13.7.1
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 +1705 -1365
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +2 -5
- package/dist_ts/classes.dcrouter.js +41 -10
- package/dist_ts/db/documents/classes.dns-provider.doc.d.ts +22 -0
- package/dist_ts/db/documents/classes.dns-provider.doc.js +134 -0
- package/dist_ts/db/documents/classes.dns-record.doc.d.ts +21 -0
- package/dist_ts/db/documents/classes.dns-record.doc.js +143 -0
- package/dist_ts/db/documents/classes.domain.doc.d.ts +22 -0
- package/dist_ts/db/documents/classes.domain.doc.js +146 -0
- package/dist_ts/db/documents/index.d.ts +3 -0
- package/dist_ts/db/documents/index.js +5 -1
- package/dist_ts/dns/index.d.ts +2 -0
- package/dist_ts/dns/index.js +3 -0
- package/dist_ts/dns/manager.dns.d.ts +227 -0
- package/dist_ts/dns/manager.dns.js +747 -0
- package/dist_ts/dns/providers/cloudflare.provider.d.ts +21 -0
- package/dist_ts/dns/providers/cloudflare.provider.js +106 -0
- package/dist_ts/dns/providers/factory.d.ts +23 -0
- package/dist_ts/dns/providers/factory.js +38 -0
- package/dist_ts/dns/providers/index.d.ts +3 -0
- package/dist_ts/dns/providers/index.js +4 -0
- package/dist_ts/dns/providers/interfaces.d.ts +54 -0
- package/dist_ts/dns/providers/interfaces.js +2 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +3 -0
- package/dist_ts/opsserver/classes.opsserver.js +7 -1
- package/dist_ts/opsserver/handlers/config.handler.js +11 -2
- package/dist_ts/opsserver/handlers/dns-provider.handler.d.ts +16 -0
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +119 -0
- package/dist_ts/opsserver/handlers/dns-record.handler.d.ts +13 -0
- package/dist_ts/opsserver/handlers/dns-record.handler.js +98 -0
- package/dist_ts/opsserver/handlers/domain.handler.d.ts +13 -0
- package/dist_ts/opsserver/handlers/domain.handler.js +124 -0
- package/dist_ts/opsserver/handlers/index.d.ts +3 -0
- package/dist_ts/opsserver/handlers/index.js +4 -1
- package/dist_ts_interfaces/data/dns-provider.d.ts +112 -0
- package/dist_ts_interfaces/data/dns-provider.js +27 -0
- package/dist_ts_interfaces/data/dns-record.d.ts +40 -0
- package/dist_ts_interfaces/data/dns-record.js +2 -0
- package/dist_ts_interfaces/data/domain.d.ts +34 -0
- package/dist_ts_interfaces/data/domain.js +2 -0
- package/dist_ts_interfaces/data/index.d.ts +3 -0
- package/dist_ts_interfaces/data/index.js +4 -1
- package/dist_ts_interfaces/data/route-management.d.ts +1 -1
- package/dist_ts_interfaces/requests/dns-providers.d.ts +117 -0
- package/dist_ts_interfaces/requests/dns-providers.js +2 -0
- package/dist_ts_interfaces/requests/dns-records.d.ts +89 -0
- package/dist_ts_interfaces/requests/dns-records.js +2 -0
- package/dist_ts_interfaces/requests/domains.d.ts +118 -0
- package/dist_ts_interfaces/requests/domains.js +2 -0
- package/dist_ts_interfaces/requests/index.d.ts +3 -0
- package/dist_ts_interfaces/requests/index.js +4 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +72 -0
- package/dist_ts_web/appstate.js +308 -6
- package/dist_ts_web/elements/access/ops-view-apitokens.js +1 -1
- package/dist_ts_web/elements/access/ops-view-users.js +1 -1
- package/dist_ts_web/elements/domains/dns-provider-form.d.ts +58 -0
- package/dist_ts_web/elements/domains/dns-provider-form.js +268 -0
- package/dist_ts_web/elements/domains/index.d.ts +5 -0
- package/dist_ts_web/elements/domains/index.js +6 -0
- package/dist_ts_web/elements/{ops-view-certificates.d.ts → domains/ops-view-certificates.d.ts} +1 -1
- package/dist_ts_web/elements/{ops-view-certificates.js → domains/ops-view-certificates.js} +5 -5
- package/dist_ts_web/elements/domains/ops-view-dns.d.ts +17 -0
- package/dist_ts_web/elements/domains/ops-view-dns.js +304 -0
- package/dist_ts_web/elements/domains/ops-view-domains.d.ts +18 -0
- package/dist_ts_web/elements/domains/ops-view-domains.js +361 -0
- package/dist_ts_web/elements/domains/ops-view-providers.d.ts +21 -0
- package/dist_ts_web/elements/domains/ops-view-providers.js +316 -0
- package/dist_ts_web/elements/email/ops-view-email-security.js +1 -1
- package/dist_ts_web/elements/email/ops-view-emails.js +1 -1
- package/dist_ts_web/elements/index.d.ts +1 -1
- package/dist_ts_web/elements/index.js +2 -2
- package/dist_ts_web/elements/network/ops-view-network-activity.js +1 -1
- package/dist_ts_web/elements/network/ops-view-networktargets.js +1 -1
- package/dist_ts_web/elements/network/ops-view-remoteingress.js +1 -1
- package/dist_ts_web/elements/network/ops-view-routes.js +1 -1
- package/dist_ts_web/elements/network/ops-view-sourceprofiles.js +1 -1
- package/dist_ts_web/elements/network/ops-view-targetprofiles.js +1 -1
- package/dist_ts_web/elements/network/ops-view-vpn.js +1 -1
- package/dist_ts_web/elements/ops-dashboard.js +14 -5
- package/dist_ts_web/elements/ops-view-logs.js +1 -1
- package/dist_ts_web/elements/overview/ops-view-config.js +3 -3
- package/dist_ts_web/elements/overview/ops-view-overview.js +1 -1
- package/dist_ts_web/elements/security/ops-view-security-authentication.js +1 -1
- package/dist_ts_web/elements/security/ops-view-security-blocked.js +1 -1
- package/dist_ts_web/elements/security/ops-view-security-overview.js +1 -1
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +4 -2
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +46 -17
- package/ts/db/documents/classes.dns-provider.doc.ts +63 -0
- package/ts/db/documents/classes.dns-record.doc.ts +62 -0
- package/ts/db/documents/classes.domain.doc.ts +66 -0
- package/ts/db/documents/index.ts +5 -0
- package/ts/dns/index.ts +2 -0
- package/ts/dns/manager.dns.ts +869 -0
- package/ts/dns/providers/cloudflare.provider.ts +131 -0
- package/ts/dns/providers/factory.ts +48 -0
- package/ts/dns/providers/index.ts +3 -0
- package/ts/dns/providers/interfaces.ts +67 -0
- package/ts/opsserver/classes.opsserver.ts +6 -0
- package/ts/opsserver/handlers/config.handler.ts +10 -1
- package/ts/opsserver/handlers/dns-provider.handler.ts +159 -0
- package/ts/opsserver/handlers/dns-record.handler.ts +127 -0
- package/ts/opsserver/handlers/domain.handler.ts +161 -0
- package/ts/opsserver/handlers/index.ts +4 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +403 -5
- package/ts_web/elements/access/ops-view-apitokens.ts +1 -1
- package/ts_web/elements/access/ops-view-users.ts +1 -1
- package/ts_web/elements/domains/dns-provider-form.ts +216 -0
- package/ts_web/elements/domains/index.ts +5 -0
- package/ts_web/elements/{ops-view-certificates.ts → domains/ops-view-certificates.ts} +4 -4
- package/ts_web/elements/domains/ops-view-dns.ts +273 -0
- package/ts_web/elements/domains/ops-view-domains.ts +335 -0
- package/ts_web/elements/domains/ops-view-providers.ts +284 -0
- package/ts_web/elements/email/ops-view-email-security.ts +1 -1
- package/ts_web/elements/email/ops-view-emails.ts +1 -1
- package/ts_web/elements/index.ts +1 -1
- package/ts_web/elements/network/ops-view-network-activity.ts +1 -1
- package/ts_web/elements/network/ops-view-networktargets.ts +1 -1
- package/ts_web/elements/network/ops-view-remoteingress.ts +1 -1
- package/ts_web/elements/network/ops-view-routes.ts +1 -1
- package/ts_web/elements/network/ops-view-sourceprofiles.ts +1 -1
- package/ts_web/elements/network/ops-view-targetprofiles.ts +1 -1
- package/ts_web/elements/network/ops-view-vpn.ts +1 -1
- package/ts_web/elements/ops-dashboard.ts +14 -4
- package/ts_web/elements/ops-view-logs.ts +1 -1
- package/ts_web/elements/overview/ops-view-config.ts +2 -2
- package/ts_web/elements/overview/ops-view-overview.ts +1 -1
- package/ts_web/elements/security/ops-view-security-authentication.ts +1 -1
- package/ts_web/elements/security/ops-view-security-blocked.ts +1 -1
- package/ts_web/elements/security/ops-view-security-overview.ts +1 -1
- package/ts_web/router.ts +3 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeesElement,
|
|
3
|
+
html,
|
|
4
|
+
customElement,
|
|
5
|
+
type TemplateResult,
|
|
6
|
+
css,
|
|
7
|
+
state,
|
|
8
|
+
property,
|
|
9
|
+
cssManager,
|
|
10
|
+
} from '@design.estate/dees-element';
|
|
11
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
'dns-provider-form': DnsProviderForm;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Reactive credential form for a DNS provider. Renders the type picker
|
|
21
|
+
* and the credential fields for the currently-selected type.
|
|
22
|
+
*
|
|
23
|
+
* Provider-agnostic — driven entirely by `dnsProviderTypeDescriptors` from
|
|
24
|
+
* `ts_interfaces/data/dns-provider.ts`. Adding a new provider type means
|
|
25
|
+
* appending one entry to the descriptors array; this form picks it up
|
|
26
|
+
* automatically.
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
*
|
|
30
|
+
* const formEl = document.createElement('dns-provider-form');
|
|
31
|
+
* formEl.providerName = 'My provider';
|
|
32
|
+
* // ... pass element into a DeesModal as content ...
|
|
33
|
+
* // on submit:
|
|
34
|
+
* const data = formEl.collectData();
|
|
35
|
+
* // → { name, type, credentials }
|
|
36
|
+
*
|
|
37
|
+
* In edit mode, set `lockType = true` so the user cannot change provider
|
|
38
|
+
* type after creation (credentials shapes don't transfer between types).
|
|
39
|
+
*/
|
|
40
|
+
@customElement('dns-provider-form')
|
|
41
|
+
export class DnsProviderForm extends DeesElement {
|
|
42
|
+
/** Pre-populated provider name. */
|
|
43
|
+
@property({ type: String })
|
|
44
|
+
accessor providerName: string = '';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Currently selected provider type. Initialized to the first descriptor;
|
|
48
|
+
* caller can override before mounting (e.g. for edit dialogs).
|
|
49
|
+
*/
|
|
50
|
+
@state()
|
|
51
|
+
accessor selectedType: interfaces.data.TDnsProviderType =
|
|
52
|
+
interfaces.data.dnsProviderTypeDescriptors[0]?.type ?? 'cloudflare';
|
|
53
|
+
|
|
54
|
+
/** When true, hide the type picker — used in edit dialogs. */
|
|
55
|
+
@property({ type: Boolean })
|
|
56
|
+
accessor lockType: boolean = false;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Help text shown above credentials. Useful for edit dialogs to indicate
|
|
60
|
+
* that fields can be left blank to keep current values.
|
|
61
|
+
*/
|
|
62
|
+
@property({ type: String })
|
|
63
|
+
accessor credentialsHint: string = '';
|
|
64
|
+
|
|
65
|
+
/** Internal map of credential field values, keyed by the descriptor's `key`. */
|
|
66
|
+
@state()
|
|
67
|
+
accessor credentialValues: Record<string, string> = {};
|
|
68
|
+
|
|
69
|
+
public static styles = [
|
|
70
|
+
cssManager.defaultStyles,
|
|
71
|
+
css`
|
|
72
|
+
:host {
|
|
73
|
+
display: block;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.field {
|
|
77
|
+
margin-bottom: 12px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.helpText {
|
|
81
|
+
font-size: 12px;
|
|
82
|
+
opacity: 0.7;
|
|
83
|
+
margin-top: -6px;
|
|
84
|
+
margin-bottom: 8px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.typeDescription {
|
|
88
|
+
font-size: 12px;
|
|
89
|
+
opacity: 0.8;
|
|
90
|
+
margin: 4px 0 16px;
|
|
91
|
+
padding: 8px 12px;
|
|
92
|
+
background: ${cssManager.bdTheme('#f3f4f6', '#1f2937')};
|
|
93
|
+
border-radius: 6px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.credentialsHint {
|
|
97
|
+
font-size: 12px;
|
|
98
|
+
opacity: 0.7;
|
|
99
|
+
margin-bottom: 12px;
|
|
100
|
+
}
|
|
101
|
+
`,
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
public render(): TemplateResult {
|
|
105
|
+
const descriptors = interfaces.data.dnsProviderTypeDescriptors;
|
|
106
|
+
const descriptor = interfaces.data.getDnsProviderTypeDescriptor(this.selectedType);
|
|
107
|
+
|
|
108
|
+
return html`
|
|
109
|
+
<dees-form>
|
|
110
|
+
<div class="field">
|
|
111
|
+
<dees-input-text
|
|
112
|
+
.key=${'name'}
|
|
113
|
+
.label=${'Provider name'}
|
|
114
|
+
.value=${this.providerName}
|
|
115
|
+
.required=${true}
|
|
116
|
+
></dees-input-text>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
${this.lockType
|
|
120
|
+
? html`
|
|
121
|
+
<div class="field">
|
|
122
|
+
<dees-input-text
|
|
123
|
+
.key=${'__type_display'}
|
|
124
|
+
.label=${'Type'}
|
|
125
|
+
.value=${descriptor?.displayName ?? this.selectedType}
|
|
126
|
+
.disabled=${true}
|
|
127
|
+
></dees-input-text>
|
|
128
|
+
</div>
|
|
129
|
+
`
|
|
130
|
+
: html`
|
|
131
|
+
<div class="field">
|
|
132
|
+
<dees-input-dropdown
|
|
133
|
+
.key=${'__type'}
|
|
134
|
+
.label=${'Provider type'}
|
|
135
|
+
.options=${descriptors.map((d) => ({ option: d.displayName, key: d.type }))}
|
|
136
|
+
.selectedOption=${descriptor
|
|
137
|
+
? { option: descriptor.displayName, key: descriptor.type }
|
|
138
|
+
: undefined}
|
|
139
|
+
@selectedOption=${(e: CustomEvent) => {
|
|
140
|
+
const newType = (e.detail as any)?.key as
|
|
141
|
+
| interfaces.data.TDnsProviderType
|
|
142
|
+
| undefined;
|
|
143
|
+
if (newType && newType !== this.selectedType) {
|
|
144
|
+
this.selectedType = newType;
|
|
145
|
+
this.credentialValues = {};
|
|
146
|
+
}
|
|
147
|
+
}}
|
|
148
|
+
></dees-input-dropdown>
|
|
149
|
+
</div>
|
|
150
|
+
`}
|
|
151
|
+
${descriptor
|
|
152
|
+
? html`
|
|
153
|
+
<div class="typeDescription">${descriptor.description}</div>
|
|
154
|
+
${this.credentialsHint
|
|
155
|
+
? html`<div class="credentialsHint">${this.credentialsHint}</div>`
|
|
156
|
+
: ''}
|
|
157
|
+
${descriptor.credentialFields.map(
|
|
158
|
+
(f) => html`
|
|
159
|
+
<div class="field">
|
|
160
|
+
<dees-input-text
|
|
161
|
+
.key=${f.key}
|
|
162
|
+
.label=${f.label}
|
|
163
|
+
.required=${f.required && !this.lockType}
|
|
164
|
+
></dees-input-text>
|
|
165
|
+
${f.helpText ? html`<div class="helpText">${f.helpText}</div>` : ''}
|
|
166
|
+
</div>
|
|
167
|
+
`,
|
|
168
|
+
)}
|
|
169
|
+
`
|
|
170
|
+
: html`<p>No provider types registered.</p>`}
|
|
171
|
+
</dees-form>
|
|
172
|
+
`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Read the form values and assemble the create/update payload.
|
|
177
|
+
* Returns the typed credentials object built from the descriptor's keys.
|
|
178
|
+
*/
|
|
179
|
+
public async collectData(): Promise<{
|
|
180
|
+
name: string;
|
|
181
|
+
type: interfaces.data.TDnsProviderType;
|
|
182
|
+
credentials: interfaces.data.TDnsProviderCredentials;
|
|
183
|
+
credentialsTouched: boolean;
|
|
184
|
+
} | null> {
|
|
185
|
+
const form = this.shadowRoot?.querySelector('dees-form') as any;
|
|
186
|
+
if (!form) return null;
|
|
187
|
+
const data = await form.collectFormData();
|
|
188
|
+
const descriptor = interfaces.data.getDnsProviderTypeDescriptor(this.selectedType);
|
|
189
|
+
if (!descriptor) return null;
|
|
190
|
+
|
|
191
|
+
// Build the credentials object from the descriptor's field keys.
|
|
192
|
+
const credsBody: Record<string, string> = {};
|
|
193
|
+
let credentialsTouched = false;
|
|
194
|
+
for (const f of descriptor.credentialFields) {
|
|
195
|
+
const value = data[f.key];
|
|
196
|
+
if (value !== undefined && value !== null && String(value).length > 0) {
|
|
197
|
+
credsBody[f.key] = String(value);
|
|
198
|
+
credentialsTouched = true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// The discriminator goes on the credentials object so the backend
|
|
203
|
+
// factory and the discriminated union both stay happy.
|
|
204
|
+
const credentials = {
|
|
205
|
+
type: this.selectedType,
|
|
206
|
+
...credsBody,
|
|
207
|
+
} as unknown as interfaces.data.TDnsProviderCredentials;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
name: String(data.name ?? ''),
|
|
211
|
+
type: this.selectedType,
|
|
212
|
+
credentials,
|
|
213
|
+
credentialsTouched,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
state,
|
|
8
8
|
cssManager,
|
|
9
9
|
} from '@design.estate/dees-element';
|
|
10
|
-
import * as appstate from '
|
|
11
|
-
import * as interfaces from '
|
|
12
|
-
import { viewHostCss } from '
|
|
10
|
+
import * as appstate from '../../appstate.js';
|
|
11
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
12
|
+
import { viewHostCss } from '../shared/css.js';
|
|
13
13
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
14
14
|
|
|
15
15
|
declare global {
|
|
@@ -159,7 +159,7 @@ export class OpsViewCertificates extends DeesElement {
|
|
|
159
159
|
const { summary } = this.certState;
|
|
160
160
|
|
|
161
161
|
return html`
|
|
162
|
-
<dees-heading level="
|
|
162
|
+
<dees-heading level="3">Certificates</dees-heading>
|
|
163
163
|
|
|
164
164
|
<div class="certificatesContainer">
|
|
165
165
|
${this.renderStatsTiles(summary)}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeesElement,
|
|
3
|
+
html,
|
|
4
|
+
customElement,
|
|
5
|
+
type TemplateResult,
|
|
6
|
+
css,
|
|
7
|
+
state,
|
|
8
|
+
cssManager,
|
|
9
|
+
} from '@design.estate/dees-element';
|
|
10
|
+
import * as appstate from '../../appstate.js';
|
|
11
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
12
|
+
import { viewHostCss } from '../shared/css.js';
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface HTMLElementTagNameMap {
|
|
16
|
+
'ops-view-dns': OpsViewDns;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const RECORD_TYPES: interfaces.data.TDnsRecordType[] = [
|
|
21
|
+
'A',
|
|
22
|
+
'AAAA',
|
|
23
|
+
'CNAME',
|
|
24
|
+
'MX',
|
|
25
|
+
'TXT',
|
|
26
|
+
'NS',
|
|
27
|
+
'CAA',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
@customElement('ops-view-dns')
|
|
31
|
+
export class OpsViewDns extends DeesElement {
|
|
32
|
+
@state()
|
|
33
|
+
accessor domainsState: appstate.IDomainsState = appstate.domainsStatePart.getState()!;
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
super();
|
|
37
|
+
const sub = appstate.domainsStatePart.select().subscribe((newState) => {
|
|
38
|
+
this.domainsState = newState;
|
|
39
|
+
});
|
|
40
|
+
this.rxSubscriptions.push(sub);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async connectedCallback() {
|
|
44
|
+
await super.connectedCallback();
|
|
45
|
+
await appstate.domainsStatePart.dispatchAction(appstate.fetchDomainsAndProvidersAction, null);
|
|
46
|
+
// If a domain is already selected (e.g. via "View Records" navigation), refresh its records
|
|
47
|
+
const selected = this.domainsState.selectedDomainId;
|
|
48
|
+
if (selected) {
|
|
49
|
+
await appstate.domainsStatePart.dispatchAction(appstate.fetchDnsRecordsForDomainAction, {
|
|
50
|
+
domainId: selected,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public static styles = [
|
|
56
|
+
cssManager.defaultStyles,
|
|
57
|
+
viewHostCss,
|
|
58
|
+
css`
|
|
59
|
+
.dnsContainer {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 24px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.domainPicker {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 12px;
|
|
69
|
+
padding: 12px 16px;
|
|
70
|
+
background: ${cssManager.bdTheme('#f9fafb', '#111827')};
|
|
71
|
+
border-radius: 8px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.sourceBadge {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
padding: 2px 8px;
|
|
78
|
+
border-radius: 4px;
|
|
79
|
+
font-size: 11px;
|
|
80
|
+
font-weight: 500;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.sourceBadge.manual {
|
|
84
|
+
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
|
85
|
+
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.sourceBadge.synced {
|
|
89
|
+
background: ${cssManager.bdTheme('#fef3c7', '#451a03')};
|
|
90
|
+
color: ${cssManager.bdTheme('#92400e', '#fde047')};
|
|
91
|
+
}
|
|
92
|
+
`,
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
public render(): TemplateResult {
|
|
96
|
+
const domains = this.domainsState.domains;
|
|
97
|
+
const selectedId = this.domainsState.selectedDomainId;
|
|
98
|
+
const records = this.domainsState.records;
|
|
99
|
+
|
|
100
|
+
return html`
|
|
101
|
+
<dees-heading level="3">DNS Records</dees-heading>
|
|
102
|
+
<div class="dnsContainer">
|
|
103
|
+
<div class="domainPicker">
|
|
104
|
+
<span>Domain:</span>
|
|
105
|
+
<dees-input-dropdown
|
|
106
|
+
.options=${domains.map((d) => ({ option: d.name, key: d.id }))}
|
|
107
|
+
.selectedOption=${selectedId
|
|
108
|
+
? { option: domains.find((d) => d.id === selectedId)?.name || '', key: selectedId }
|
|
109
|
+
: undefined}
|
|
110
|
+
@selectedOption=${async (e: CustomEvent) => {
|
|
111
|
+
const id = (e.detail as any)?.key;
|
|
112
|
+
if (!id) return;
|
|
113
|
+
await appstate.domainsStatePart.dispatchAction(
|
|
114
|
+
appstate.fetchDnsRecordsForDomainAction,
|
|
115
|
+
{ domainId: id },
|
|
116
|
+
);
|
|
117
|
+
}}
|
|
118
|
+
></dees-input-dropdown>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
${selectedId
|
|
122
|
+
? html`
|
|
123
|
+
<dees-table
|
|
124
|
+
.heading1=${'DNS Records'}
|
|
125
|
+
.heading2=${this.domainHint(selectedId)}
|
|
126
|
+
.data=${records}
|
|
127
|
+
.showColumnFilters=${true}
|
|
128
|
+
.displayFunction=${(r: interfaces.data.IDnsRecord) => ({
|
|
129
|
+
Name: r.name,
|
|
130
|
+
Type: r.type,
|
|
131
|
+
Value: r.value,
|
|
132
|
+
TTL: r.ttl,
|
|
133
|
+
Source: html`<span class="sourceBadge ${r.source}">${r.source}</span>`,
|
|
134
|
+
})}
|
|
135
|
+
.dataActions=${[
|
|
136
|
+
{
|
|
137
|
+
name: 'Add Record',
|
|
138
|
+
iconName: 'lucide:plus',
|
|
139
|
+
type: ['header' as const],
|
|
140
|
+
actionFunc: async () => {
|
|
141
|
+
await this.showCreateRecordDialog(selectedId);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'Refresh',
|
|
146
|
+
iconName: 'lucide:rotateCw',
|
|
147
|
+
type: ['header' as const],
|
|
148
|
+
actionFunc: async () => {
|
|
149
|
+
await appstate.domainsStatePart.dispatchAction(
|
|
150
|
+
appstate.fetchDnsRecordsForDomainAction,
|
|
151
|
+
{ domainId: selectedId },
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'Edit',
|
|
157
|
+
iconName: 'lucide:pencil',
|
|
158
|
+
type: ['inRow', 'contextmenu'] as any,
|
|
159
|
+
actionFunc: async (actionData: any) => {
|
|
160
|
+
const rec = actionData.item as interfaces.data.IDnsRecord;
|
|
161
|
+
await this.showEditRecordDialog(rec);
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'Delete',
|
|
166
|
+
iconName: 'lucide:trash2',
|
|
167
|
+
type: ['inRow', 'contextmenu'] as any,
|
|
168
|
+
actionFunc: async (actionData: any) => {
|
|
169
|
+
const rec = actionData.item as interfaces.data.IDnsRecord;
|
|
170
|
+
await appstate.domainsStatePart.dispatchAction(
|
|
171
|
+
appstate.deleteDnsRecordAction,
|
|
172
|
+
{ id: rec.id, domainId: rec.domainId },
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
]}
|
|
177
|
+
></dees-table>
|
|
178
|
+
`
|
|
179
|
+
: html`<p style="opacity: 0.7;">Pick a domain above to view its records.</p>`}
|
|
180
|
+
</div>
|
|
181
|
+
`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private domainHint(domainId: string): string {
|
|
185
|
+
const domain = this.domainsState.domains.find((d) => d.id === domainId);
|
|
186
|
+
if (!domain) return '';
|
|
187
|
+
if (domain.source === 'manual') {
|
|
188
|
+
return 'Records are served by dcrouter (authoritative).';
|
|
189
|
+
}
|
|
190
|
+
return 'Records are stored at the provider — changes here are pushed via the provider API.';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private async showCreateRecordDialog(domainId: string) {
|
|
194
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
195
|
+
DeesModal.createAndShow({
|
|
196
|
+
heading: 'Add DNS Record',
|
|
197
|
+
content: html`
|
|
198
|
+
<dees-form>
|
|
199
|
+
<dees-input-text .key=${'name'} .label=${'Name (FQDN)'} .required=${true}></dees-input-text>
|
|
200
|
+
<dees-input-dropdown
|
|
201
|
+
.key=${'type'}
|
|
202
|
+
.label=${'Type'}
|
|
203
|
+
.options=${RECORD_TYPES.map((t) => ({ option: t, key: t }))}
|
|
204
|
+
.required=${true}
|
|
205
|
+
></dees-input-dropdown>
|
|
206
|
+
<dees-input-text
|
|
207
|
+
.key=${'value'}
|
|
208
|
+
.label=${'Value (for MX use "10 mail.example.com")'}
|
|
209
|
+
.required=${true}
|
|
210
|
+
></dees-input-text>
|
|
211
|
+
<dees-input-text .key=${'ttl'} .label=${'TTL (seconds)'} .value=${'300'}></dees-input-text>
|
|
212
|
+
</dees-form>
|
|
213
|
+
`,
|
|
214
|
+
menuOptions: [
|
|
215
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
216
|
+
{
|
|
217
|
+
name: 'Create',
|
|
218
|
+
action: async (modalArg: any) => {
|
|
219
|
+
const form = modalArg.shadowRoot
|
|
220
|
+
?.querySelector('.content')
|
|
221
|
+
?.querySelector('dees-form');
|
|
222
|
+
if (!form) return;
|
|
223
|
+
const data = await form.collectFormData();
|
|
224
|
+
const type = (data.type?.key ?? data.type) as interfaces.data.TDnsRecordType;
|
|
225
|
+
await appstate.domainsStatePart.dispatchAction(appstate.createDnsRecordAction, {
|
|
226
|
+
domainId,
|
|
227
|
+
name: String(data.name),
|
|
228
|
+
type,
|
|
229
|
+
value: String(data.value),
|
|
230
|
+
ttl: parseInt(String(data.ttl || '300'), 10),
|
|
231
|
+
});
|
|
232
|
+
modalArg.destroy();
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async showEditRecordDialog(rec: interfaces.data.IDnsRecord) {
|
|
240
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
241
|
+
DeesModal.createAndShow({
|
|
242
|
+
heading: `Edit ${rec.type} ${rec.name}`,
|
|
243
|
+
content: html`
|
|
244
|
+
<dees-form>
|
|
245
|
+
<dees-input-text .key=${'name'} .label=${'Name (FQDN)'} .value=${rec.name}></dees-input-text>
|
|
246
|
+
<dees-input-text .key=${'value'} .label=${'Value'} .value=${rec.value}></dees-input-text>
|
|
247
|
+
<dees-input-text .key=${'ttl'} .label=${'TTL (seconds)'} .value=${String(rec.ttl)}></dees-input-text>
|
|
248
|
+
</dees-form>
|
|
249
|
+
`,
|
|
250
|
+
menuOptions: [
|
|
251
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
252
|
+
{
|
|
253
|
+
name: 'Save',
|
|
254
|
+
action: async (modalArg: any) => {
|
|
255
|
+
const form = modalArg.shadowRoot
|
|
256
|
+
?.querySelector('.content')
|
|
257
|
+
?.querySelector('dees-form');
|
|
258
|
+
if (!form) return;
|
|
259
|
+
const data = await form.collectFormData();
|
|
260
|
+
await appstate.domainsStatePart.dispatchAction(appstate.updateDnsRecordAction, {
|
|
261
|
+
id: rec.id,
|
|
262
|
+
domainId: rec.domainId,
|
|
263
|
+
name: String(data.name),
|
|
264
|
+
value: String(data.value),
|
|
265
|
+
ttl: parseInt(String(data.ttl || '300'), 10),
|
|
266
|
+
});
|
|
267
|
+
modalArg.destroy();
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|