lazydns 0.3.20

A light and fast DNS server/forwarder implementation in Rust
Documentation
<script lang="ts">
  import { onMount, onDestroy } from "svelte";
  import * as echarts from "echarts";
  import type { TopClient } from "../lib/types";
  import { darkMode } from "../lib/stores";

  export let clients: TopClient[];

  let chartContainer: HTMLDivElement;
  let chart: echarts.ECharts | null = null;
  let isDark = true;

  darkMode.subscribe((value) => {
    isDark = value;
    if (chart) {
      chart.dispose();
      initChart();
    }
  });

  function initChart() {
    if (!chartContainer) return;

    chart = echarts.init(chartContainer, isDark ? "dark" : undefined);

    updateChart();
  }

  function updateChart() {
    if (!chart) return;

    const topClients = clients.slice(0, 8);

    const tooltipBg = isDark
      ? "rgba(17, 24, 39, 0.95)"
      : "rgba(255, 255, 255, 0.95)";
    const tooltipBorder = isDark ? "#374151" : "#e5e7eb";
    const tooltipTextColor = isDark ? "#f3f4f6" : "#1f2937";
    const legendColor = isDark ? "#9ca3af" : "#4b5563";
    const borderColor = isDark ? "#1f2937" : "#f3f4f6";
    const emphasisColor = isDark ? "#fff" : "#1f2937";

    const option: echarts.EChartsOption = {
      backgroundColor: "transparent",
      tooltip: {
        trigger: "item",
        backgroundColor: tooltipBg,
        borderColor: tooltipBorder,
        textStyle: { color: tooltipTextColor },
        formatter: (params: any) => {
          const client = topClients[params.dataIndex];
          return `
            <div class="font-medium">${client.ip}</div>
            <div>Queries: ${client.queries.toLocaleString()}</div>
            <div>Blocked: ${client.blocked}</div>
            <div>Avg Latency: ${client.avg_response_ms.toFixed(1)}ms</div>
          `;
        },
      },
      legend: {
        type: "scroll",
        orient: "vertical",
        right: 10,
        top: "center",
        textStyle: {
          color: legendColor,
          fontSize: 11,
        },
      },
      series: [
        {
          type: "pie",
          radius: ["45%", "70%"],
          center: ["35%", "50%"],
          avoidLabelOverlap: false,
          itemStyle: {
            borderRadius: 6,
            borderColor: borderColor,
            borderWidth: 2,
          },
          label: {
            show: false,
          },
          emphasis: {
            label: {
              show: true,
              fontSize: 14,
              fontWeight: "bold",
              color: emphasisColor,
            },
          },
          labelLine: {
            show: false,
          },
          data: topClients.map((client, i) => ({
            value: client.queries,
            name: client.ip,
            itemStyle: {
              color: [
                "#3b82f6",
                "#8b5cf6",
                "#ec4899",
                "#f97316",
                "#10b981",
                "#06b6d4",
                "#6366f1",
                "#84cc16",
              ][i % 8],
            },
          })),
        },
      ],
    };

    chart.setOption(option);
  }

  function handleResize() {
    chart?.resize();
  }

  onMount(() => {
    initChart();
    window.addEventListener("resize", handleResize);
  });

  onDestroy(() => {
    window.removeEventListener("resize", handleResize);
    chart?.dispose();
  });

  $: if (chart && clients) {
    updateChart();
  }
</script>

<div class="card">
  <div class="card-header">
    <h3 class="font-semibold {isDark ? 'text-white' : 'text-gray-900'}">
      Top Clients
    </h3>
  </div>
  <div class="card-body">
    <div bind:this={chartContainer} class="w-full h-80"></div>
  </div>
</div>