2024-04-20 12:21:41 +02:00
|
|
|
import * as plugins from '../plugins.js';
|
2024-10-16 14:35:38 +02:00
|
|
|
import * as shared from '../elements/shared/index.js';
|
2024-04-20 12:21:41 +02:00
|
|
|
|
|
|
|
import {
|
|
|
|
DeesElement,
|
|
|
|
customElement,
|
|
|
|
html,
|
|
|
|
state,
|
|
|
|
css,
|
|
|
|
cssManager,
|
|
|
|
} from '@design.estate/dees-element';
|
|
|
|
|
|
|
|
import * as appstate from '../appstate.js';
|
|
|
|
|
|
|
|
@customElement('cloudly-view-dns')
|
|
|
|
export class CloudlyViewDns extends DeesElement {
|
|
|
|
@state()
|
|
|
|
private data: appstate.IDataState = {
|
|
|
|
secretGroups: [],
|
|
|
|
secretBundles: [],
|
2025-09-09 15:08:28 +00:00
|
|
|
dnsEntries: [],
|
2024-04-20 12:21:41 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super();
|
2025-09-09 15:08:28 +00:00
|
|
|
const subscription = appstate.dataState
|
2024-04-20 12:21:41 +02:00
|
|
|
.select((stateArg) => stateArg)
|
|
|
|
.subscribe((dataArg) => {
|
|
|
|
this.data = dataArg;
|
|
|
|
});
|
2025-09-09 15:08:28 +00:00
|
|
|
this.rxSubscriptions.push(subscription);
|
2024-04-20 12:21:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public static styles = [
|
|
|
|
cssManager.defaultStyles,
|
2024-10-16 14:35:38 +02:00
|
|
|
shared.viewHostCss,
|
2025-09-09 15:08:28 +00:00
|
|
|
css`
|
|
|
|
.dns-type-badge {
|
|
|
|
display: inline-block;
|
|
|
|
padding: 2px 8px;
|
|
|
|
border-radius: 4px;
|
|
|
|
font-size: 0.85em;
|
|
|
|
font-weight: 500;
|
|
|
|
color: white;
|
|
|
|
}
|
|
|
|
.type-A { background: #4CAF50; }
|
|
|
|
.type-AAAA { background: #45a049; }
|
|
|
|
.type-CNAME { background: #2196F3; }
|
|
|
|
.type-MX { background: #FF9800; }
|
|
|
|
.type-TXT { background: #9C27B0; }
|
|
|
|
.type-NS { background: #795548; }
|
|
|
|
.type-SOA { background: #607D8B; }
|
|
|
|
.type-SRV { background: #E91E63; }
|
|
|
|
.type-CAA { background: #00BCD4; }
|
|
|
|
.type-PTR { background: #673AB7; }
|
|
|
|
|
|
|
|
.status-active {
|
|
|
|
color: #4CAF50;
|
|
|
|
}
|
|
|
|
.status-inactive {
|
|
|
|
color: #f44336;
|
|
|
|
}
|
2024-04-20 12:21:41 +02:00
|
|
|
`,
|
|
|
|
];
|
|
|
|
|
2025-09-09 15:08:28 +00:00
|
|
|
private getRecordTypeBadge(type: string) {
|
|
|
|
return html`<span class="dns-type-badge type-${type}">${type}</span>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
private getStatusBadge(active: boolean) {
|
|
|
|
return html`<span class="${active ? 'status-active' : 'status-inactive'}">
|
|
|
|
${active ? '✓ Active' : '✗ Inactive'}
|
|
|
|
</span>`;
|
|
|
|
}
|
|
|
|
|
2024-04-20 12:21:41 +02:00
|
|
|
public render() {
|
|
|
|
return html`
|
2025-09-09 15:08:28 +00:00
|
|
|
<cloudly-sectionheading>DNS Management</cloudly-sectionheading>
|
2024-04-20 12:21:41 +02:00
|
|
|
<dees-table
|
2025-09-09 15:08:28 +00:00
|
|
|
.heading1=${'DNS Entries'}
|
|
|
|
.heading2=${'Manage DNS records for your domains'}
|
|
|
|
.data=${this.data.dnsEntries || []}
|
|
|
|
.displayFunction=${(itemArg: plugins.interfaces.data.IDnsEntry) => {
|
2024-04-20 12:21:41 +02:00
|
|
|
return {
|
2025-09-09 15:08:28 +00:00
|
|
|
Type: this.getRecordTypeBadge(itemArg.data.type),
|
|
|
|
Name: itemArg.data.name === '@' ? '<root>' : itemArg.data.name,
|
|
|
|
Value: itemArg.data.value,
|
|
|
|
TTL: `${itemArg.data.ttl}s`,
|
|
|
|
Priority: itemArg.data.priority || '-',
|
|
|
|
Zone: itemArg.data.zone,
|
|
|
|
Status: this.getStatusBadge(itemArg.data.active),
|
|
|
|
Description: itemArg.data.description || '-',
|
2024-04-20 12:21:41 +02:00
|
|
|
};
|
|
|
|
}}
|
|
|
|
.dataActions=${[
|
|
|
|
{
|
2025-09-09 15:08:28 +00:00
|
|
|
name: 'Add DNS Entry',
|
2024-04-20 12:21:41 +02:00
|
|
|
iconName: 'plus',
|
|
|
|
type: ['header', 'footer'],
|
|
|
|
actionFunc: async (dataActionArg) => {
|
|
|
|
const modal = await plugins.deesCatalog.DeesModal.createAndShow({
|
2025-09-09 15:08:28 +00:00
|
|
|
heading: 'Add DNS Entry',
|
|
|
|
content: html`
|
|
|
|
<dees-form>
|
|
|
|
<dees-input-dropdown
|
|
|
|
.key=${'type'}
|
|
|
|
.label=${'Record Type'}
|
|
|
|
.options=${[
|
|
|
|
{key: 'A', option: 'A - IPv4 Address'},
|
|
|
|
{key: 'AAAA', option: 'AAAA - IPv6 Address'},
|
|
|
|
{key: 'CNAME', option: 'CNAME - Canonical Name'},
|
|
|
|
{key: 'MX', option: 'MX - Mail Exchange'},
|
|
|
|
{key: 'TXT', option: 'TXT - Text Record'},
|
|
|
|
{key: 'NS', option: 'NS - Name Server'},
|
|
|
|
{key: 'SOA', option: 'SOA - Start of Authority'},
|
|
|
|
{key: 'SRV', option: 'SRV - Service'},
|
|
|
|
{key: 'CAA', option: 'CAA - Certification Authority'},
|
|
|
|
{key: 'PTR', option: 'PTR - Pointer'},
|
|
|
|
]}
|
|
|
|
.value=${'A'}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-dropdown>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'zone'}
|
|
|
|
.label=${'Zone (Domain)'}
|
|
|
|
.placeholder=${'example.com'}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'name'}
|
|
|
|
.label=${'Name'}
|
|
|
|
.placeholder=${'@ for root, www, mail, etc.'}
|
|
|
|
.value=${'@'}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'value'}
|
|
|
|
.label=${'Value'}
|
|
|
|
.placeholder=${'IP address, domain, or text value'}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'ttl'}
|
|
|
|
.label=${'TTL (seconds)'}
|
|
|
|
.value=${'3600'}
|
|
|
|
.type=${'number'}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'priority'}
|
|
|
|
.label=${'Priority (MX/SRV only)'}
|
|
|
|
.type=${'number'}
|
|
|
|
.placeholder=${'10'}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'weight'}
|
|
|
|
.label=${'Weight (SRV only)'}
|
|
|
|
.type=${'number'}
|
|
|
|
.placeholder=${'0'}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'port'}
|
|
|
|
.label=${'Port (SRV only)'}
|
|
|
|
.type=${'number'}
|
|
|
|
.placeholder=${'443'}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-checkbox
|
|
|
|
.key=${'active'}
|
|
|
|
.label=${'Active'}
|
|
|
|
.value=${true}>
|
|
|
|
</dees-input-checkbox>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'description'}
|
|
|
|
.label=${'Description (optional)'}
|
|
|
|
.placeholder=${'What is this record for?'}>
|
|
|
|
</dees-input-text>
|
|
|
|
</dees-form>
|
|
|
|
`,
|
|
|
|
menuOptions: [
|
|
|
|
{
|
|
|
|
name: 'Create DNS Entry',
|
|
|
|
action: async (modalArg) => {
|
|
|
|
const form = modalArg.shadowRoot.querySelector('dees-form') as any;
|
|
|
|
const formData = await form.gatherData();
|
|
|
|
|
|
|
|
await appstate.dataState.dispatchAction(appstate.createDnsEntryAction, {
|
|
|
|
dnsEntryData: {
|
|
|
|
type: formData.type,
|
|
|
|
zone: formData.zone,
|
|
|
|
name: formData.name || '@',
|
|
|
|
value: formData.value,
|
|
|
|
ttl: parseInt(formData.ttl) || 3600,
|
|
|
|
priority: formData.priority ? parseInt(formData.priority) : undefined,
|
|
|
|
weight: formData.weight ? parseInt(formData.weight) : undefined,
|
|
|
|
port: formData.port ? parseInt(formData.port) : undefined,
|
|
|
|
active: formData.active,
|
|
|
|
description: formData.description || undefined,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
await modalArg.destroy();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Cancel',
|
|
|
|
action: async (modalArg) => {
|
|
|
|
modalArg.destroy();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Edit',
|
|
|
|
iconName: 'edit',
|
|
|
|
type: ['contextmenu', 'inRow'],
|
|
|
|
actionFunc: async (actionDataArg) => {
|
|
|
|
const dnsEntry = actionDataArg.item as plugins.interfaces.data.IDnsEntry;
|
|
|
|
const modal = await plugins.deesCatalog.DeesModal.createAndShow({
|
|
|
|
heading: `Edit DNS Entry`,
|
2024-04-20 12:21:41 +02:00
|
|
|
content: html`
|
|
|
|
<dees-form>
|
2025-09-09 15:08:28 +00:00
|
|
|
<dees-input-dropdown
|
|
|
|
.key=${'type'}
|
|
|
|
.label=${'Record Type'}
|
|
|
|
.options=${[
|
|
|
|
{key: 'A', option: 'A - IPv4 Address'},
|
|
|
|
{key: 'AAAA', option: 'AAAA - IPv6 Address'},
|
|
|
|
{key: 'CNAME', option: 'CNAME - Canonical Name'},
|
|
|
|
{key: 'MX', option: 'MX - Mail Exchange'},
|
|
|
|
{key: 'TXT', option: 'TXT - Text Record'},
|
|
|
|
{key: 'NS', option: 'NS - Name Server'},
|
|
|
|
{key: 'SOA', option: 'SOA - Start of Authority'},
|
|
|
|
{key: 'SRV', option: 'SRV - Service'},
|
|
|
|
{key: 'CAA', option: 'CAA - Certification Authority'},
|
|
|
|
{key: 'PTR', option: 'PTR - Pointer'},
|
|
|
|
]}
|
|
|
|
.value=${dnsEntry.data.type}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-dropdown>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'zone'}
|
|
|
|
.label=${'Zone (Domain)'}
|
|
|
|
.value=${dnsEntry.data.zone}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'name'}
|
|
|
|
.label=${'Name'}
|
|
|
|
.value=${dnsEntry.data.name}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'value'}
|
|
|
|
.label=${'Value'}
|
|
|
|
.value=${dnsEntry.data.value}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'ttl'}
|
|
|
|
.label=${'TTL (seconds)'}
|
|
|
|
.value=${dnsEntry.data.ttl}
|
|
|
|
.type=${'number'}
|
|
|
|
.required=${true}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'priority'}
|
|
|
|
.label=${'Priority (MX/SRV only)'}
|
|
|
|
.value=${dnsEntry.data.priority || ''}
|
|
|
|
.type=${'number'}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'weight'}
|
|
|
|
.label=${'Weight (SRV only)'}
|
|
|
|
.value=${dnsEntry.data.weight || ''}
|
|
|
|
.type=${'number'}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'port'}
|
|
|
|
.label=${'Port (SRV only)'}
|
|
|
|
.value=${dnsEntry.data.port || ''}
|
|
|
|
.type=${'number'}>
|
|
|
|
</dees-input-text>
|
|
|
|
<dees-input-checkbox
|
|
|
|
.key=${'active'}
|
|
|
|
.label=${'Active'}
|
|
|
|
.value=${dnsEntry.data.active}>
|
|
|
|
</dees-input-checkbox>
|
|
|
|
<dees-input-text
|
|
|
|
.key=${'description'}
|
|
|
|
.label=${'Description (optional)'}
|
|
|
|
.value=${dnsEntry.data.description || ''}>
|
|
|
|
</dees-input-text>
|
2024-04-20 12:21:41 +02:00
|
|
|
</dees-form>
|
|
|
|
`,
|
|
|
|
menuOptions: [
|
|
|
|
{
|
2025-09-09 15:08:28 +00:00
|
|
|
name: 'Update DNS Entry',
|
|
|
|
action: async (modalArg) => {
|
|
|
|
const form = modalArg.shadowRoot.querySelector('dees-form') as any;
|
|
|
|
const formData = await form.gatherData();
|
|
|
|
|
|
|
|
await appstate.dataState.dispatchAction(appstate.updateDnsEntryAction, {
|
|
|
|
dnsEntryId: dnsEntry.id,
|
|
|
|
dnsEntryData: {
|
|
|
|
...dnsEntry.data,
|
|
|
|
type: formData.type,
|
|
|
|
zone: formData.zone,
|
|
|
|
name: formData.name || '@',
|
|
|
|
value: formData.value,
|
|
|
|
ttl: parseInt(formData.ttl) || 3600,
|
|
|
|
priority: formData.priority ? parseInt(formData.priority) : undefined,
|
|
|
|
weight: formData.weight ? parseInt(formData.weight) : undefined,
|
|
|
|
port: formData.port ? parseInt(formData.port) : undefined,
|
|
|
|
active: formData.active,
|
|
|
|
description: formData.description || undefined,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
await modalArg.destroy();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Cancel',
|
2024-04-20 12:21:41 +02:00
|
|
|
action: async (modalArg) => {
|
|
|
|
modalArg.destroy();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2025-09-09 15:08:28 +00:00
|
|
|
name: 'Duplicate',
|
|
|
|
iconName: 'copy',
|
|
|
|
type: ['contextmenu', 'inRow'],
|
|
|
|
actionFunc: async (actionDataArg) => {
|
|
|
|
const dnsEntry = actionDataArg.item as plugins.interfaces.data.IDnsEntry;
|
|
|
|
await appstate.dataState.dispatchAction(appstate.createDnsEntryAction, {
|
|
|
|
dnsEntryData: {
|
|
|
|
...dnsEntry.data,
|
|
|
|
description: `Copy of ${dnsEntry.data.description || dnsEntry.data.name}`,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Toggle Active',
|
|
|
|
iconName: 'power',
|
|
|
|
type: ['contextmenu', 'inRow'],
|
|
|
|
actionFunc: async (actionDataArg) => {
|
|
|
|
const dnsEntry = actionDataArg.item as plugins.interfaces.data.IDnsEntry;
|
|
|
|
await appstate.dataState.dispatchAction(appstate.updateDnsEntryAction, {
|
|
|
|
dnsEntryId: dnsEntry.id,
|
|
|
|
dnsEntryData: {
|
|
|
|
...dnsEntry.data,
|
|
|
|
active: !dnsEntry.data.active,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Delete',
|
2024-04-20 12:21:41 +02:00
|
|
|
iconName: 'trash',
|
|
|
|
type: ['contextmenu', 'inRow'],
|
|
|
|
actionFunc: async (actionDataArg) => {
|
2025-09-09 15:08:28 +00:00
|
|
|
const dnsEntry = actionDataArg.item as plugins.interfaces.data.IDnsEntry;
|
2024-04-20 12:21:41 +02:00
|
|
|
plugins.deesCatalog.DeesModal.createAndShow({
|
2025-09-09 15:08:28 +00:00
|
|
|
heading: `Delete DNS Entry`,
|
2024-04-20 12:21:41 +02:00
|
|
|
content: html`
|
|
|
|
<div style="text-align:center">
|
2025-09-09 15:08:28 +00:00
|
|
|
Are you sure you want to delete this DNS entry?
|
2024-04-20 12:21:41 +02:00
|
|
|
</div>
|
2025-09-09 15:08:28 +00:00
|
|
|
<div style="margin-top: 16px; padding: 16px; background: #333; border-radius: 8px;">
|
|
|
|
<div style="color: #fff; font-weight: bold;">
|
|
|
|
${dnsEntry.data.type} - ${dnsEntry.data.name}.${dnsEntry.data.zone}
|
|
|
|
</div>
|
|
|
|
<div style="color: #aaa; font-size: 0.9em; margin-top: 4px;">
|
|
|
|
${dnsEntry.data.value}
|
|
|
|
</div>
|
|
|
|
${dnsEntry.data.description ? html`
|
|
|
|
<div style="color: #888; font-size: 0.85em; margin-top: 8px;">
|
|
|
|
${dnsEntry.data.description}
|
|
|
|
</div>
|
|
|
|
` : ''}
|
2024-04-20 12:21:41 +02:00
|
|
|
</div>
|
|
|
|
`,
|
|
|
|
menuOptions: [
|
|
|
|
{
|
2025-09-09 15:08:28 +00:00
|
|
|
name: 'Cancel',
|
2024-04-20 12:21:41 +02:00
|
|
|
action: async (modalArg) => {
|
|
|
|
await modalArg.destroy();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2025-09-09 15:08:28 +00:00
|
|
|
name: 'Delete',
|
2024-04-20 12:21:41 +02:00
|
|
|
action: async (modalArg) => {
|
2025-09-09 15:08:28 +00:00
|
|
|
await appstate.dataState.dispatchAction(appstate.deleteDnsEntryAction, {
|
|
|
|
dnsEntryId: dnsEntry.id,
|
2024-04-20 12:21:41 +02:00
|
|
|
});
|
|
|
|
await modalArg.destroy();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
] as plugins.deesCatalog.ITableAction[]}
|
|
|
|
></dees-table>
|
|
|
|
`;
|
|
|
|
}
|
2025-09-09 15:08:28 +00:00
|
|
|
}
|