mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-30 00:19:30 +00:00
fix(torrent): handle WebTorrent error events (bad magnet/torrent no longer crashes service) + install-lxc restarts on upgrade + npm-ci lock-hash guard (ref #917)
- engine: client.on('error') + torrent.on('error',reject) — invalid input rejects cleanly (was: unhandled 'error'→process crash, exposed by /add-file with bad bytes)
- install-lxc: 'systemctl enable --now' does NOT restart a running unit → in-place upgrade kept old code; add explicit restart
- install-lxc: skip the ~10min npm ci native rebuild when package-lock hash unchanged
- 28 node tests (+error-path)
Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
This commit is contained in:
parent
5ec54ab52b
commit
4c87101e43
|
|
@ -10,6 +10,10 @@ export class Engine {
|
|||
this.webrtc = webrtc;
|
||||
// webrtc=false (spike fallback) tells WebTorrent to skip the WebRTC transport.
|
||||
this.client = new WebTorrentCtor(webrtc ? {} : { tracker: { wrtc: false } });
|
||||
// A bad magnet/URL/.torrent (or a peer/tracker fault) makes WebTorrent
|
||||
// emit 'error' on the CLIENT — unhandled it crashes the whole process.
|
||||
// Swallow it here (per-add errors are surfaced by add()'s own handler).
|
||||
if (typeof this.client.on === 'function') this.client.on('error', () => {});
|
||||
}
|
||||
// torrentId is anything WebTorrent's add() accepts: a magnet URI, an http(s)
|
||||
// URL to a .torrent file (fetched by WebTorrent), a .torrent Buffer, or an
|
||||
|
|
@ -32,6 +36,10 @@ export class Engine {
|
|||
files: t.files.map((f, i) => ({ idx: i, name: f.name, length: f.length, type: ext(f.name) })),
|
||||
});
|
||||
};
|
||||
t.on('error', (err) => {
|
||||
clearTimeout(to);
|
||||
reject(err instanceof Error ? err : new Error(String(err || 'torrent error')));
|
||||
});
|
||||
t.on('metadata', done);
|
||||
if (t.files && t.files.length) done();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,3 +42,11 @@ test('remove frees capacity for new add', async () => {
|
|||
const r2 = await eng.add('magnet:?xt=urn:btih:' + 'b'.repeat(40));
|
||||
assert.equal(r2.infohash, 'b'.repeat(40));
|
||||
});
|
||||
|
||||
test('add rejects (never hangs/crashes) when the torrent emits error', async () => {
|
||||
const client = new FakeWebTorrent();
|
||||
const eng = new Engine({ WebTorrentCtor: function () { return client; },
|
||||
downloadDir: '/tmp', maxActive: 5, webrtc: true });
|
||||
client._next = new FakeTorrent('e'.repeat(40), 'Bad', [], { emitError: true });
|
||||
await assert.rejects(() => eng.add('magnet:?xt=urn:btih:bad'), /bad torrent/);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,15 +9,19 @@ export class FakeFile {
|
|||
}
|
||||
|
||||
export class FakeTorrent {
|
||||
constructor(infoHash, name, files, { failMeta = false } = {}) {
|
||||
constructor(infoHash, name, files, { failMeta = false, emitError = false } = {}) {
|
||||
this.infoHash = infoHash; this.name = name;
|
||||
this.magnetURI = 'magnet:?xt=urn:btih:' + infoHash;
|
||||
this.files = files.map((f, i) => Object.assign(new FakeFile(f.name, f.length), { idx: i }));
|
||||
this.progress = 0.1; this.downloadSpeed = 1000; this.uploadSpeed = 500;
|
||||
this.numPeers = 3; this.wires = [{ type: 'webrtc' }, { type: 'tcp' }];
|
||||
this._failMeta = failMeta; this._handlers = {}; this._client = null;
|
||||
this._failMeta = failMeta; this._emitError = emitError; this._handlers = {}; this._client = null;
|
||||
}
|
||||
on(ev, cb) {
|
||||
this._handlers[ev] = cb;
|
||||
if (ev === 'error' && this._emitError) queueMicrotask(() => cb(new Error('bad torrent')));
|
||||
if (ev === 'metadata' && !this._failMeta && !this._emitError) queueMicrotask(cb);
|
||||
}
|
||||
on(ev, cb) { this._handlers[ev] = cb; if (ev === 'metadata' && !this._failMeta) queueMicrotask(cb); }
|
||||
destroy(_opts, cb) {
|
||||
if (this._client) {
|
||||
const idx = this._client.torrents.indexOf(this);
|
||||
|
|
@ -35,5 +39,6 @@ export class FakeWebTorrent {
|
|||
t._client = this; this.torrents.push(t); if (cb) queueMicrotask(() => cb(t)); return t;
|
||||
}
|
||||
get(infohash) { return this.torrents.find(t => t.infoHash === infohash) || null; }
|
||||
on() {} // real WebTorrent client is an EventEmitter; engine attaches 'error'
|
||||
destroy(cb) { if (cb) cb(); }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,18 @@ APP_SRC="${SECUBOX_APP_SRC:-/usr/lib/secubox/torrent/app}"
|
|||
la mkdir -p /opt/secubox-torrent/app /opt/secubox-torrent/www
|
||||
tar -C "$APP_SRC" -cf - . | la tar -C /opt/secubox-torrent/app -xf -
|
||||
tar -C /usr/share/secubox/www/torrent -cf - . | la tar -C /opt/secubox-torrent/www -xf -
|
||||
la bash -lc 'cd /opt/secubox-torrent/app && npm ci --omit=dev || npm install --omit=dev'
|
||||
# npm ci wipes + rebuilds node_modules, which recompiles the native deps
|
||||
# (@roamhq/wrtc's node-datachannel is ~10min on arm64). Only pay that on a
|
||||
# code-only upgrade when the lockfile actually changed — otherwise the
|
||||
# already-built node_modules is fine and we just reload the JS.
|
||||
LOCK_HASH="$(sha256sum "$APP_SRC/package-lock.json" 2>/dev/null | awk '{print $1}')"
|
||||
if [ -n "$LOCK_HASH" ] && [ "$LOCK_HASH" = "$(cat "$STATE_DIR/.npm-lock-hash" 2>/dev/null)" ] \
|
||||
&& la test -f /opt/secubox-torrent/app/node_modules/better-sqlite3/build/Release/better_sqlite3.node; then
|
||||
log "deps unchanged (lockfile hash match) — skipping npm ci"
|
||||
else
|
||||
la bash -lc 'cd /opt/secubox-torrent/app && npm ci --omit=dev || npm install --omit=dev'
|
||||
[ -n "$LOCK_HASH" ] && printf '%s\n' "$LOCK_HASH" > "$STATE_DIR/.npm-lock-hash"
|
||||
fi
|
||||
# env file from host TOML values (exported by postinst into these vars)
|
||||
la bash -c "cat > /opt/secubox-torrent/torrent.env <<EOF
|
||||
TORRENT_DOWNLOAD_DIR=/data/torrent
|
||||
|
|
@ -89,5 +100,9 @@ EOF"
|
|||
# /usr/lib/secubox/torrent/ dir so $(dirname "$0") resolves on the board)
|
||||
tar -C "$(dirname "$0")" -cf - secubox-torrent.service | la tar -C /etc/systemd/system -xf -
|
||||
la systemctl daemon-reload
|
||||
la systemctl enable --now secubox-torrent.service
|
||||
la systemctl enable secubox-torrent.service
|
||||
# `enable --now` does NOT restart an already-running unit — so an in-place app
|
||||
# upgrade (re-copied code above) would keep serving the OLD code. Restart so
|
||||
# the new code is actually loaded (start-if-stopped is covered by restart too).
|
||||
la systemctl restart secubox-torrent.service
|
||||
log "torrent LXC app deployed + started on $LXC_IP:8090"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user