Files
eth_hedge_sim/control/backend/app/db.py
T
dekun da5eb4c18c Add Fleet control plane and split manage.sh deploy menu.
Strategy nodes gain fleet token APIs; control/ app for local ops; manage.sh offers strategy vs control one-click deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 10:46:40 +08:00

120 lines
3.6 KiB
Python

"""中控 SQLite。"""
from __future__ import annotations
from pathlib import Path
from threading import Lock
from typing import Any
from .config import get_control_settings
_SCHEMA = """
CREATE TABLE IF NOT EXISTS nodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
base_url TEXT NOT NULL UNIQUE,
token_sealed TEXT NOT NULL DEFAULT '',
created_at_ms INTEGER NOT NULL,
updated_at_ms INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
"""
_db: "ControlDB | None" = None
_lock = Lock()
def set_control_db(db: "ControlDB | None") -> None:
global _db
_db = db
def get_control_db() -> "ControlDB":
if _db is None:
raise RuntimeError("control db not initialized")
return _db
class ControlDB:
def __init__(self, path: Path | None = None) -> None:
import sqlite3
import time
settings = get_control_settings()
self.path = path or settings.db_path
self.path.parent.mkdir(parents=True, exist_ok=True)
self._conn = sqlite3.connect(str(self.path), check_same_thread=False)
self._conn.row_factory = sqlite3.Row
self._conn.executescript(_SCHEMA)
self._conn.commit()
self._lock = Lock()
# touch
_ = time.time()
def close(self) -> None:
self._conn.close()
def list_nodes(self) -> list[dict[str, Any]]:
with self._lock:
rows = self._conn.execute(
"SELECT id, name, base_url, token_sealed, created_at_ms, updated_at_ms FROM nodes ORDER BY id"
).fetchall()
return [dict(r) for r in rows]
def get_node(self, node_id: int) -> dict[str, Any] | None:
with self._lock:
row = self._conn.execute(
"SELECT id, name, base_url, token_sealed, created_at_ms, updated_at_ms FROM nodes WHERE id=?",
(node_id,),
).fetchone()
return dict(row) if row else None
def create_node(self, name: str, base_url: str) -> dict[str, Any]:
import time
now = int(time.time() * 1000)
name = name.strip()
base_url = base_url.strip().rstrip("/")
with self._lock:
cur = self._conn.execute(
"INSERT INTO nodes(name, base_url, token_sealed, created_at_ms, updated_at_ms) VALUES (?,?,?,?,?)",
(name, base_url, "", now, now),
)
self._conn.commit()
nid = int(cur.lastrowid)
return self.get_node(nid) # type: ignore[return-value]
def update_node(
self,
node_id: int,
*,
name: str | None = None,
base_url: str | None = None,
token_sealed: str | None = None,
) -> dict[str, Any] | None:
import time
node = self.get_node(node_id)
if not node:
return None
now = int(time.time() * 1000)
new_name = name.strip() if name is not None else node["name"]
new_url = base_url.strip().rstrip("/") if base_url is not None else node["base_url"]
new_tok = token_sealed if token_sealed is not None else node["token_sealed"]
with self._lock:
self._conn.execute(
"UPDATE nodes SET name=?, base_url=?, token_sealed=?, updated_at_ms=? WHERE id=?",
(new_name, new_url, new_tok, now, node_id),
)
self._conn.commit()
return self.get_node(node_id)
def delete_node(self, node_id: int) -> bool:
with self._lock:
cur = self._conn.execute("DELETE FROM nodes WHERE id=?", (node_id,))
self._conn.commit()
return cur.rowcount > 0