rmi-backend/alembic/versions/0001_baseline.py
cryptorugmunch c1d1fc82ee fix(infra): wire Alembic + remove docker-restart-in-web-request
P3.1 — Alembic wiring:
- alembic.ini: was hardcoded to Supabase env vars that never resolved.
  Now uses canonical DATABASE_URL (Postgres).
- alembic/env.py: reads DATABASE_URL env var (with async->sync driver
  conversion), falls back to alembic.ini default.
- alembic/versions/0001_baseline.py: captures core tables (tokens,
  alerts, news_items, scan_reports, rag_findings, x402_receipts,
  profiles, contract_audits, badges, chat_messages) that were previously
  created via scattered CREATE TABLE IF NOT EXISTS across the codebase.
  Uses IF NOT EXISTS for idempotent re-run.
- Documented: no ORM models, so autogenerate doesn't work. All
  migrations must be hand-written: alembic revision -m ...

P4.1 — async correctness:
- oracle_manipulation_detector.py:1287 was inside async def main() with
  time.sleep(30). Wrapped in async _monitor_loop() with asyncio.sleep().
- auto_healing.py:82 was a web endpoint that ran os.system('docker
  restart rmi-redis') directly from a FastAPI request — security hole.
  Replaced with audit-logged stub that requires manual operator action.
2026-07-07 17:56:57 +07:00

185 lines
No EOL
4.9 KiB
Python

"""Baseline schema migration.
Captures the core tables that were created via scattered ``CREATE TABLE
IF NOT EXISTS`` statements across the codebase before Alembic existed.
This migration is idempotent (uses ``IF NOT EXISTS``) so re-running it
on a database that already has these tables is a no-op.
After this baseline, all schema changes MUST go through Alembic:
alembic revision -m "add_my_field"
# edit alembic/versions/00xx_add_my_field.py
alembic upgrade head
The codebase has no SQLAlchemy ORM models (uses raw SQL + Supabase),
so ``alembic revision --autogenerate`` cannot introspect. All
migrations must be written by hand.
"""
from __future__ import annotations
from alembic import op
# revision identifiers, used by Alembic.
revision = "0001_baseline"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
"""Baseline: create the core tables that were previously scattered."""
# catalog schema (app/catalog/init_schema.py)
op.execute(
"""
CREATE TABLE IF NOT EXISTS tokens (
address TEXT PRIMARY KEY,
chain TEXT NOT NULL,
name TEXT,
symbol TEXT,
decimals INT,
total_supply NUMERIC,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS alerts (
id BIGSERIAL PRIMARY KEY,
user_id TEXT NOT NULL,
alert_type TEXT NOT NULL,
severity TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS news_items (
id BIGSERIAL PRIMARY KEY,
source TEXT NOT NULL,
title TEXT,
url TEXT,
content TEXT,
sentiment NUMERIC,
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS scan_reports (
id BIGSERIAL PRIMARY KEY,
user_id TEXT,
target_address TEXT NOT NULL,
chain TEXT,
risk_score INT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS rag_findings (
id BIGSERIAL PRIMARY KEY,
collection TEXT NOT NULL,
content TEXT,
metadata JSONB,
score NUMERIC,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS x402_receipts (
id BIGSERIAL PRIMARY KEY,
agent_id TEXT,
tool TEXT NOT NULL,
tx_hash TEXT,
amount_usdc NUMERIC,
paid_via_x402 TEXT,
paid_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute("CREATE INDEX IF NOT EXISTS idx_x402_paid_at ON x402_receipts(paid_at DESC)")
op.execute("CREATE INDEX IF NOT EXISTS idx_x402_tool ON x402_receipts(tool)")
op.execute("CREATE INDEX IF NOT EXISTS idx_x402_agent ON x402_receipts(agent_id)")
# app/db_client.py schema
op.execute(
"""
CREATE TABLE IF NOT EXISTS profiles (
id TEXT PRIMARY KEY,
display_name TEXT,
email TEXT,
wallet_address TEXT,
wallet_chain TEXT,
tier TEXT DEFAULT 'FREE',
role TEXT DEFAULT 'USER',
xp INT DEFAULT 0,
level INT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS contract_audits (
id BIGSERIAL PRIMARY KEY,
contract_address TEXT NOT NULL,
chain TEXT,
report JSONB,
risk_score INT,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
# app/community_badges.py schema
op.execute(
"""
CREATE TABLE IF NOT EXISTS badges (
id TEXT PRIMARY KEY,
name TEXT,
description TEXT,
emoji TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
# app/routers/chat.py schema
op.execute(
"""
CREATE TABLE IF NOT EXISTS chat_messages (
id BIGSERIAL PRIMARY KEY,
user_id TEXT NOT NULL,
role TEXT,
content TEXT,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
)
"""
)
def downgrade() -> None:
"""Drop baseline tables (DESTRUCTIVE)."""
for table in [
"chat_messages",
"badges",
"contract_audits",
"profiles",
"x402_receipts",
"rag_findings",
"scan_reports",
"news_items",
"alerts",
"tokens",
]:
op.execute(f"DROP TABLE IF EXISTS {table} CASCADE")