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}
+
+
+
+
+
+ | Zone |
+ {currentZone.name} |
+
+
+ | Admin |
+ {currentZone.admin} |
+
+ {#if currentZone.nameservers?.length > 0}
+
+ | Nameservers |
+ {(currentZone.nameservers || []).join(", ")} |
+
+ {/if}
+
+ | 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]) {