chore: remote forwarding integration

This commit is contained in:
2026-07-17 18:16:11 +08:00
parent d12c74649a
commit ec9f854136
14 changed files with 1660 additions and 77 deletions
+86
View File
@@ -42,6 +42,23 @@ CREATE TABLE IF NOT EXISTS unread (
device_id TEXT PRIMARY KEY,
count INTEGER NOT NULL DEFAULT 0
);
-- 远程操作审计 (terminal / forward / usb)
CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER NOT NULL,
action TEXT NOT NULL,
source_device TEXT,
target_device TEXT,
session_id TEXT,
payload_json TEXT,
result TEXT NOT NULL,
bytes_in INTEGER NOT NULL DEFAULT 0,
bytes_out INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_audit_ts ON audit_log(ts DESC);
CREATE INDEX IF NOT EXISTS idx_audit_session ON audit_log(session_id);
`)
// 迁移: 给老库补 ignored 列 (用 try/catch 包住, 已存在会抛)
@@ -240,3 +257,72 @@ export function totalUnread(): number {
const r = db.prepare(`SELECT COALESCE(SUM(count), 0) AS s FROM unread`).get() as { s: number }
return r.s
}
// 审计日志 (远程操作: terminal / forward / usb)
export interface AuditRow {
id: number
ts: number
action: string
source_device: string | null
target_device: string | null
session_id: string | null
payload_json: string | null
result: string
bytes_in: number
bytes_out: number
}
const insertAuditStmt = db.prepare(`
INSERT INTO audit_log
(ts, action, source_device, target_device, session_id, payload_json, result, bytes_in, bytes_out)
VALUES
(@ts, @action, @source_device, @target_device, @session_id, @payload_json, @result, @bytes_in, @bytes_out)
`)
export function recordAudit(args: {
action: string
source?: string | null
target?: string | null
sessionId?: string | null
payload?: any
result: 'ok' | 'denied' | 'error' | 'closed'
bytesIn?: number
bytesOut?: number
}): void {
insertAuditStmt.run({
ts: Date.now(),
action: args.action,
source_device: args.source ?? null,
target_device: args.target ?? null,
session_id: args.sessionId ?? null,
payload_json: args.payload !== undefined ? JSON.stringify(args.payload) : null,
result: args.result,
bytes_in: args.bytesIn ?? 0,
bytes_out: args.bytesOut ?? 0,
})
}
export function listAudit(opts: { limit?: number; since?: number; sessionId?: string } = {}): AuditRow[] {
const limit = Math.min(Math.max(opts.limit ?? 200, 1), 2000)
if (opts.sessionId) {
return db.prepare(`SELECT * FROM audit_log WHERE session_id = ? ORDER BY ts DESC LIMIT ?`).all(opts.sessionId, limit) as AuditRow[]
}
if (opts.since) {
return db.prepare(`SELECT * FROM audit_log WHERE ts >= ? ORDER BY ts DESC LIMIT ?`).all(opts.since, limit) as AuditRow[]
}
return db.prepare(`SELECT * FROM audit_log ORDER BY ts DESC LIMIT ?`).all(limit) as AuditRow[]
}
export function pruneAudit(olderThanMs: number): number {
const r = db.prepare(`DELETE FROM audit_log WHERE ts < ?`).run(Date.now() - olderThanMs)
return r.changes
}
// 远程操作白名单 (按 deviceId -> { terminal, forward, usb })
// 写入 settings.json (electron-store) 而不是 DB, 便于跨设备查看与回滚
export interface RemoteAllowRow {
device_id: string
terminal: number // 0/1
forward: number
usb: number
}