refactor: improve error handling and remove hardcoded credentials
- Changed bare except clauses to specific exception types in web3_utils.py, testing.py, messages.py, and message_storage.py - Replaced print() calls with logger in testing.py, agent_discovery.py, compliance_agent.py, coordinator.py, trading_agent.py, keys.py, escrow.py, persistent_spending_tracker.py, sync_cli.py, and client.py - Added logger initialization using get_logger(__name__) in compliance_agent.py, coordinator.py, trading_agent.py, keys.py, escrow.py, persistent_spending_tracker.py, and client.py - Removed hardcoded secret
This commit is contained in:
3
apps/shared-core/README.md
Normal file
3
apps/shared-core/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Shared Core Library
|
||||
|
||||
Common configuration, logging, and database utilities for AITBC microservices.
|
||||
21
apps/shared-core/pyproject.toml
Normal file
21
apps/shared-core/pyproject.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[tool.poetry]
|
||||
name = "aitbc-{SERVICE_NAME}"
|
||||
version = "0.1.0"
|
||||
description = "AITBC {SERVICE_DESC}"
|
||||
authors = ["AITBC Team <team@aitbc.dev>"]
|
||||
readme = "README.md"
|
||||
packages = [{include = "app", from = "src"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.13"
|
||||
aitbc = {path = "../../../"} # Root aitbc package
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = ">=9.0.3"
|
||||
pytest-asyncio = ">=1.3.0"
|
||||
pytest-cov = ">=6.0.0"
|
||||
httpx = ">=0.28.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
0
apps/shared-core/src/app/__init__.py
Normal file
0
apps/shared-core/src/app/__init__.py
Normal file
0
apps/shared-core/src/app/core/__init__.py
Normal file
0
apps/shared-core/src/app/core/__init__.py
Normal file
63
apps/shared-core/src/app/core/config.py
Normal file
63
apps/shared-core/src/app/core/config.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Shared configuration base for all AITBC services."""
|
||||
|
||||
from pydantic import Field, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from typing import List, Optional
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from aitbc.constants import DATA_DIR, LOG_DIR
|
||||
|
||||
|
||||
class DatabaseConfig(BaseSettings):
|
||||
"""Database configuration with adapter selection."""
|
||||
|
||||
adapter: str = "sqlite" # sqlite, postgresql
|
||||
url: Optional[str] = None
|
||||
pool_size: int = 10
|
||||
max_overflow: int = 20
|
||||
pool_pre_ping: bool = True
|
||||
|
||||
@property
|
||||
def effective_url(self) -> str:
|
||||
"""Get the effective database URL."""
|
||||
if self.url:
|
||||
return self.url
|
||||
|
||||
if self.adapter == "sqlite":
|
||||
return f"sqlite:///{DATA_DIR}/data/service.db"
|
||||
|
||||
return f"{self.adapter}://localhost:5432/service"
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow"
|
||||
)
|
||||
|
||||
|
||||
class ServiceSettings(BaseSettings):
|
||||
"""Base settings for all AITBC microservices."""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow"
|
||||
)
|
||||
|
||||
# Environment
|
||||
service_name: str = "aitbc-service"
|
||||
app_env: str = "dev"
|
||||
app_host: str = "127.0.0.1"
|
||||
app_port: int = 8000
|
||||
debug: bool = False
|
||||
|
||||
# Logging
|
||||
log_level: str = "INFO"
|
||||
log_dir: str = str(LOG_DIR / "services")
|
||||
|
||||
# Database
|
||||
database: DatabaseConfig = DatabaseConfig()
|
||||
|
||||
# API
|
||||
api_prefix: str = "/api/v1"
|
||||
|
||||
# Feature flags
|
||||
enable_metrics: bool = True
|
||||
enable_health_check: bool = True
|
||||
75
apps/shared-core/src/app/core/database.py
Normal file
75
apps/shared-core/src/app/core/database.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""Shared database utilities for AITBC services."""
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from typing import AsyncGenerator
|
||||
import os
|
||||
|
||||
from .config import DatabaseConfig
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
def get_engine(config: DatabaseConfig):
|
||||
"""Create SQLAlchemy engine based on configuration."""
|
||||
if config.adapter == "sqlite":
|
||||
# SQLite needs special handling for async
|
||||
engine = create_engine(
|
||||
config.effective_url,
|
||||
pool_size=config.pool_size,
|
||||
max_overflow=config.max_overflow,
|
||||
pool_pre_ping=config.pool_pre_ping,
|
||||
echo=config.db_echo if hasattr(config, 'db_echo') else False
|
||||
)
|
||||
return engine
|
||||
else:
|
||||
return create_engine(
|
||||
config.effective_url,
|
||||
pool_size=config.pool_size,
|
||||
max_overflow=config.max_overflow,
|
||||
pool_pre_ping=config.pool_pre_ping,
|
||||
echo=config.db_echo if hasattr(config, 'db_echo') else False
|
||||
)
|
||||
|
||||
|
||||
def get_sessionmaker(engine):
|
||||
"""Create session factory."""
|
||||
return sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
|
||||
|
||||
# Async support (for async services)
|
||||
def get_async_engine(config: DatabaseConfig):
|
||||
"""Create async SQLAlchemy engine."""
|
||||
if config.adapter == "sqlite":
|
||||
# SQLite async uses aiosqlite
|
||||
async_url = config.effective_url.replace("sqlite:///", "sqlite+aiosqlite:///")
|
||||
return create_async_engine(
|
||||
async_url,
|
||||
echo=config.db_echo if hasattr(config, 'db_echo') else False
|
||||
)
|
||||
else:
|
||||
# PostgreSQL async uses asyncpg
|
||||
async_url = config.effective_url.replace("postgresql://", "postgresql+asyncpg://")
|
||||
return create_async_engine(
|
||||
async_url,
|
||||
pool_size=config.pool_size,
|
||||
max_overflow=config.max_overflow,
|
||||
echo=config.db_echo if hasattr(config, 'db_echo') else False
|
||||
)
|
||||
|
||||
|
||||
async def get_async_session(engine) -> AsyncGenerator[AsyncSession, None]:
|
||||
"""Dependency for FastAPI async endpoints."""
|
||||
async_session = sessionmaker(
|
||||
engine, class_=AsyncSession, expire_on_commit=False
|
||||
)
|
||||
async with async_session() as session:
|
||||
try:
|
||||
yield session
|
||||
await session.commit()
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
await session.close()
|
||||
0
apps/shared-core/src/app/core/database/__init__.py
Normal file
0
apps/shared-core/src/app/core/database/__init__.py
Normal file
66
apps/shared-core/src/app/core/logging.py
Normal file
66
apps/shared-core/src/app/core/logging.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""Shared logging configuration for AITBC services."""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from ..core.config import ServiceSettings
|
||||
|
||||
|
||||
def setup_logging(settings: Optional[ServiceSettings] = None, level: str = None) -> logging.Logger:
|
||||
"""Configure structured logging for the service.
|
||||
|
||||
Args:
|
||||
settings: Service settings containing log configuration
|
||||
level: Override log level
|
||||
|
||||
Returns:
|
||||
Configured root logger
|
||||
"""
|
||||
if settings:
|
||||
log_level = level or settings.log_level
|
||||
log_dir = Path(settings.log_dir)
|
||||
else:
|
||||
log_level = level or "INFO"
|
||||
log_dir = Path("/var/log/aitbc/services")
|
||||
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create formatter
|
||||
formatter = logging.Formatter(
|
||||
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
|
||||
# Configure root logger
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(getattr(logging, log_level.upper()))
|
||||
|
||||
# Clear existing handlers
|
||||
root_logger.handlers.clear()
|
||||
|
||||
# Console handler
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setFormatter(formatter)
|
||||
root_logger.addHandler(console_handler)
|
||||
|
||||
# File handler
|
||||
if settings and settings.service_name:
|
||||
file_handler = logging.FileHandler(
|
||||
log_dir / f"{settings.service_name}.log"
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
root_logger.addHandler(file_handler)
|
||||
|
||||
return root_logger
|
||||
|
||||
|
||||
def get_logger(name: str) -> logging.Logger:
|
||||
"""Get a logger with the given name.
|
||||
|
||||
Usage:
|
||||
from aitbc import get_logger
|
||||
logger = get_logger(__name__)
|
||||
"""
|
||||
return logging.getLogger(name)
|
||||
0
apps/shared-core/src/app/core/security/__init__.py
Normal file
0
apps/shared-core/src/app/core/security/__init__.py
Normal file
Reference in New Issue
Block a user