AkurAI Build
Menu

Public repository / 2704a695631eb72db620d03a9099c15609ebd9d7

AkurAI-Mail / src/routes/(admin)/dns/+page.svelte

Latest change a7e96191cab7cc0297487b9e56f4e19a4c057957 - refactor: switch to adapter-static for Rust API backend by Ólafur Búi Ólafsson

<script lang="ts">
  import { invalidateAll } from "$app/navigation";

  let { data } = $props();
  let records = $derived(data.records);
  let dnsHealth = $derived(data.dnsHealth);
  let busy = $state(false);

  async function refresh() {
    await invalidateAll();
  }
</script>

<svelte:head><title>DNS Records - AkurAI Mail Admin</title></svelte:head>

<header class="topbar">
  <div>
    <p class="kicker">DNS configuration</p>
    <h1>DNS Records</h1>
    <p>1984 Hosting values expected by this VM</p>
  </div>
  <button class="primary" onclick={refresh} disabled={busy}>Refresh</button>
</header>

<section class="panel">
  <div class="panel-head">
    <div>
      <h2>Expected records</h2>
      <p>{records.length} records configured</p>
    </div>
  </div>
  <div class="records">
    {#each records as record}
      <div>
        <b>{record.type}</b>
        <span>{record.name}</span>
        <code>{record.value}</code>
      </div>
    {/each}
  </div>
</section>

<section class="panel">
  <div class="panel-head">
    <div>
      <h2>Health checks</h2>
      <p>Live DNS resolution status</p>
    </div>
  </div>
  <div class="health-list">
    {#each dnsHealth as check}
      <div>
        <b class:ok={check.ok}>{check.ok ? "OK" : "Check"}</b>
        <span>{check.type} {check.name}</span>
        <code>{(check.values ?? []).join(", ") || check.error || "no answer"}</code>
      </div>
    {:else}
      <p class="empty">No health check data available.</p>
    {/each}
  </div>
</section>

<style>
  .topbar{display:flex;justify-content:space-between;align-items:flex-start;gap:18px;min-width:0}
  .kicker{margin:0 0 6px;color:#126b5a;font-size:13px;font-weight:800}.topbar h1{margin:0;font-size:30px;line-height:1.15;text-wrap:balance}.topbar p:last-child{margin:6px 0 0;color:#61716c;overflow-wrap:anywhere}
  .primary{border:0;border-radius:6px;background:#126b5a;color:#fff;min-height:44px;padding:10px 15px;font-weight:800;cursor:pointer}.primary:hover{background:#0e594b}.primary:disabled{opacity:.55;cursor:not-allowed}
  .panel{background:#fff;border:1px solid #dce4e1;border-radius:8px;min-width:0;padding:18px}.panel-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:15px}.panel h2{margin:0;font-size:18px}.panel p{margin:4px 0 0;color:#61716c}
  .records{display:grid;gap:8px}.records div{display:grid;align-items:center;gap:10px;border-top:1px solid #edf1ef;padding-top:10px;min-width:0;grid-template-columns:72px minmax(150px,260px) minmax(0,1fr)}
  .records span,.records code{min-width:0;overflow-wrap:anywhere}.records code{background:#eef4f1;border-radius:5px;padding:5px 7px;white-space:pre-wrap;word-break:break-word}
  .health-list{display:grid;gap:8px}.health-list div{display:grid;grid-template-columns:72px minmax(150px,260px) minmax(0,1fr);gap:10px;align-items:center}.health-list b{color:#9a473a}.health-list b.ok{color:#126b5a}
  .health-list span,.health-list code{min-width:0;overflow-wrap:anywhere}.health-list code{background:#eef4f1;border-radius:5px;padding:5px 7px;white-space:pre-wrap;word-break:break-word}
  .empty{color:#61716c}
  @media(max-width:760px){.topbar{display:grid}.topbar h1{font-size:25px}.primary{width:100%}.records div,.health-list div{grid-template-columns:minmax(0,1fr)}}
  @media(max-width:420px){.panel{padding:15px}.topbar h1{font-size:23px}}
</style>