mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-28 21:17:36 +00:00
library.js add() used INSERT OR REPLACE, so re-adding an already-kept
magnet reset kept=0 and added_at — the ephemeral purge sweep would then
delete it 6h later as if it had never been kept. Switch to
INSERT ... ON CONFLICT DO UPDATE SET last_played_at only, preserving kept/
added_at/name/magnet/path across a re-add.
server.js never resynced the WebTorrent engine with the library DB at
startup: kept titles 404'd on /stream after any restart (engine.get()
returns null for a torrent the engine never re-learned about), and
runPurge's engine.remove() became a silent no-op for ephemeral rows,
leaking their downloaded bytes on disk while their DB rows vanished. Add
resumeLibrary(engine, library, { rmrf }), wired into start(): kept rows are
re-added to the engine (try/catch + promise .catch so one bad magnet can't
abort the loop); ephemeral rows are dropped from the library and their
download dir is reclaimed via an injectable rmrf.
api.js recorded a fictitious tmp/<infohash> (add) and library/<infohash>
(keep) path that no code ever moves files into — files download flat to
downloadDir. Record path.join(downloadDir, infohash) in both places so the
path resumeLibrary's rmrf acts on matches where files actually live.
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
60 lines
2.3 KiB
JavaScript
60 lines
2.3 KiB
JavaScript
// SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
|
// Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
|
// SecuBox-Deb :: secubox-torrent :: api.js — CyberMind https://cybermind.fr
|
|
//
|
|
// Fastify control API: torrent lifecycle (add/list/keep/remove) + status/health,
|
|
// plus the /stream mount that hands off to the Range streaming handler.
|
|
|
|
import Fastify from 'fastify';
|
|
import path from 'node:path';
|
|
import { handleStream } from './stream.js';
|
|
|
|
export function buildApi({ engine, library, diskFreeBytes }) {
|
|
const app = Fastify({ logger: false });
|
|
|
|
app.get('/api/v1/torrent/health', async () => ({ status: 'ok' }));
|
|
app.get('/api/v1/torrent/status', async () => ({
|
|
active: engine.client.torrents.length, disk_free: diskFreeBytes(), webrtc: engine.webrtc !== false }));
|
|
|
|
app.post('/api/v1/torrent/add', async (req, reply) => {
|
|
const magnet = (req.body || {}).magnet;
|
|
if (!magnet) return reply.code(400).send({ error: 'magnet required' });
|
|
let meta;
|
|
try { meta = await engine.add(magnet); }
|
|
catch (e) { return reply.code(504).send({ error: e.message }); }
|
|
library.add({ infohash: meta.infohash, name: meta.name, magnet,
|
|
path: path.join(engine.downloadDir, meta.infohash) });
|
|
return meta;
|
|
});
|
|
|
|
app.get('/api/v1/torrent/list', async () =>
|
|
library.list().map(r => ({ ...r, stats: engine.stats(r.infohash) })));
|
|
|
|
app.get('/api/v1/torrent/files/:infohash', async (req, reply) => {
|
|
const t = engine.get(req.params.infohash);
|
|
if (!t) return reply.code(404).send({ error: 'not found' });
|
|
return t.files.map((f, i) => ({ idx: i, name: f.name, length: f.length }));
|
|
});
|
|
|
|
app.post('/api/v1/torrent/keep/:infohash', async (req, reply) => {
|
|
const ih = req.params.infohash;
|
|
if (!library.get(ih)) return reply.code(404).send({ error: 'not found' });
|
|
library.keep(ih, path.join(engine.downloadDir, ih));
|
|
return { status: 'kept' };
|
|
});
|
|
|
|
app.post('/api/v1/torrent/remove/:infohash', async (req) => {
|
|
engine.remove(req.params.infohash, { deleteData: true });
|
|
library.remove(req.params.infohash);
|
|
return { status: 'removed' };
|
|
});
|
|
|
|
app.get('/stream/:infohash/:fileIdx', (req, reply) => {
|
|
library.touch(req.params.infohash);
|
|
return handleStream(engine, req, reply.raw)
|
|
.then(() => { reply.hijack(); });
|
|
});
|
|
|
|
return app;
|
|
}
|