Add device diagnostics and P2P access controls
This commit is contained in:
@@ -37,6 +37,10 @@ export async function initP2pSchema() {
|
||||
fingerprint TEXT,
|
||||
profile TEXT,
|
||||
share INTEGER NOT NULL DEFAULT 1,
|
||||
user_agent TEXT,
|
||||
banned_at INTEGER,
|
||||
bytes_up INTEGER NOT NULL DEFAULT 0,
|
||||
bytes_down INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL,
|
||||
last_seen_at INTEGER NOT NULL
|
||||
);
|
||||
@@ -60,6 +64,10 @@ export async function initP2pSchema() {
|
||||
PRIMARY KEY (video_id, day)
|
||||
);
|
||||
`);
|
||||
for (const [name, decl] of [['user_agent','TEXT'],['banned_at','INTEGER'],['bytes_up','INTEGER NOT NULL DEFAULT 0'],['bytes_down','INTEGER NOT NULL DEFAULT 0']]) {
|
||||
try { await db.execute(`ALTER TABLE p2p_devices ADD COLUMN ${name} ${decl}`); }
|
||||
catch (e) { if (!/duplicate column/i.test(String(e.message))) throw e; }
|
||||
}
|
||||
// media_cache.sha256 — the cid of the current <id>.<gen>.mp4 (plan 009).
|
||||
try { await db.execute('ALTER TABLE media_cache ADD COLUMN sha256 TEXT'); }
|
||||
catch (e) { if (!/duplicate column/i.test(String(e.message))) throw e; }
|
||||
@@ -117,11 +125,11 @@ export async function revokeContent(cid) {
|
||||
|
||||
// ---- devices ------------------------------------------------------------------
|
||||
|
||||
export async function createDevice({ deviceId, secretHash, fingerprint, profile, now }) {
|
||||
export async function createDevice({ deviceId, secretHash, fingerprint, profile, userAgent, now }) {
|
||||
await db.execute({
|
||||
sql: `INSERT INTO p2p_devices (device_id, secret_hash, fingerprint, profile, share, created_at, last_seen_at)
|
||||
VALUES (?, ?, ?, ?, 1, ?, ?)`,
|
||||
args: [deviceId, secretHash, fingerprint || null, profile || null, now, now],
|
||||
sql: `INSERT INTO p2p_devices (device_id, secret_hash, fingerprint, profile, share, user_agent, created_at, last_seen_at)
|
||||
VALUES (?, ?, ?, ?, 1, ?, ?, ?)`,
|
||||
args: [deviceId, secretHash, fingerprint || null, profile || null, userAgent || null, now, now],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -139,6 +147,26 @@ export async function touchDevice(deviceId, { now, share, profile } = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function listAdminDevices({ limit = 100, offset = 0 } = {}) {
|
||||
const r = await db.execute({ sql: `SELECT d.device_id,d.fingerprint,d.profile,d.share,d.user_agent,d.banned_at,d.bytes_up,d.bytes_down,d.created_at,d.last_seen_at,
|
||||
COUNT(CASE WHEN h.status='active' THEN 1 END) AS holdings,
|
||||
COALESCE(SUM(CASE WHEN h.status='active' THEN c.size ELSE 0 END),0) AS holding_bytes
|
||||
FROM p2p_devices d LEFT JOIN p2p_holders h ON h.device_id=d.device_id LEFT JOIN p2p_content c ON c.cid=h.cid
|
||||
GROUP BY d.device_id ORDER BY d.last_seen_at DESC LIMIT ? OFFSET ?`, args: [Math.max(1, Math.min(200, limit)), Math.max(0, offset)] });
|
||||
return rowsOf(r);
|
||||
}
|
||||
export async function setDeviceBanned(deviceId, banned, now = Date.now()) {
|
||||
const r = await db.execute({ sql: 'UPDATE p2p_devices SET banned_at=? WHERE device_id=?', args: [banned ? now : null, deviceId] });
|
||||
return !!r.rowsAffected;
|
||||
}
|
||||
export async function removeDeviceHolders(deviceId, now = Date.now()) {
|
||||
const r = await db.execute({ sql: "UPDATE p2p_holders SET status='removed',removed_at=? WHERE device_id=? AND status='active'", args: [now, deviceId] });
|
||||
return Number(r.rowsAffected || 0);
|
||||
}
|
||||
export async function recordDeviceTraffic(deviceId, { up = 0, down = 0 } = {}) {
|
||||
await db.execute({ sql: 'UPDATE p2p_devices SET bytes_up=bytes_up+?,bytes_down=bytes_down+? WHERE device_id=?', args: [Math.max(0, Number(up) || 0), Math.max(0, Number(down) || 0), deviceId] });
|
||||
}
|
||||
|
||||
// ---- holders (persistent; never expired by time) ------------------------------
|
||||
|
||||
export async function upsertHolder({ cid, deviceId, trust = 'reported', now }) {
|
||||
|
||||
Reference in New Issue
Block a user