mindless/src/views/Dht.svelte
2026-09-11 23:53:00 -04:00

70 lines
2.9 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { ips } from '../lib/api.js';
import { badge } from '../lib/ui.js';
let torrents: any[] = [];
let peers: any[] = [];
let infoHash = '';
let ip = '';
let error = '';
async function load() {
try {
torrents = await ips('/api/dht/torrents?take=50');
error = '';
} catch (e) {
error = e instanceof Error ? e.message : String(e);
}
}
async function lookupPeers() {
if (!infoHash.trim()) return;
peers = await ips(`/api/dht/peers?infoHash=${encodeURIComponent(infoHash.trim())}`);
}
async function reverseIp() {
if (!ip.trim()) return;
peers = await ips(`/api/dht/reverse?ip=${encodeURIComponent(ip.trim())}`);
}
onMount(load);
</script>
{#if error}<p class="mb-2 text-sm text-red-400">{error}</p>{/if}
<div class="grid gap-4 md:grid-cols-2">
<section class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div class="mb-2 flex items-center justify-between">
<h2 class="text-sm font-semibold uppercase tracking-wide opacity-70">Recent torrents</h2>
<button class="text-xs underline opacity-70 hover:opacity-100" on:click={load}>refresh</button>
</div>
<ul class="max-h-96 space-y-1 overflow-y-auto text-sm">
{#each torrents as t}
<li class="rounded px-2 py-1 hover:bg-white/5">
<code class="text-xs">{t.infoHash?.slice(0, 20)}…</code>
<span class="ml-2 opacity-80">{t.name || '(unknown)'}</span>
<span class="ml-2"><span class={badge({ variant: 'success' })}>S {t.seeders}</span>
<span class={badge({ variant: 'warn' })}>L {t.leechers}</span></span>
</li>
{/each}
{#if !torrents.length}<li class="opacity-50">No torrents indexed.</li>{/if}
</ul>
</section>
<section class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h2 class="mb-2 text-sm font-semibold uppercase tracking-wide opacity-70">Peer lookup</h2>
<div class="mb-2 flex gap-2">
<input class="h-9 flex-1 rounded-md border border-[hsl(var(--border))] bg-transparent px-3 text-sm" bind:value={infoHash} placeholder="infohash…" />
<button class="inline-flex h-9 items-center rounded-md border border-[hsl(var(--border))] px-3 text-sm hover:bg-white/5" on:click={lookupPeers}>peers</button>
</div>
<div class="mb-2 flex gap-2">
<input class="h-9 flex-1 rounded-md border border-[hsl(var(--border))] bg-transparent px-3 text-sm" bind:value={ip} placeholder="reverse ip…" />
<button class="inline-flex h-9 items-center rounded-md border border-[hsl(var(--border))] px-3 text-sm hover:bg-white/5" on:click={reverseIp}>reverse</button>
</div>
<ul class="max-h-72 space-y-1 overflow-y-auto text-sm">
{#each peers as p}
<li class="rounded px-2 py-1 hover:bg-white/5"><code>{p.address}:{p.port}</code> <span class="opacity-60">{p.client || ''}</span></li>
{/each}
</ul>
</section>
</div>