Stage 04.1 - storage foundation (PostgreSQL) and system dashboard UI

This commit is contained in:
2026-04-14 21:38:52 +03:00
parent 1deb676585
commit c35deeaefa
15 changed files with 657 additions and 90 deletions

33
app/src/storage/models.py Normal file
View File

@@ -0,0 +1,33 @@
from __future__ import annotations
from dataclasses import dataclass
@dataclass(slots=True)
class BalanceSnapshotRecord:
id: int | None
created_at: str
source: str
payload_json: str
@dataclass(slots=True)
class JournalEventRecord:
id: int | None
created_at: str
level: str
event_type: str
message: str
payload_json: str | None
@dataclass(slots=True)
class OrderDraftRecord:
id: int | None
created_at: str
symbol: str
side: str
order_type: str
quantity: str
status: str
payload_json: str | None

44
app/src/storage/schema.py Normal file
View File

@@ -0,0 +1,44 @@
from __future__ import annotations
from src.storage.session import get_connection
DDL = [
'''
CREATE TABLE IF NOT EXISTS balance_snapshots (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
source TEXT NOT NULL,
payload_json JSONB NOT NULL
)
''',
'''
CREATE TABLE IF NOT EXISTS journal_events (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
level TEXT NOT NULL,
event_type TEXT NOT NULL,
message TEXT NOT NULL,
payload_json JSONB
)
''',
'''
CREATE TABLE IF NOT EXISTS order_drafts (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
symbol TEXT NOT NULL,
side TEXT NOT NULL,
order_type TEXT NOT NULL,
quantity NUMERIC(36, 18) NOT NULL,
status TEXT NOT NULL,
payload_json JSONB
)
'''
]
def init_schema() -> None:
with get_connection() as connection:
with connection.cursor() as cursor:
for statement in DDL:
cursor.execute(statement)

View File

@@ -0,0 +1,42 @@
from __future__ import annotations
from contextlib import contextmanager
import psycopg
from src.core.config import load_settings
def build_dsn() -> str:
settings = load_settings()
password_part = settings.db_password.replace("@", "%40")
return (
f"postgresql://{settings.db_user}:{password_part}"
f"@{settings.db_host}:{settings.db_port}/{settings.db_name}"
)
@contextmanager
def get_connection():
connection = psycopg.connect(build_dsn(), autocommit=False)
try:
yield connection
connection.commit()
finally:
connection.close()
def check_database_health() -> tuple[bool, str]:
try:
with get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT version()")
row = cursor.fetchone()
if row is None:
return False, "PostgreSQL ping returned no rows."
version = str(row[0]).strip()
except Exception as exc:
return False, f"PostgreSQL error: {exc}"
return True, version