diff --git a/src/components/create-domains/SoaCreate.svelte b/src/components/create-domains/SoaCreate.svelte new file mode 100644 index 0000000..5b5437d --- /dev/null +++ b/src/components/create-domains/SoaCreate.svelte @@ -0,0 +1,26 @@ + + +
Name
+
{editItem.name}
+ + + +
Refresh
+
+
Retry
+
+
Expire
+
+
Default Time-to-Live
+
diff --git a/src/views/DomainsView.svelte b/src/views/DomainsView.svelte index 10af17b..b120f7b 100644 --- a/src/views/DomainsView.svelte +++ b/src/views/DomainsView.svelte @@ -27,6 +27,7 @@ type MxValue, type NsValue, type PtrValue, + type SoaValue, type SrvValue, type TxtValue, } from "../types/records"; @@ -54,6 +55,7 @@ import PtrCreate from "../components/create-domains/PtrCreate.svelte"; import download from "downloadjs"; import {parseArpaToPrefix} from "../utils/arpa"; + import SoaCreate from "../components/create-domains/SoaCreate.svelte"; const apiVerbena = import.meta.env.VITE_API_VERBENA; const apiAllZones = apiVerbena + "/zones"; @@ -77,6 +79,20 @@ allZones = rows; } + async function fetchSingleZone(id: number) { + let f = await LOGIN.clientRequest(apiAllZones + "/" + id, {method: "GET"}); + if (f.status != 200) throw new Error("Unexpected status code: " + f.status); + let fJson = await f.json(); + let row = fJson as Zone; + let newAllZones = allZones.map(x => { + if (x.id === row.id) { + return row; + } + return x; + }); + allZones = newAllZones; + } + onMount(() => { (async () => { await fetchAllZones(); @@ -285,6 +301,46 @@ let blob = await f.blob(); download(blob, `${currentZone.name}.zone`); } + + let editSoaItem: ApiRecordFormat; + let editSoaPopup: boolean = false; + let editSoaErrorMessage: string | null = null; + + $: if (currentZone) { + editSoaItem = { + name: currentZone.name, + type: "SOA", + ttl: 0, + value: { + ns: currentZone.nameservers[0] ?? "", + mbox: currentZone.admin, + serial: currentZone.serial, + refresh: currentZone.refresh, + retry: currentZone.retry, + expire: currentZone.expire, + minttl: currentZone.ttl, + }, + }; + } + + async function editSoaRecord() { + if (editSoaItem == null) return; + if (!currentZone) return; + + let zone = currentZone; + let item = { + refresh: editSoaItem.value.refresh, + retry: editSoaItem.value.retry, + expire: editSoaItem.value.expire, + ttl: editSoaItem.value.minttl, + }; + + let f = await LOGIN.clientRequest(apiAllZones + "/" + zone.id, {method: "PUT", body: JSON.stringify(item)}); + if (f.status !== 200 && f.status !== 201) throw new Error("Unexpected status code: " + f.status); + await f.json(); + + await fetchSingleZone(zone.id); + } {#if domainTitle} @@ -310,6 +366,54 @@ {/if} {#if currentZone} +
+

SOA Record

+ + + + + + + {#if editSoaErrorMessage} +
{editSoaErrorMessage}
+ {/if} +
+
+ + + + + + + + + + + {#if currentZone.nameservers?.length > 0} + + + + + {/if} + + + + + + + + + + + + + + + + + +
Zone{currentZone.name}
Admin{currentZone.admin}
Nameservers{(currentZone.nameservers || []).join(", ")}
Refresh{currentZone.refresh}
Retry{currentZone.retry}
Expire{currentZone.expire}
Default Time-to-Live{currentZone.ttl}
+ {#each recordTypes as recordType} @@ -349,6 +453,17 @@ } } + .row { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + } + + .edit-soa-button { + @include button-green-box; + } + .bot-token-selection { -webkit-touch-callout: all; /* iOS Safari */ -webkit-user-select: all; /* Safari */ diff --git a/test-server/main.go b/test-server/main.go index 029dc79..7c3a649 100644 --- a/test-server/main.go +++ b/test-server/main.go @@ -274,12 +274,21 @@ func apiServer(verify *mjwt.KeyStore) { })) r.Handle("/v1/verbena/zones", hasPerm(verify, "verbena:domains", func(rw http.ResponseWriter, req *http.Request, b mjwt.BaseTypeClaims[auth.AccessTokenClaims]) { type Zone struct { - ID int64 `json:"id"` - Name string `json:"name"` + ID int64 `json:"id"` + Name string `json:"name"` + Serial int64 `json:"serial"` + Admin string `json:"admin"` + Refresh int64 `json:"refresh"` + Retry int64 `json:"retry"` + Expire int64 `json:"expire"` + Ttl int64 `json:"ttl"` + Active bool `json:"active"` + Nameservers []string `json:"nameservers"` } + json.NewEncoder(rw).Encode([]Zone{ - {ID: 1, Name: "example.com"}, - {ID: 2, Name: "example.org"}, + {ID: 1, Name: "example.com", Serial: 20260101, Admin: "hostmaster.example.com", Refresh: 7200, Retry: 1800, Expire: 86400, Ttl: 600, Active: true, Nameservers: []string{"ns1.example.com", "ns2.example.com"}}, + {ID: 2, Name: "example.org", Serial: 20260101, Admin: "hostmaster.example.org", Refresh: 7200, Retry: 1800, Expire: 86400, Ttl: 600, Active: true, Nameservers: []string{"ns1.example.org", "ns2.example.org"}}, }) })) r.Handle("/v1/verbena/zones/0/records", hasPerm(verify, "verbena:domains", func(rw http.ResponseWriter, req *http.Request, b mjwt.BaseTypeClaims[auth.AccessTokenClaims]) {