from pathlib import Path from sqlalchemy import create_engine, inspect, text from sqlalchemy.orm import declarative_base, sessionmaker from app.core.config import settings from app.core.security import get_password_hash def _ensure_sqlite_parent(database_url: str) -> None: if not database_url.startswith('sqlite:///'): return relative_path = database_url.removeprefix('sqlite:///') if not relative_path or relative_path == ':memory:': return db_path = Path(relative_path) if not db_path.is_absolute(): db_path = Path.cwd() / db_path db_path.parent.mkdir(parents=True, exist_ok=True) _ensure_sqlite_parent(settings.database_url) engine = create_engine( settings.database_url, connect_args={'check_same_thread': False} if settings.database_url.startswith('sqlite') else {}, ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() def _ensure_column(table_name: str, column_name: str, ddl: str) -> None: inspector = inspect(engine) existing = {column['name'] for column in inspector.get_columns(table_name)} if column_name in existing: return with engine.begin() as connection: connection.execute(text(f'ALTER TABLE {table_name} ADD COLUMN {column_name} {ddl}')) def _run_lightweight_migrations() -> None: tables = set(inspect(engine).get_table_names()) if 'global_settings' in tables: _ensure_column('global_settings', 'connection_test_interval_minutes', 'INTEGER DEFAULT 0') _ensure_column('global_settings', 'default_switchos_username', 'VARCHAR(120)') _ensure_column('global_settings', 'default_switchos_password', 'VARCHAR(255)') if 'users' in tables: _ensure_column('users', 'preferred_language', "VARCHAR(8) DEFAULT 'pl' NOT NULL") _ensure_column('users', 'preferred_font', "VARCHAR(32) DEFAULT 'default' NOT NULL") if 'routers' in tables: _ensure_column('routers', 'device_type', "VARCHAR(32) DEFAULT 'routeros' NOT NULL") _ensure_column('routers', 'last_connection_status', 'BOOLEAN') _ensure_column('routers', 'last_connection_tested_at', 'DATETIME') _ensure_column('routers', 'last_connection_error', 'TEXT') _ensure_column('routers', 'last_connection_hostname', 'VARCHAR(255)') _ensure_column('routers', 'last_connection_model', 'VARCHAR(255)') _ensure_column('routers', 'last_connection_version', 'VARCHAR(255)') _ensure_column('routers', 'last_connection_uptime', 'VARCHAR(255)') _ensure_column('routers', 'last_connection_transport', 'VARCHAR(32)') _ensure_column('routers', 'last_connection_server', 'VARCHAR(255)') _ensure_column('routers', 'last_connection_auth_mode', 'VARCHAR(64)') _ensure_column('routers', 'last_connection_http_status', 'VARCHAR(32)') _ensure_column('routers', 'last_connection_backup_available', 'BOOLEAN') def init_db(): import app.db.base # noqa: F401 from app.models.settings import GlobalSettings from app.models.user import User Base.metadata.create_all(bind=engine) _run_lightweight_migrations() with SessionLocal() as db: if not db.query(User).first(): db.add( User( username=settings.default_admin_username, password_hash=get_password_hash(settings.default_admin_password), ) ) db.commit() if not db.query(GlobalSettings).first(): db.add(GlobalSettings()) db.commit()