refactor: consolidate blockchain explorer into single app and update backup ignore patterns
- Remove standalone explorer-web app (README, HTML, package files) - Add /web endpoint to blockchain-explorer for web interface access - Update .gitignore to exclude application backup archives (*.tar.gz, *.zip) - Add backup documentation files to .gitignore (BACKUP_INDEX.md, README.md) - Consolidate explorer functionality into main blockchain-explorer application
This commit is contained in:
3
apps/wallet/src/app/ledger_mock/__init__.py
Normal file
3
apps/wallet/src/app/ledger_mock/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .sqlite_adapter import SQLiteLedgerAdapter, WalletRecord, WalletEvent
|
||||
|
||||
__all__ = ["SQLiteLedgerAdapter", "WalletRecord", "WalletEvent"]
|
||||
197
apps/wallet/src/app/ledger_mock/postgresql_adapter.py
Normal file
197
apps/wallet/src/app/ledger_mock/postgresql_adapter.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""PostgreSQL adapter for Wallet Daemon"""
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from typing import Optional, Dict, Any, List
|
||||
from datetime import datetime
|
||||
import json
|
||||
from aitbc.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
class PostgreSQLLedgerAdapter:
|
||||
"""PostgreSQL implementation of the wallet ledger"""
|
||||
|
||||
def __init__(self, db_config: Dict[str, Any]):
|
||||
self.db_config = db_config
|
||||
self.connection = None
|
||||
self._connect()
|
||||
|
||||
def _connect(self):
|
||||
"""Establish database connection"""
|
||||
try:
|
||||
self.connection = psycopg2.connect(
|
||||
cursor_factory=RealDictCursor,
|
||||
**self.db_config
|
||||
)
|
||||
logger.info("Connected to PostgreSQL wallet ledger")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to PostgreSQL: {e}")
|
||||
raise
|
||||
|
||||
def create_wallet(self, wallet_id: str, public_key: str, metadata: Dict[str, Any] = None) -> bool:
|
||||
"""Create a new wallet"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
INSERT INTO wallets (wallet_id, public_key, metadata)
|
||||
VALUES (%s, %s, %s)
|
||||
ON CONFLICT (wallet_id) DO UPDATE
|
||||
SET public_key = EXCLUDED.public_key,
|
||||
metadata = EXCLUDED.metadata,
|
||||
updated_at = NOW()
|
||||
""", (wallet_id, public_key, json.dumps(metadata or {})))
|
||||
|
||||
self.connection.commit()
|
||||
logger.info(f"Created wallet: {wallet_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create wallet {wallet_id}: {e}")
|
||||
self.connection.rollback()
|
||||
return False
|
||||
|
||||
def get_wallet(self, wallet_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get wallet information"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
SELECT wallet_id, public_key, metadata, created_at, updated_at
|
||||
FROM wallets
|
||||
WHERE wallet_id = %s
|
||||
""", (wallet_id,))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
return dict(result)
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get wallet {wallet_id}: {e}")
|
||||
return None
|
||||
|
||||
def list_wallets(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
|
||||
"""List all wallets"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
SELECT wallet_id, public_key, metadata, created_at, updated_at
|
||||
FROM wallets
|
||||
ORDER BY created_at DESC
|
||||
LIMIT %s OFFSET %s
|
||||
""", (limit, offset))
|
||||
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list wallets: {e}")
|
||||
return []
|
||||
|
||||
def add_wallet_event(self, wallet_id: str, event_type: str, payload: Dict[str, Any]) -> bool:
|
||||
"""Add an event to the wallet"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
INSERT INTO wallet_events (wallet_id, event_type, payload)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (wallet_id, event_type, json.dumps(payload)))
|
||||
|
||||
self.connection.commit()
|
||||
logger.debug(f"Added event {event_type} to wallet {wallet_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add event to wallet {wallet_id}: {e}")
|
||||
self.connection.rollback()
|
||||
return False
|
||||
|
||||
def get_wallet_events(self, wallet_id: str, limit: int = 100) -> List[Dict[str, Any]]:
|
||||
"""Get events for a wallet"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
SELECT id, event_type, payload, created_at
|
||||
FROM wallet_events
|
||||
WHERE wallet_id = %s
|
||||
ORDER BY created_at DESC
|
||||
LIMIT %s
|
||||
""", (wallet_id, limit))
|
||||
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get events for wallet {wallet_id}: {e}")
|
||||
return []
|
||||
|
||||
def update_wallet_metadata(self, wallet_id: str, metadata: Dict[str, Any]) -> bool:
|
||||
"""Update wallet metadata"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
UPDATE wallets
|
||||
SET metadata = %s, updated_at = NOW()
|
||||
WHERE wallet_id = %s
|
||||
""", (json.dumps(metadata), wallet_id))
|
||||
|
||||
self.connection.commit()
|
||||
return cursor.rowcount > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update metadata for wallet {wallet_id}: {e}")
|
||||
self.connection.rollback()
|
||||
return False
|
||||
|
||||
def delete_wallet(self, wallet_id: str) -> bool:
|
||||
"""Delete a wallet and all its events"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("""
|
||||
DELETE FROM wallets
|
||||
WHERE wallet_id = %s
|
||||
""", (wallet_id,))
|
||||
|
||||
self.connection.commit()
|
||||
return cursor.rowcount > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete wallet {wallet_id}: {e}")
|
||||
self.connection.rollback()
|
||||
return False
|
||||
|
||||
def get_wallet_stats(self) -> Dict[str, Any]:
|
||||
"""Get wallet statistics"""
|
||||
try:
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("SELECT COUNT(*) as total_wallets FROM wallets")
|
||||
total_wallets = cursor.fetchone()['total_wallets']
|
||||
|
||||
cursor.execute("SELECT COUNT(*) as total_events FROM wallet_events")
|
||||
total_events = cursor.fetchone()['total_events']
|
||||
|
||||
cursor.execute("""
|
||||
SELECT event_type, COUNT(*) as count
|
||||
FROM wallet_events
|
||||
GROUP BY event_type
|
||||
ORDER BY count DESC
|
||||
""")
|
||||
event_types = {row['event_type']: row['count'] for row in cursor.fetchall()}
|
||||
|
||||
return {
|
||||
"total_wallets": total_wallets,
|
||||
"total_events": total_events,
|
||||
"event_types": event_types
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get wallet stats: {e}")
|
||||
return {}
|
||||
|
||||
def close(self):
|
||||
"""Close the database connection"""
|
||||
if self.connection:
|
||||
self.connection.close()
|
||||
logger.info("PostgreSQL connection closed")
|
||||
|
||||
# Factory function
|
||||
def create_postgresql_adapter() -> PostgreSQLLedgerAdapter:
|
||||
"""Create a PostgreSQL ledger adapter"""
|
||||
config = {
|
||||
"host": "localhost",
|
||||
"database": "aitbc_wallet",
|
||||
"user": "aitbc_user",
|
||||
"password": "aitbc_password",
|
||||
"port": 5432
|
||||
}
|
||||
return PostgreSQLLedgerAdapter(config)
|
||||
106
apps/wallet/src/app/ledger_mock/sqlite_adapter.py
Normal file
106
apps/wallet/src/app/ledger_mock/sqlite_adapter.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletRecord:
|
||||
wallet_id: str
|
||||
public_key: str
|
||||
metadata: dict
|
||||
|
||||
|
||||
@dataclass
|
||||
class WalletEvent:
|
||||
wallet_id: str
|
||||
event_type: str
|
||||
payload: dict
|
||||
|
||||
|
||||
class SQLiteLedgerAdapter:
|
||||
def __init__(self, db_path: Path) -> None:
|
||||
self._db_path = db_path
|
||||
self._ensure_schema()
|
||||
|
||||
def _connect(self) -> sqlite3.Connection:
|
||||
conn = sqlite3.connect(self._db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
def _ensure_schema(self) -> None:
|
||||
self._db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with self._connect() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS wallets (
|
||||
wallet_id TEXT PRIMARY KEY,
|
||||
public_key TEXT NOT NULL,
|
||||
metadata TEXT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS wallet_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
wallet_id TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
payload TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(wallet_id) REFERENCES wallets(wallet_id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
def upsert_wallet(self, wallet_id: str, public_key: str, metadata: dict) -> None:
|
||||
payload = json.dumps(metadata)
|
||||
with self._connect() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO wallets(wallet_id, public_key, metadata)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(wallet_id) DO UPDATE SET public_key=excluded.public_key, metadata=excluded.metadata
|
||||
""",
|
||||
(wallet_id, public_key, payload),
|
||||
)
|
||||
|
||||
def get_wallet(self, wallet_id: str) -> Optional[WalletRecord]:
|
||||
with self._connect() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT wallet_id, public_key, metadata FROM wallets WHERE wallet_id = ?",
|
||||
(wallet_id,),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
return WalletRecord(wallet_id=row["wallet_id"], public_key=row["public_key"], metadata=json.loads(row["metadata"]))
|
||||
|
||||
def list_wallets(self) -> Iterable[WalletRecord]:
|
||||
with self._connect() as conn:
|
||||
rows = conn.execute("SELECT wallet_id, public_key, metadata FROM wallets").fetchall()
|
||||
for row in rows:
|
||||
yield WalletRecord(wallet_id=row["wallet_id"], public_key=row["public_key"], metadata=json.loads(row["metadata"]))
|
||||
|
||||
def record_event(self, wallet_id: str, event_type: str, payload: dict) -> None:
|
||||
data = json.dumps(payload)
|
||||
with self._connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO wallet_events(wallet_id, event_type, payload) VALUES (?, ?, ?)",
|
||||
(wallet_id, event_type, data),
|
||||
)
|
||||
|
||||
def list_events(self, wallet_id: str) -> Iterable[WalletEvent]:
|
||||
with self._connect() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT wallet_id, event_type, payload FROM wallet_events WHERE wallet_id = ? ORDER BY id",
|
||||
(wallet_id,),
|
||||
).fetchall()
|
||||
for row in rows:
|
||||
yield WalletEvent(
|
||||
wallet_id=row["wallet_id"],
|
||||
event_type=row["event_type"],
|
||||
payload=json.loads(row["payload"]),
|
||||
)
|
||||
Reference in New Issue
Block a user