|
|
ο»Ώ""" |
|
|
π DATABASE CONNECTION MODULE |
|
|
Provides database session management for SQLite/PostgreSQL with mock fallback. |
|
|
""" |
|
|
|
|
|
import os |
|
|
from pathlib import Path |
|
|
from sqlalchemy import create_engine |
|
|
from sqlalchemy.orm import sessionmaker, scoped_session |
|
|
from sqlalchemy.exc import OperationalError |
|
|
import sys |
|
|
|
|
|
|
|
|
project_root = Path(__file__).parent.parent |
|
|
sys.path.insert(0, str(project_root)) |
|
|
|
|
|
from database.config import DATABASE_CONFIG |
|
|
|
|
|
class MockSession: |
|
|
""" |
|
|
π§ͺ MOCK DATABASE SESSION |
|
|
Provides mock database functionality when real database isn't available. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
self._data = { |
|
|
'deployments': [], |
|
|
'models': [], |
|
|
'security_memory': [], |
|
|
'autonomous_decisions': [], |
|
|
'policy_versions': [], |
|
|
'operator_interactions': [], |
|
|
'system_health': [] |
|
|
} |
|
|
self.committed = False |
|
|
|
|
|
def query(self, model_class): |
|
|
"""Mock query method""" |
|
|
class MockQuery: |
|
|
def __init__(self, data): |
|
|
self.data = data |
|
|
|
|
|
def all(self): |
|
|
return [] |
|
|
|
|
|
def filter(self, *args, **kwargs): |
|
|
return self |
|
|
|
|
|
def order_by(self, *args): |
|
|
return self |
|
|
|
|
|
def limit(self, limit): |
|
|
return self |
|
|
|
|
|
def first(self): |
|
|
return None |
|
|
|
|
|
def count(self): |
|
|
return 0 |
|
|
|
|
|
def delete(self): |
|
|
return self |
|
|
|
|
|
return MockQuery([]) |
|
|
|
|
|
def add(self, item): |
|
|
"""Mock add method""" |
|
|
pass |
|
|
|
|
|
def commit(self): |
|
|
"""Mock commit method""" |
|
|
self.committed = True |
|
|
|
|
|
def close(self): |
|
|
"""Mock close method""" |
|
|
pass |
|
|
|
|
|
def rollback(self): |
|
|
"""Mock rollback method""" |
|
|
pass |
|
|
|
|
|
def create_sqlite_engine(): |
|
|
"""Create SQLite engine for development""" |
|
|
try: |
|
|
db_path = Path(__file__).parent.parent / "security_nervous_system.db" |
|
|
db_path.parent.mkdir(exist_ok=True) |
|
|
|
|
|
sqlite_url = f"sqlite:///{db_path}" |
|
|
engine = create_engine( |
|
|
sqlite_url, |
|
|
echo=False, |
|
|
connect_args={"check_same_thread": False} |
|
|
) |
|
|
|
|
|
print(f"β
SQLite engine created at {db_path}") |
|
|
return engine |
|
|
|
|
|
except Exception as e: |
|
|
print(f"β Failed to create SQLite engine: {e}") |
|
|
return None |
|
|
|
|
|
def create_postgresql_engine(): |
|
|
"""Create PostgreSQL engine for production""" |
|
|
try: |
|
|
|
|
|
if not hasattr(DATABASE_CONFIG, 'host'): |
|
|
print("β οΈ PostgreSQL not configured, using SQLite") |
|
|
return create_sqlite_engine() |
|
|
|
|
|
|
|
|
db_url = ( |
|
|
f"postgresql://{DATABASE_CONFIG.user}:{DATABASE_CONFIG.password}" |
|
|
f"@{DATABASE_CONFIG.host}:{DATABASE_CONFIG.port}/{DATABASE_CONFIG.database}" |
|
|
) |
|
|
|
|
|
engine = create_engine( |
|
|
db_url, |
|
|
pool_size=DATABASE_CONFIG.pool_size, |
|
|
max_overflow=DATABASE_CONFIG.max_overflow, |
|
|
pool_recycle=3600, |
|
|
echo=DATABASE_CONFIG.get('echo', False) |
|
|
) |
|
|
|
|
|
print(f"β
PostgreSQL engine created for {DATABASE_CONFIG.database}") |
|
|
return engine |
|
|
|
|
|
except Exception as e: |
|
|
print(f"β PostgreSQL connection failed: {e}") |
|
|
print("π‘ Falling back to SQLite") |
|
|
return create_sqlite_engine() |
|
|
|
|
|
def get_engine(): |
|
|
"""Get database engine (PostgreSQL -> SQLite -> Mock)""" |
|
|
|
|
|
engine = create_postgresql_engine() |
|
|
|
|
|
|
|
|
if engine is None: |
|
|
engine = create_sqlite_engine() |
|
|
|
|
|
|
|
|
if engine is None: |
|
|
print("β οΈ All database engines failed, using mock mode") |
|
|
return None |
|
|
|
|
|
return engine |
|
|
|
|
|
def get_session(): |
|
|
""" |
|
|
Get database session with automatic fallback. |
|
|
|
|
|
Returns: |
|
|
SQLAlchemy session or MockSession |
|
|
""" |
|
|
try: |
|
|
engine = get_engine() |
|
|
|
|
|
if engine is None: |
|
|
print("π Using MOCK database session (development)") |
|
|
return MockSession() |
|
|
|
|
|
|
|
|
Session = scoped_session(sessionmaker(bind=engine)) |
|
|
session = Session() |
|
|
|
|
|
|
|
|
session.execute("SELECT 1") |
|
|
|
|
|
print("β
Real database session created") |
|
|
return session |
|
|
|
|
|
except OperationalError as e: |
|
|
print(f"β οΈ Database connection failed: {e}") |
|
|
print("π Using MOCK database session (fallback)") |
|
|
return MockSession() |
|
|
|
|
|
except Exception as e: |
|
|
print(f"β Unexpected database error: {e}") |
|
|
print("π Using MOCK database session (error fallback)") |
|
|
return MockSession() |
|
|
|
|
|
def get_session_factory(): |
|
|
"""Get session factory for creating multiple sessions""" |
|
|
engine = get_engine() |
|
|
|
|
|
if engine is None: |
|
|
|
|
|
def mock_session_factory(): |
|
|
return MockSession() |
|
|
return mock_session_factory |
|
|
|
|
|
Session = sessionmaker(bind=engine) |
|
|
return Session |
|
|
|
|
|
|
|
|
_session = None |
|
|
|
|
|
def get_global_session(): |
|
|
"""Get or create global session (thread-local)""" |
|
|
global _session |
|
|
|
|
|
if _session is None: |
|
|
_session = get_session() |
|
|
|
|
|
return _session |
|
|
|
|
|
def close_global_session(): |
|
|
"""Close global session""" |
|
|
global _session |
|
|
|
|
|
if _session is not None: |
|
|
_session.close() |
|
|
_session = None |
|
|
print("β
Global database session closed") |
|
|
|
|
|
|