Initial commit: NAUT torrent web UI with stubbed server
Advanced torrent client web UI aimed at power users, with a zero-dependency Node stub server (built-in http + SSE) serving live mock data. - Dense sortable/multi-select torrent grid with live updates - Detail panel: General/Trackers/Peers/Content/Pieces (resizable) - Sidebar filters: status, categories, tags, trackers - Create/delete categories and tags (sidebar + right-click) - Add via magnet or client-side-parsed .torrent upload - RSS, integrated search, and read-only engine views - Hand-drawn SVG icon set, dark theme, keyboard shortcuts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
bc1be49a37
12 changed files with 2677 additions and 0 deletions
48
public/js/api.js
Normal file
48
public/js/api.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Thin API client + SSE live stream.
|
||||
|
||||
async function jget(url) {
|
||||
const r = await fetch(url);
|
||||
if (!r.ok) throw new Error(`${url} → ${r.status}`);
|
||||
return r.json();
|
||||
}
|
||||
async function jpost(url, body) {
|
||||
const r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body || {}),
|
||||
});
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export const api = {
|
||||
meta: () => jget('/api/meta'),
|
||||
snapshot: () => jget('/api/snapshot'),
|
||||
torrent: (hash) => jget(`/api/torrents/${hash}`),
|
||||
trackers: (hash) => jget(`/api/torrents/${hash}/trackers`),
|
||||
peers: (hash) => jget(`/api/torrents/${hash}/peers`),
|
||||
files: (hash) => jget(`/api/torrents/${hash}/files`),
|
||||
pieces: (hash) => jget(`/api/torrents/${hash}/pieces`),
|
||||
|
||||
action: (action, hashes, params) => jpost('/api/action', { action, hashes, params }),
|
||||
remove: (hashes, deleteFiles) => jpost('/api/delete', { hashes, deleteFiles }),
|
||||
add: (opts) => jpost('/api/add', opts),
|
||||
toggleAltSpeed: () => jpost('/api/altspeed', {}),
|
||||
|
||||
createCategory: (name, savePath) => jpost('/api/categories', { name, savePath }),
|
||||
deleteCategory: (name) => jpost('/api/categories/delete', { name }),
|
||||
createTag: (name) => jpost('/api/tags', { name }),
|
||||
deleteTag: (name) => jpost('/api/tags/delete', { name }),
|
||||
|
||||
rss: () => jget('/api/rss'),
|
||||
rssRules: () => jget('/api/rss/rules'),
|
||||
search: (q) => jget(`/api/search?q=${encodeURIComponent(q)}`),
|
||||
|
||||
// Live snapshot stream. onSnapshot(snapshot) called ~1/s.
|
||||
stream(onSnapshot, onStatus) {
|
||||
const es = new EventSource('/api/stream');
|
||||
es.addEventListener('snapshot', (e) => onSnapshot(JSON.parse(e.data)));
|
||||
es.onopen = () => onStatus && onStatus('connected');
|
||||
es.onerror = () => onStatus && onStatus('reconnecting');
|
||||
return es;
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue