secubox-openwrt/package/secubox/secubox-app-rtty-remote/files/usr/lib/secubox/rtty-session.sh
CyberMind-FR 2c763c3583 feat(rtty-remote): Add RTTY Remote Control module with support panel
New packages:
- secubox-app-rtty-remote: Backend with rttyctl CLI
- luci-app-rtty-remote: LuCI dashboard with KISS theme

Features:
- RPCD Proxy: Execute remote ubus calls to mesh nodes over HTTP
- Support Panel: Remote assistance with shareable session codes
- Session tracking: SQLite database for audit trail
- Quick actions: System info, network, services, vhosts, reboot
- RPC Console: Execute arbitrary ubus commands

CLI commands:
- rttyctl nodes - List mesh nodes
- rttyctl rpc <node> <object> <method> - Execute remote RPCD
- rttyctl rpc-list <node> - List available objects
- rttyctl sessions - Show session history

LuCI views:
- dashboard.js: Node management, stats, RPC console
- support.js: Remote assistance with session codes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-08 11:17:08 +01:00

195 lines
5.1 KiB
Bash

# rtty-session.sh - Session Management Library for RTTY Remote
#
# SQLite-based session storage and replay functionality
#
SESSION_DB="${SESSION_DB:-/srv/rtty-remote/sessions.db}"
# Initialize session database
session_init_db() {
mkdir -p "$(dirname "$SESSION_DB")" 2>/dev/null
sqlite3 "$SESSION_DB" << 'EOF'
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
node_id TEXT NOT NULL,
node_address TEXT,
type TEXT NOT NULL, -- 'terminal', 'rpc', 'replay'
started_at INTEGER NOT NULL,
ended_at INTEGER,
duration INTEGER,
bytes_sent INTEGER DEFAULT 0,
bytes_recv INTEGER DEFAULT 0,
commands TEXT, -- JSON array of commands/calls
label TEXT,
metadata TEXT -- JSON metadata
);
CREATE TABLE IF NOT EXISTS rpc_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER,
object TEXT NOT NULL,
method TEXT NOT NULL,
params TEXT,
result TEXT,
status_code INTEGER,
executed_at INTEGER NOT NULL,
duration_ms INTEGER,
FOREIGN KEY(session_id) REFERENCES sessions(id)
);
CREATE INDEX IF NOT EXISTS idx_sessions_node ON sessions(node_id);
CREATE INDEX IF NOT EXISTS idx_sessions_started ON sessions(started_at);
CREATE INDEX IF NOT EXISTS idx_rpc_log_session ON rpc_log(session_id);
EOF
}
# Start a new session
# $1 = node_id
# $2 = node_address
# $3 = type (terminal|rpc|replay)
session_start() {
local node_id="$1"
local node_address="$2"
local type="${3:-rpc}"
local now=$(date +%s)
session_init_db
sqlite3 "$SESSION_DB" "INSERT INTO sessions (node_id, node_address, type, started_at) VALUES ('$node_id', '$node_address', '$type', $now)"
# Return session ID
sqlite3 "$SESSION_DB" "SELECT last_insert_rowid()"
}
# End a session
# $1 = session_id
session_end() {
local session_id="$1"
local now=$(date +%s)
sqlite3 "$SESSION_DB" "UPDATE sessions SET ended_at = $now, duration = $now - started_at WHERE id = $session_id"
}
# Log an RPC call
# $1 = session_id
# $2 = object
# $3 = method
# $4 = params (JSON)
# $5 = result (JSON)
# $6 = status_code
# $7 = duration_ms
session_log_rpc() {
local session_id="$1"
local object="$2"
local method="$3"
local params="$4"
local result="$5"
local status_code="${6:-0}"
local duration_ms="${7:-0}"
local now=$(date +%s)
# Escape single quotes
params=$(echo "$params" | sed "s/'/''/g")
result=$(echo "$result" | sed "s/'/''/g")
sqlite3 "$SESSION_DB" "INSERT INTO rpc_log (session_id, object, method, params, result, status_code, executed_at, duration_ms) VALUES ($session_id, '$object', '$method', '$params', '$result', $status_code, $now, $duration_ms)"
}
# List sessions
# $1 = node_id (optional filter)
# $2 = limit (default 50)
session_list() {
local node_id="$1"
local limit="${2:-50}"
session_init_db
local where=""
[ -n "$node_id" ] && where="WHERE node_id = '$node_id'"
sqlite3 -json "$SESSION_DB" "SELECT id, node_id, node_address, type, datetime(started_at, 'unixepoch') as started, duration, label FROM sessions $where ORDER BY started_at DESC LIMIT $limit"
}
# Get session details
# $1 = session_id
session_get() {
local session_id="$1"
session_init_db
sqlite3 -json "$SESSION_DB" "SELECT * FROM sessions WHERE id = $session_id"
}
# Get RPC log for session
# $1 = session_id
session_get_rpc_log() {
local session_id="$1"
sqlite3 -json "$SESSION_DB" "SELECT * FROM rpc_log WHERE session_id = $session_id ORDER BY executed_at"
}
# Label a session
# $1 = session_id
# $2 = label
session_label() {
local session_id="$1"
local label="$2"
sqlite3 "$SESSION_DB" "UPDATE sessions SET label = '$label' WHERE id = $session_id"
}
# Delete a session
# $1 = session_id
session_delete() {
local session_id="$1"
sqlite3 "$SESSION_DB" "DELETE FROM rpc_log WHERE session_id = $session_id"
sqlite3 "$SESSION_DB" "DELETE FROM sessions WHERE id = $session_id"
}
# Export session to JSON
# $1 = session_id
session_export() {
local session_id="$1"
local session=$(session_get "$session_id")
local rpc_log=$(session_get_rpc_log "$session_id")
cat << EOF
{
"session": $session,
"rpc_log": $rpc_log
}
EOF
}
# Cleanup old sessions
# $1 = days (default 7)
session_cleanup() {
local days="${1:-7}"
local cutoff=$(($(date +%s) - (days * 86400)))
sqlite3 "$SESSION_DB" "DELETE FROM rpc_log WHERE session_id IN (SELECT id FROM sessions WHERE ended_at < $cutoff AND label IS NULL)"
sqlite3 "$SESSION_DB" "DELETE FROM sessions WHERE ended_at < $cutoff AND label IS NULL"
}
# Get session statistics
session_stats() {
session_init_db
local total=$(sqlite3 "$SESSION_DB" "SELECT COUNT(*) FROM sessions")
local active=$(sqlite3 "$SESSION_DB" "SELECT COUNT(*) FROM sessions WHERE ended_at IS NULL")
local rpc_calls=$(sqlite3 "$SESSION_DB" "SELECT COUNT(*) FROM rpc_log")
local nodes=$(sqlite3 "$SESSION_DB" "SELECT COUNT(DISTINCT node_id) FROM sessions")
cat << EOF
{
"total_sessions": $total,
"active_sessions": $active,
"total_rpc_calls": $rpc_calls,
"unique_nodes": $nodes
}
EOF
}