import {
DeesElement,
html,
customElement,
type TemplateResult,
css,
state,
cssManager,
} from '@design.estate/dees-element';
import * as appstate from '../../appstate.js';
import * as interfaces from '../../../dist_ts_interfaces/index.js';
import { viewHostCss } from '../shared/css.js';
import { appRouter } from '../../router.js';
declare global {
interface HTMLElementTagNameMap {
'ops-view-domains': OpsViewDomains;
}
}
@customElement('ops-view-domains')
export class OpsViewDomains extends DeesElement {
@state()
accessor domainsState: appstate.IDomainsState = appstate.domainsStatePart.getState()!;
constructor() {
super();
const sub = appstate.domainsStatePart.select().subscribe((newState) => {
this.domainsState = newState;
});
this.rxSubscriptions.push(sub);
}
async connectedCallback() {
await super.connectedCallback();
await appstate.domainsStatePart.dispatchAction(appstate.fetchDomainsAndProvidersAction, null);
}
public static styles = [
cssManager.defaultStyles,
viewHostCss,
css`
.domainsContainer {
display: flex;
flex-direction: column;
gap: 24px;
}
.sourceBadge {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
}
.sourceBadge.dcrouter {
background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
}
.sourceBadge.provider {
background: ${cssManager.bdTheme('#fef3c7', '#451a03')};
color: ${cssManager.bdTheme('#92400e', '#fde047')};
}
`,
];
public render(): TemplateResult {
const domains = this.domainsState.domains;
const providersById = new Map(this.domainsState.providers.map((p) => [p.id, p]));
return html`
dcrouter will become the authoritative DNS server for this domain. You'll need to delegate the domain's nameservers to dcrouter to make this effective.
`, menuOptions: [ { name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() }, { name: 'Create', action: async (modalArg: any) => { const form = modalArg.shadowRoot ?.querySelector('.content') ?.querySelector('dees-form'); if (!form) return; const data = await form.collectFormData(); await appstate.domainsStatePart.dispatchAction(appstate.createDcrouterDomainAction, { name: String(data.name), description: data.description ? String(data.description) : undefined, }); modalArg.destroy(); }, }, ], }); } private async showImportDialog() { const providers = this.domainsState.providers; if (providers.length === 0) { const { DeesToast } = await import('@design.estate/dees-catalog'); DeesToast.show({ message: 'Add a DNS provider first (Domains > Providers)', type: 'warning', duration: 3500, }); return; } const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog'); DeesModal.createAndShow({ heading: 'Import Domains from Provider', content: html`Tip: use "List Provider Domains" to see what's available before typing.
`, menuOptions: [ { name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() }, { name: 'List Provider Domains', action: async (_modalArg: any) => { const form = _modalArg.shadowRoot ?.querySelector('.content') ?.querySelector('dees-form'); if (!form) return; const data = await form.collectFormData(); const providerKey = data.providerId?.key ?? data.providerId; if (!providerKey) { DeesToast.show({ message: 'Pick a provider first', type: 'warning', duration: 2500 }); return; } const result = await appstate.fetchProviderDomains(String(providerKey)); if (!result.success) { DeesToast.show({ message: result.message || 'Failed to fetch domains', type: 'error', duration: 4000, }); return; } const list = (result.domains ?? []).map((d) => d.name).join(', '); DeesToast.show({ message: `Provider has: ${list || '(none)'}`, type: 'info', duration: 8000, }); }, }, { name: 'Import', action: async (modalArg: any) => { const form = modalArg.shadowRoot ?.querySelector('.content') ?.querySelector('dees-form'); if (!form) return; const data = await form.collectFormData(); const providerKey = data.providerId?.key ?? data.providerId; if (!providerKey) { DeesToast.show({ message: 'Pick a provider', type: 'warning', duration: 2500 }); return; } const names = String(data.domainNames || '') .split(',') .map((s) => s.trim()) .filter(Boolean); if (names.length === 0) { DeesToast.show({ message: 'Enter at least one FQDN', type: 'warning', duration: 2500 }); return; } await appstate.domainsStatePart.dispatchAction( appstate.importDomainsFromProviderAction, { providerId: String(providerKey), domainNames: names }, ); modalArg.destroy(); }, }, ], }); } private async showMigrateDialog(domain: interfaces.data.IDomain) { const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog'); const providers = this.domainsState.providers; // Build target options based on current source const targetOptions: { option: string; key: string }[] = []; if (domain.source === 'provider') { targetOptions.push({ option: 'DcRouter (authoritative)', key: 'dcrouter' }); } // Add all providers (except the current one if already provider-managed) for (const p of providers) { if (domain.source === 'provider' && domain.providerId === p.id) continue; targetOptions.push({ option: `${p.name} (${p.type})`, key: `provider:${p.id}` }); } if (domain.source === 'dcrouter') { targetOptions.unshift({ option: 'DcRouter (authoritative)', key: 'dcrouter' }); } if (targetOptions.length === 0) { DeesToast.show({ message: 'No migration targets available. Add a DNS provider first.', type: 'warning', duration: 3000, }); return; } const currentLabel = domain.source === 'dcrouter' ? 'DcRouter (authoritative)' : providers.find((p) => p.id === domain.providerId)?.name || 'Provider'; DeesModal.createAndShow({ heading: `Migrate: ${domain.name}`, content: html`${domain.source === 'provider' ? 'This removes the domain and its cached records from dcrouter only. The zone at the provider is NOT touched.' : 'This removes the domain and all of its DNS records from dcrouter. dcrouter will no longer answer queries for this domain after the next restart.'}
`, menuOptions: [ { name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() }, { name: 'Delete', action: async (modalArg: any) => { await appstate.domainsStatePart.dispatchAction(appstate.deleteDomainAction, { id: domain.id, }); modalArg.destroy(); }, }, ], }); } }