lazydns 0.3.20

A light and fast DNS server/forwarder implementation in Rust
Documentation
<script lang="ts">
  import { onMount } from "svelte";
  import AlertsList from "../components/AlertsList.svelte";
  import { darkMode } from "../lib/stores";
  import { api, type Alert } from "../lib/api";
  import { notifications } from "../lib/stores";
  import { features } from "../lib/features.svelte";

  let alerts: Alert[] = [];
  let loading = true;
  let error: string | null = null;

  async function fetchAlerts() {
    try {
      const response = await api.getRecentAlerts();
      alerts = response.alerts || [];
      error = null;
    } catch (e) {
      error = e instanceof Error ? e.message : "Failed to fetch alerts";
      console.error("Alerts fetch error:", e);
    } finally {
      loading = false;
    }
  }

  async function acknowledgeAllAlerts() {
    if (!features.admin) {
      notifications.add({
        type: "error",
        message: "Admin feature is not enabled on this server",
      });
      return;
    }
    try {
      await api.acknowledgeAllAlerts();
      alerts = alerts.map((a) => ({ ...a, acknowledged: true }));
      notifications.add({
        type: "success",
        message: "All alerts acknowledged",
      });
    } catch (e) {
      notifications.add({
        type: "error",
        message:
          e instanceof Error ? e.message : "Failed to acknowledge alerts",
      });
    }
  }

  async function acknowledgeAlert(id: string) {
    if (!features.admin) {
      notifications.add({
        type: "error",
        message: "Admin feature is not enabled on this server",
      });
      return;
    }
    try {
      await api.acknowledgeAlert(id);
      alerts = alerts.map((a) =>
        a.id === id ? { ...a, acknowledged: true } : a,
      );
      notifications.add({
        type: "success",
        message: "Alert acknowledged",
      });
    } catch (e) {
      notifications.add({
        type: "error",
        message: e instanceof Error ? e.message : "Failed to acknowledge alert",
      });
    }
  }

  async function clearAllAlerts() {
    if (!features.admin) {
      notifications.add({
        type: "error",
        message: "Admin feature is not enabled on this server",
      });
      return;
    }
    try {
      await api.clearAlerts();
      alerts = [];
      notifications.add({
        type: "success",
        message: "All alerts cleared",
      });
    } catch (e) {
      notifications.add({
        type: "error",
        message: e instanceof Error ? e.message : "Failed to clear alerts",
      });
    }
  }

  onMount(() => {
    fetchAlerts();
    // Refresh every 5 seconds
    const interval = setInterval(fetchAlerts, 5000);
    return () => clearInterval(interval);
  });
</script>

<div class="max-w-7xl mx-auto">
  <div class="mb-8">
    <h1 class="text-2xl font-bold {$darkMode ? 'text-white' : 'text-gray-900'}">
      All Alerts
    </h1>
    <p class="{$darkMode ? 'text-gray-400' : 'text-gray-700'} mt-1">
      View all security and system alerts
    </p>
  </div>

  {#if error}
    <div class="card bg-red-500/10 border border-red-500/50 p-4 text-red-500">
      <p>Error: {error}</p>
    </div>
  {:else if loading}
    <div class="card p-8 text-center">
      <div class="inline-block">
        <div
          class="w-8 h-8 border-4 {$darkMode
            ? 'border-gray-700 border-t-primary-500'
            : 'border-gray-300 border-t-primary-600'} rounded-full animate-spin"
        ></div>
      </div>
    </div>
  {:else}
    <div class="card">
      <div
        class="card-header flex items-center justify-between bg-white {$darkMode
          ? 'dark:bg-gray-800'
          : 'bg-gray-50'}"
      >
        <h3
          class="font-semibold {$darkMode
            ? 'text-white'
            : 'text-gray-900'} flex items-center gap-2"
        >
          <svg
            class="w-5 h-5 {$darkMode ? 'text-gray-400' : 'text-gray-500'}"
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
            />
          </svg>
          All Alerts ({alerts.length})
        </h3>
        <div class="flex gap-2">
          <button
            on:click={clearAllAlerts}
            disabled={!features.admin}
            class="px-3 py-1.5 text-xs font-medium rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed {$darkMode
              ? 'bg-red-900/30 hover:bg-red-900/50 text-red-400'
              : 'bg-red-100 hover:bg-red-200 text-red-700'}"
          >
            Clear All
          </button>
          <button
            on:click={acknowledgeAllAlerts}
            disabled={!features.admin}
            class="px-3 py-1.5 text-xs font-medium rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed {$darkMode
              ? 'bg-blue-900/30 hover:bg-blue-900/50 text-blue-400'
              : 'bg-blue-100 hover:bg-blue-200 text-blue-700'}"
          >
            Acknowledge All
          </button>
        </div>
      </div>
      <div class="p-4">
        <AlertsList
          {alerts}
          limit={alerts.length}
          showAcknowledgeButton={true}
          adminEnabled={features.admin}
          showViewAllLink={false}
          on:acknowledge={(e) => acknowledgeAlert(e.detail)}
        />
      </div>
    </div>
  {/if}
</div>