diff --git a/src/components/domains/ARow.svelte b/src/components/domains/ARow.svelte new file mode 100644 index 0000000..15f2adc --- /dev/null +++ b/src/components/domains/ARow.svelte @@ -0,0 +1,53 @@ + + + + {value.data.Hdr.Name} + {isARecord(value.data) ? value.data.A : isAaaaRecord(value.data) ? value.data.AAAA : ""} + + { + editItem = JSON.parse(JSON.stringify(value.data)); + editPopup = true; + }} + remove={() => value.remove()} + /> + + +
Name
+
{editItem.Hdr.Name}
+ {#if isARecord(value.data)} +
IPv4 Address
+
+ {:else if isAaaaRecord(value.data)} +
IPv6 Address
+
+ {:else} +
Pretty sure something is broken. WOMP WOMP!!
+ {/if} +
+ + diff --git a/src/components/domains/CnameRow.svelte b/src/components/domains/CnameRow.svelte new file mode 100644 index 0000000..b235ea4 --- /dev/null +++ b/src/components/domains/CnameRow.svelte @@ -0,0 +1,45 @@ + + + + {value.data.Hdr.Name} + {value.data.Target} + + { + editItem = JSON.parse(JSON.stringify(value.data)); + editPopup = true; + }} + remove={() => value.remove()} + /> + + +
Name
+
{editItem.Hdr.Name}
+
Target
+
+
+ + diff --git a/src/components/domains/MxRow.svelte b/src/components/domains/MxRow.svelte new file mode 100644 index 0000000..a4ab101 --- /dev/null +++ b/src/components/domains/MxRow.svelte @@ -0,0 +1,49 @@ + + + + {value.data.Hdr.Name} + {value.data.Mx} + {value.data.Preference} + + { + editItem = JSON.parse(JSON.stringify(value.data)); + editPopup = true; + }} + remove={() => value.remove()} + /> + + +
Name
+
{editItem.Hdr.Name}
+
Mail Server
+
+
Preference
+
+
+ + diff --git a/src/components/domains/NsRow.svelte b/src/components/domains/NsRow.svelte new file mode 100644 index 0000000..197cfa3 --- /dev/null +++ b/src/components/domains/NsRow.svelte @@ -0,0 +1,45 @@ + + + + {value.data.Hdr.Name} + {value.data.Ns} + + { + editItem = JSON.parse(JSON.stringify(value.data)); + editPopup = true; + }} + remove={() => value.remove()} + /> + + +
Name
+
{editItem.Hdr.Name}
+
Nameserver
+
+
+ + diff --git a/src/components/domains/SoaRow.svelte b/src/components/domains/SoaRow.svelte new file mode 100644 index 0000000..a389649 --- /dev/null +++ b/src/components/domains/SoaRow.svelte @@ -0,0 +1,63 @@ + + + + {value.data.Hdr.Name} + {value.data.Mbox} + {value.data.Minttl} + {value.data.Refresh} + {value.data.Retry} + {value.data.Expire} + + { + editItem = JSON.parse(JSON.stringify(value.data)); + editPopup = true; + }} + remove={() => value.remove()} + /> + + +
Name
+
{editItem.Hdr.Name}
+
Mailbox
+
+
Minimum Time-to-Live
+
+
Refresh
+
+
Retry
+
+
Expire
+
+
+ + diff --git a/src/components/domains/TxtRow.svelte b/src/components/domains/TxtRow.svelte new file mode 100644 index 0000000..d63e607 --- /dev/null +++ b/src/components/domains/TxtRow.svelte @@ -0,0 +1,47 @@ + + + + {value.data.Hdr.Name} + + {value.data.Txt.join("\n")} + + + { + editItem = JSON.parse(JSON.stringify(value.data)); + editPopup = true; + }} + remove={() => value.remove()} + /> + + +
Name
+
{editItem.Hdr.Name}
+
Value
+
+
+ + diff --git a/src/stores/records.ts b/src/stores/records.ts index 01c048a..053cf83 100644 --- a/src/stores/records.ts +++ b/src/stores/records.ts @@ -1,4 +1,12 @@ -import {writable} from "svelte/store"; +export const DnsTypeSOA = 6; +export const DnsTypeNS = 2; +export const DnsTypeMX = 15; +export const DnsTypeA = 1; +export const DnsTypeAAAA = 28; +export const DnsTypeCNAME = 5; +export const DnsTypeTXT = 16; +export const DnsTypeSRV = 33; +export const DnsTypeCAA = 257; export interface RecordHeader { Name: string; @@ -22,72 +30,58 @@ export interface SoaRecord extends UnknownRecord { } export function isSoaRecord(x: UnknownRecord): x is SoaRecord { - return x.Hdr.Rrtype === 6; + return x.Hdr.Rrtype === DnsTypeSOA; } -export const soaRecords = writable>([]); - export interface NsRecord extends UnknownRecord { Ns: string; } export function isNsRecord(x: UnknownRecord): x is NsRecord { - return x.Hdr.Rrtype === 2; + return x.Hdr.Rrtype === DnsTypeNS; } -export const nsRecords = writable>([]); - export interface MxRecord extends UnknownRecord { Preference: number; Mx: string; } export function isMxRecord(x: UnknownRecord): x is MxRecord { - return x.Hdr.Rrtype === 15; + return x.Hdr.Rrtype === DnsTypeMX; } -export const mxRecords = writable>([]); - export interface ARecord extends UnknownRecord { A: string; } export function isARecord(x: UnknownRecord): x is ARecord { - return x.Hdr.Rrtype === 1; + return x.Hdr.Rrtype === DnsTypeA; } -export const aRecords = writable>([]); - export interface AaaaRecord extends UnknownRecord { AAAA: string; } export function isAaaaRecord(x: UnknownRecord): x is AaaaRecord { - return x.Hdr.Rrtype === 28; + return x.Hdr.Rrtype === DnsTypeAAAA; } -export const aaaaRecords = writable>([]); - export interface CnameRecord extends UnknownRecord { Target: string; } export function isCnameRecord(x: UnknownRecord): x is CnameRecord { - return x.Hdr.Rrtype === 5; + return x.Hdr.Rrtype === DnsTypeCNAME; } -export const cnameRecords = writable>([]); - export interface TxtRecord extends UnknownRecord { Txt: Array; } export function isTxtRecord(x: UnknownRecord): x is TxtRecord { - return x.Hdr.Rrtype === 16; + return x.Hdr.Rrtype === DnsTypeTXT; } -export const txtRecords = writable>([]); - export interface SrvRecord extends UnknownRecord { Priority: number; Weight: number; @@ -96,11 +90,9 @@ export interface SrvRecord extends UnknownRecord { } export function isSrvRecord(x: UnknownRecord): x is SrvRecord { - return x.Hdr.Rrtype === 33; + return x.Hdr.Rrtype === DnsTypeSRV; } -export const srvRecords = writable>([]); - export interface CaaRecord extends UnknownRecord { Flag: number; Tag: string; @@ -108,7 +100,5 @@ export interface CaaRecord extends UnknownRecord { } export function isCaaRecord(x: UnknownRecord): x is CaaRecord { - return x.Hdr.Rrtype === 257; + return x.Hdr.Rrtype === DnsTypeCAA; } - -export const caaRecords = writable>([]); diff --git a/src/views/DomainsView.svelte b/src/views/DomainsView.svelte index f922b4d..9d78375 100644 --- a/src/views/DomainsView.svelte +++ b/src/views/DomainsView.svelte @@ -2,10 +2,7 @@ import {LOGIN} from "../utils/login"; import {domainOption} from "../stores/domain-option"; import { - aRecords, - aaaaRecords, - caaRecords, - cnameRecords, + DnsTypeSOA, isARecord, isAaaaRecord, isCaaRecord, @@ -15,38 +12,50 @@ isSoaRecord, isSrvRecord, isTxtRecord, - mxRecords, - nsRecords, - soaRecords, - srvRecords, - txtRecords, + type ARecord, + type AaaaRecord, + type CaaRecord, + type CnameRecord, + type MxRecord, + type NsRecord, + type SoaRecord, + type SrvRecord, + type TxtRecord, type UnknownRecord, } from "../stores/records"; import ActionMenu from "../components/ActionMenu.svelte"; + import PromiseTable from "../components/PromiseTable.svelte"; + import {RestItem, RestTable} from "../utils/rest-table"; + import PromiseLike from "../components/PromiseLike.svelte"; + import SoaRow from "../components/domains/SoaRow.svelte"; const apiAzalea = import.meta.env.VITE_API_AZALEA; - let promiseForTable: Promise = reloadTable(); + type AllRecords = SoaRecord | NsRecord | MxRecord | ARecord | AaaaRecord | CnameRecord | TxtRecord | SrvRecord | CaaRecord; - async function reloadTable(): Promise { - let f = await LOGIN.clientRequest(apiAzalea + "/domains/" + $domainOption + "/records", {}); - if (f.status != 200) throw new Error("Unexpected status code: " + f.status); - let fJson = await f.json(); - let rows = fJson as Array; - $soaRecords = rows.filter(isSoaRecord); - $nsRecords = rows.filter(isNsRecord); - $mxRecords = rows.filter(isMxRecord); - $aRecords = rows.filter(isARecord); - $aaaaRecords = rows.filter(isAaaaRecord); - $cnameRecords = rows.filter(isCnameRecord); - $txtRecords = rows.filter(isTxtRecord); - $srvRecords = rows.filter(isSrvRecord); - $caaRecords = rows.filter(isCaaRecord); + const table = new RestTable(apiAzalea + "/domains/" + $domainOption + "/records", (item: AllRecords) => item.Hdr.Name); + + function rowOrdering( + rows: RestItem[], + domain: string, + isTRecord: (t: UnknownRecord) => t is T, + ): RestItem[] { + return rows + .filter(x => isTRecord(x.data)) + .filter(x => domainFilter(x.data.Hdr.Name, domain)) + .sort((a, b) => a.data.Hdr.Name.localeCompare(b.data.Hdr.Name)) as unknown as RestItem[]; } - domainOption.subscribe(x => { - promiseForTable = reloadTable(); - }); + function domainFilter(src: string, domain: string) { + if (domain == "*") return true; + let n = src.indexOf("/"); + if (n == -1) n = src.length; + let p = src.slice(0, n); + if (p == domain) return true; + return p.endsWith(domain); + } + + domainOption.subscribe(() => table.reload()); function getTitleDomain(name: string): string { if (name.endsWith(".")) { @@ -54,266 +63,309 @@ } return name; } + + let soaRecords: SoaRecord[] = [ + { + Hdr: { + Name: "example.com.", + Rrtype: DnsTypeSOA, + Class: 1, + Ttl: 300, + }, + Ns: "ns1.example.com.", + Mbox: "postmaster.example.com.", + Serial: 0, + Refresh: 0, + Retry: 0, + Expire: 0, + Minttl: 0, + }, + ]; -{#await promiseForTable} -
-
Loading...
+{#if soaRecords.length >= 1} +
+

Domains / {getTitleDomain(soaRecords[0].Hdr.Name)}

+ + Download DNS Zone File +
-{:then} - {#if $soaRecords.length >= 1} -
-

Domains / {getTitleDomain($soaRecords[0].Hdr.Name)}

- - Download DNS Zone File - -
- {/if} +{/if} -

SOA Record

- - - - - - - - - - - - - - {#if $soaRecords.length === 0} - - {/if} - {#each $soaRecords as record} - - - - - - - - + + + + + + + + + + + {#each rowOrdering(value.rows, $domainOption, DnsTypeSOA) as item} + + + - {/each} - -
Primary DomainEmailDefault TTLRefresh RateRetry RateExpire Time
No items to display
{record.Hdr.Name}{record.Mbox}{record.Minttl}{record.Refresh}{record.Retry}{record.Expire} - console.log(t)} remove={null} /> +

SOA Record

+ +
Primary DomainEmailDefault TTLRefresh RateRetry RateExpire Time
+
Loading...
-

NS Record

- - - - - - - - - - - {#if $nsRecords.length === 0} - - {/if} - {#each $nsRecords as record} - - - - - + + - {/each} - -
Name ServerSubdomainTTL
No items to display
{record.Ns}{record.Hdr.Name}{record.Hdr.Ttl}
Error loading row for {item.data.Hdr.Name}: {reason}
-

MX Record

- - + + + {/each} + + +
+ + + + + + + + + + + + + {#if $soaRecords.length === 0} + + {/if} + {#each $soaRecords as record} - - - - - + + + + + + + - - - {#if $mxRecords.length === 0} - - {/if} - {#each $mxRecords as record} - - - - - - - - {/each} - -
Primary DomainEmailDefault TTLRefresh RateRetry RateExpire Time
No items to display
Mail ServerPreferenceSubdomainTTL{record.Hdr.Name}{record.Mbox}{record.Minttl}{record.Refresh}{record.Retry}{record.Expire} + console.log(t)} remove={null} /> +
No items to display
{record.Mx}{record.Preference}{record.Hdr.Name}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
+ {/each} + + -

A/AAAA Record

- - +

NS Record

+
+ + + + + + + + + + {#if $nsRecords.length === 0} + + {/if} + {#each $nsRecords as record} - - - - + + + + - - - {#if $aRecords.length === 0 && $aaaaRecords.length === 0} - - {/if} - {#each $aRecords as record} - - - - - - - {/each} - {#each $aaaaRecords as record} - - - - - - - {/each} - -
Name ServerSubdomainTTL
No items to display
HostnameIP AddressTTL{record.Ns}{record.Hdr.Name}{record.Hdr.Ttl}
No items to display
{record.Hdr.Name}{record.A}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
{record.Hdr.Name}{record.AAAA}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
+ {/each} + + -

CNAME Record

- - +

MX Record

+
+ + + + + + + + + + + {#if $mxRecords.length === 0} + + {/if} + {#each $mxRecords as record} - - - - + + + + + - - - {#if $cnameRecords.length === 0} - - {/if} - {#each $cnameRecords as record} - - - - - - - {/each} - -
Mail ServerPreferenceSubdomainTTL
No items to display
HostnameAliases toTTL{record.Mx}{record.Preference}{record.Hdr.Name}{record.Hdr.Ttl} + console.log(t)} remove={t => console.log(t)} /> +
No items to display
{record.Hdr.Name}{record.Target}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
+ {/each} + + -

TXT Record

- - +

A/AAAA Record

+
+ + + + + + + + + + {#if $aRecords.length === 0 && $aaaaRecords.length === 0} + + {/if} + {#each $aRecords as record} - - - - + + + + - - - {#if $txtRecords.length === 0} - - {/if} - {#each $txtRecords as record} - - - - - - - {/each} - -
HostnameIP AddressTTL
No items to display
HostnameValueTTL{record.Hdr.Name}{record.A}{record.Hdr.Ttl} + console.log(t)} remove={t => console.log(t)} /> +
No items to display
{record.Hdr.Name}{record.Txt.join("\n")}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
+ {/each} + {#each $aaaaRecords as record} + + {record.Hdr.Name} + {record.AAAA} + {record.Hdr.Ttl} + + console.log(t)} remove={t => console.log(t)} /> + + + {/each} + + -

SRV Record

- - +

CNAME Record

+
+ + + + + + + + + + {#if $cnameRecords.length === 0} + + {/if} + {#each $cnameRecords as record} - - - - - - - + + + + - - - {#if $srvRecords.length === 0} - - {/if} - {#each $srvRecords as record} - - - - - - - - - - {/each} - -
HostnameAliases toTTL
No items to display
NamePriorityWeightPortTargetTTL{record.Hdr.Name}{record.Target}{record.Hdr.Ttl} + console.log(t)} remove={t => console.log(t)} /> +
No items to display
{record.Hdr.Name}{record.Priority}{record.Weight}{record.Port}{record.Target}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
+ {/each} + + -

CAA Record

- - +

TXT Record

+
+ + + + + + + + + + {#if $txtRecords.length === 0} + + {/if} + {#each $txtRecords as record} - - - - - + + + + - - - {#if $caaRecords.length === 0} - - {/if} - {#each $caaRecords as record} - - - - - - - - {/each} - -
HostnameValueTTL
No items to display
NameTagValueTTL{record.Hdr.Name} + {record.Txt.join("\n")} + {record.Hdr.Ttl} + console.log(t)} remove={t => console.log(t)} /> +
No items to display
{record.Hdr.Name}{record.Tag}{record.Value}{record.Hdr.Ttl} - console.log(t)} remove={t => console.log(t)} /> -
-{/await} + {/each} + + + +

SRV Record

+ + + + + + + + + + + + + + {#if $srvRecords.length === 0} + + {/if} + {#each $srvRecords as record} + + + + + + + + + + {/each} + +
NamePriorityWeightPortTargetTTL
No items to display
{record.Hdr.Name}{record.Priority}{record.Weight}{record.Port}{record.Target}{record.Hdr.Ttl} + console.log(t)} remove={t => console.log(t)} /> +
+ +

CAA Record

+ + + + + + + + + + + + {#if $caaRecords.length === 0} + + {/if} + {#each $caaRecords as record} + + + + + + + + {/each} + +
NameTagValueTTL
No items to display
{record.Hdr.Name}{record.Tag}{record.Value}{record.Hdr.Ttl} + console.log(t)} remove={t => console.log(t)} /> +