mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-29 18:36:55 +00:00
feat(torrent): library.js SQLite + purge selection (ref #917)
Adds SQLite-backed torrent library (add/list/get/touch/keep/remove) and the expiredEphemeral() query the purge job uses to find stale kept=0 torrents (last_played_at < now-ttl). Includes package-lock.json and a lxc/app .gitignore for node_modules. Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
This commit is contained in:
parent
d45803db0c
commit
7aaa8d5409
1
packages/secubox-torrent/lxc/app/.gitignore
vendored
Normal file
1
packages/secubox-torrent/lxc/app/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules/
|
||||
34
packages/secubox-torrent/lxc/app/library.js
Normal file
34
packages/secubox-torrent/lxc/app/library.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
// Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
// SecuBox-Deb :: secubox-torrent :: library.js — CyberMind https://cybermind.fr
|
||||
//
|
||||
// SQLite-backed torrent library: ephemeral (kept=0) vs. kept torrents, plus
|
||||
// the selection query used by the purge job to find stale ephemeral entries.
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
export class Library {
|
||||
constructor(dbPath) {
|
||||
this.db = new Database(dbPath);
|
||||
this.db.exec(`CREATE TABLE IF NOT EXISTS torrents (
|
||||
infohash TEXT PRIMARY KEY, name TEXT, magnet TEXT, path TEXT,
|
||||
added_at INTEGER, last_played_at INTEGER, kept INTEGER DEFAULT 0)`);
|
||||
}
|
||||
add({ infohash, name, magnet, path }) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
this.db.prepare(`INSERT OR REPLACE INTO torrents
|
||||
(infohash,name,magnet,path,added_at,last_played_at,kept)
|
||||
VALUES (?,?,?,?,?,?,0)`).run(infohash, name, magnet, path, now, now);
|
||||
}
|
||||
list() { return this.db.prepare('SELECT * FROM torrents ORDER BY added_at DESC').all(); }
|
||||
get(infohash) { return this.db.prepare('SELECT * FROM torrents WHERE infohash=?').get(infohash) || null; }
|
||||
touch(infohash) { this.touchAt(infohash, Math.floor(Date.now() / 1000)); }
|
||||
touchAt(infohash, ts) { this.db.prepare('UPDATE torrents SET last_played_at=? WHERE infohash=?').run(ts, infohash); }
|
||||
keep(infohash, newPath) { this.db.prepare('UPDATE torrents SET kept=1, path=? WHERE infohash=?').run(newPath, infohash); }
|
||||
remove(infohash) { this.db.prepare('DELETE FROM torrents WHERE infohash=?').run(infohash); }
|
||||
expiredEphemeral(ttlSeconds, now = Math.floor(Date.now() / 1000)) {
|
||||
const cutoff = now - ttlSeconds;
|
||||
return this.db.prepare('SELECT infohash FROM torrents WHERE kept=0 AND last_played_at < ?')
|
||||
.all(cutoff).map(r => r.infohash);
|
||||
}
|
||||
}
|
||||
38
packages/secubox-torrent/lxc/app/library.test.js
Normal file
38
packages/secubox-torrent/lxc/app/library.test.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
// Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
// SecuBox-Deb :: secubox-torrent :: library.test.js — CyberMind https://cybermind.fr
|
||||
//
|
||||
// Tests for the SQLite-backed torrent library + ephemeral purge selection.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { Library } from './library.js';
|
||||
|
||||
const mk = () => new Library(':memory:');
|
||||
|
||||
test('add then list yields one ephemeral row', () => {
|
||||
const lib = mk();
|
||||
lib.add({ infohash: 'a', name: 'A', magnet: 'm', path: '/tmp/a' });
|
||||
const rows = lib.list();
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].kept, 0);
|
||||
});
|
||||
|
||||
test('keep flips kept and updates path', () => {
|
||||
const lib = mk();
|
||||
lib.add({ infohash: 'a', name: 'A', magnet: 'm', path: '/tmp/a' });
|
||||
lib.keep('a', '/data/torrent/library/a');
|
||||
assert.equal(lib.get('a').kept, 1);
|
||||
assert.equal(lib.get('a').path, '/data/torrent/library/a');
|
||||
});
|
||||
|
||||
test('expiredEphemeral returns only stale unkept torrents', () => {
|
||||
const lib = mk();
|
||||
lib.add({ infohash: 'old', name: 'O', magnet: 'm', path: '/tmp/o' });
|
||||
lib.add({ infohash: 'fresh', name: 'F', magnet: 'm', path: '/tmp/f' });
|
||||
lib.add({ infohash: 'kept', name: 'K', magnet: 'm', path: '/tmp/k' });
|
||||
lib.touchAt('old', 1000); lib.touchAt('fresh', 9000); lib.touchAt('kept', 1000);
|
||||
lib.keep('kept', '/data/k');
|
||||
const exp = lib.expiredEphemeral(3600, 10000); // now=10000, ttl=3600 → cutoff 6400
|
||||
assert.deepEqual(exp, ['old']);
|
||||
});
|
||||
3061
packages/secubox-torrent/lxc/app/package-lock.json
generated
Normal file
3061
packages/secubox-torrent/lxc/app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user