feat(dns): add built-in dcrouter DNS provider support and rename manual domains to dcrouter-hosted/local
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '13.8.0',
|
||||
version: '13.9.0',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -1793,7 +1793,7 @@ export async function fetchProviderDomains(
|
||||
return await request.fire({ identity: context.identity, providerId });
|
||||
}
|
||||
|
||||
export const createManualDomainAction = domainsStatePart.createAction<{
|
||||
export const createDcrouterDomainAction = domainsStatePart.createAction<{
|
||||
name: string;
|
||||
description?: string;
|
||||
}>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
|
||||
|
||||
@@ -44,12 +44,15 @@ export class DnsProviderForm extends DeesElement {
|
||||
accessor providerName: string = '';
|
||||
|
||||
/**
|
||||
* Currently selected provider type. Initialized to the first descriptor;
|
||||
* caller can override before mounting (e.g. for edit dialogs).
|
||||
* Currently selected provider type. Initialized to the first user-creatable
|
||||
* descriptor; caller can override before mounting (e.g. for edit dialogs).
|
||||
* The built-in 'dcrouter' pseudo-provider is excluded from the picker —
|
||||
* operators cannot create another one.
|
||||
*/
|
||||
@state()
|
||||
accessor selectedType: interfaces.data.TDnsProviderType =
|
||||
interfaces.data.dnsProviderTypeDescriptors[0]?.type ?? 'cloudflare';
|
||||
interfaces.data.dnsProviderTypeDescriptors.find((d) => d.type !== 'dcrouter')?.type ??
|
||||
'cloudflare';
|
||||
|
||||
/** When true, hide the type picker — used in edit dialogs. */
|
||||
@property({ type: Boolean })
|
||||
@@ -102,7 +105,12 @@ export class DnsProviderForm extends DeesElement {
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
const descriptors = interfaces.data.dnsProviderTypeDescriptors;
|
||||
// Exclude the built-in 'dcrouter' pseudo-provider from the type picker —
|
||||
// operators cannot create another one, it's surfaced at read time by the
|
||||
// backend handler instead.
|
||||
const descriptors = interfaces.data.dnsProviderTypeDescriptors.filter(
|
||||
(d) => d.type !== 'dcrouter',
|
||||
);
|
||||
const descriptor = interfaces.data.getDnsProviderTypeDescriptor(this.selectedType);
|
||||
|
||||
return html`
|
||||
|
||||
@@ -80,7 +80,7 @@ export class OpsViewDns extends DeesElement {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sourceBadge.manual {
|
||||
.sourceBadge.local {
|
||||
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
||||
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
||||
}
|
||||
@@ -184,7 +184,7 @@ export class OpsViewDns extends DeesElement {
|
||||
private domainHint(domainId: string): string {
|
||||
const domain = this.domainsState.domains.find((d) => d.id === domainId);
|
||||
if (!domain) return '';
|
||||
if (domain.source === 'manual') {
|
||||
if (domain.source === 'dcrouter') {
|
||||
return 'Records are served by dcrouter (authoritative).';
|
||||
}
|
||||
return 'Records are stored at the provider — changes here are pushed via the provider API.';
|
||||
|
||||
@@ -55,7 +55,7 @@ export class OpsViewDomains extends DeesElement {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sourceBadge.manual {
|
||||
.sourceBadge.dcrouter {
|
||||
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
||||
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export class OpsViewDomains extends DeesElement {
|
||||
<div class="domainsContainer">
|
||||
<dees-table
|
||||
.heading1=${'Domains'}
|
||||
.heading2=${'Domains under management — manual (authoritative) or imported from a provider'}
|
||||
.heading2=${'Domains under management — served by dcrouter (authoritative) or imported from a provider'}
|
||||
.data=${domains}
|
||||
.showColumnFilters=${true}
|
||||
.displayFunction=${(d: interfaces.data.IDomain) => ({
|
||||
@@ -90,11 +90,11 @@ export class OpsViewDomains extends DeesElement {
|
||||
})}
|
||||
.dataActions=${[
|
||||
{
|
||||
name: 'Add Manual Domain',
|
||||
name: 'Add DcRouter Domain',
|
||||
iconName: 'lucide:plus',
|
||||
type: ['header' as const],
|
||||
actionFunc: async () => {
|
||||
await this.showCreateManualDialog();
|
||||
await this.showCreateDcrouterDialog();
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -168,17 +168,17 @@ export class OpsViewDomains extends DeesElement {
|
||||
d: interfaces.data.IDomain,
|
||||
providersById: Map<string, interfaces.data.IDnsProviderPublic>,
|
||||
): TemplateResult {
|
||||
if (d.source === 'manual') {
|
||||
return html`<span class="sourceBadge manual">Manual</span>`;
|
||||
if (d.source === 'dcrouter') {
|
||||
return html`<span class="sourceBadge dcrouter">DcRouter</span>`;
|
||||
}
|
||||
const provider = d.providerId ? providersById.get(d.providerId) : undefined;
|
||||
return html`<span class="sourceBadge provider">${provider?.name || 'Provider'}</span>`;
|
||||
}
|
||||
|
||||
private async showCreateManualDialog() {
|
||||
private async showCreateDcrouterDialog() {
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Add Manual Domain',
|
||||
heading: 'Add DcRouter Domain',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<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 {
|
||||
?.querySelector('dees-form');
|
||||
if (!form) return;
|
||||
const data = await form.collectFormData();
|
||||
await appstate.domainsStatePart.dispatchAction(appstate.createManualDomainAction, {
|
||||
await appstate.domainsStatePart.dispatchAction(appstate.createDcrouterDomainAction, {
|
||||
name: String(data.name),
|
||||
description: data.description ? String(data.description) : undefined,
|
||||
});
|
||||
|
||||
@@ -71,6 +71,11 @@ export class OpsViewProviders extends DeesElement {
|
||||
background: ${cssManager.bdTheme('#f3f4f6', '#1f2937')};
|
||||
color: ${cssManager.bdTheme('#4b5563', '#9ca3af')};
|
||||
}
|
||||
|
||||
.statusBadge.builtin {
|
||||
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
|
||||
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -82,15 +87,21 @@ export class OpsViewProviders extends DeesElement {
|
||||
<div class="providersContainer">
|
||||
<dees-table
|
||||
.heading1=${'Providers'}
|
||||
.heading2=${'External DNS provider accounts'}
|
||||
.heading2=${'Built-in dcrouter + external DNS provider accounts'}
|
||||
.data=${providers}
|
||||
.showColumnFilters=${true}
|
||||
.displayFunction=${(p: interfaces.data.IDnsProviderPublic) => ({
|
||||
Name: p.name,
|
||||
Type: this.providerTypeLabel(p.type),
|
||||
Status: this.renderStatusBadge(p.status),
|
||||
'Last Tested': p.lastTestedAt ? new Date(p.lastTestedAt).toLocaleString() : 'never',
|
||||
Error: p.lastError || '-',
|
||||
Status: p.builtIn
|
||||
? html`<span class="statusBadge builtin">built-in</span>`
|
||||
: this.renderStatusBadge(p.status),
|
||||
'Last Tested': p.builtIn
|
||||
? '—'
|
||||
: p.lastTestedAt
|
||||
? new Date(p.lastTestedAt).toLocaleString()
|
||||
: 'never',
|
||||
Error: p.builtIn ? '—' : p.lastError || '-',
|
||||
})}
|
||||
.dataActions=${[
|
||||
{
|
||||
@@ -116,6 +127,7 @@ export class OpsViewProviders extends DeesElement {
|
||||
name: 'Test Connection',
|
||||
iconName: 'lucide:plug',
|
||||
type: ['inRow', 'contextmenu'] as any,
|
||||
actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
|
||||
actionFunc: async (actionData: any) => {
|
||||
const provider = actionData.item as interfaces.data.IDnsProviderPublic;
|
||||
await this.testProvider(provider);
|
||||
@@ -125,6 +137,7 @@ export class OpsViewProviders extends DeesElement {
|
||||
name: 'Edit',
|
||||
iconName: 'lucide:pencil',
|
||||
type: ['inRow', 'contextmenu'] as any,
|
||||
actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
|
||||
actionFunc: async (actionData: any) => {
|
||||
const provider = actionData.item as interfaces.data.IDnsProviderPublic;
|
||||
await this.showEditDialog(provider);
|
||||
@@ -134,6 +147,7 @@ export class OpsViewProviders extends DeesElement {
|
||||
name: 'Delete',
|
||||
iconName: 'lucide:trash2',
|
||||
type: ['inRow', 'contextmenu'] as any,
|
||||
actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
|
||||
actionFunc: async (actionData: any) => {
|
||||
const provider = actionData.item as interfaces.data.IDnsProviderPublic;
|
||||
await this.deleteProvider(provider);
|
||||
|
||||
Reference in New Issue
Block a user