import asyncio import os import sys import importlib.util from pathlib import Path from sqlalchemy import inspect, text from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.dialects.postgresql import JSONB, ENUM, NUMERIC # Elérési utak beállítása BASE_DIR = Path(__file__).resolve().parents[2] sys.path.append(str(BASE_DIR)) try: from app.database import Base, engine from app.core.config import settings except ImportError as e: print(f"❌ Hiba az alapvető importoknál: {e}") sys.exit(1) def dynamic_import_models(models_dir: Path): """ Automatikusan bejárja az app/models mappát és beimportál minden .py fájlt, hogy a Base.metadata.tables feltöltődjön. """ print(f"🔍 Modellek dinamikus felderítése itt: {models_dir}...") count = 0 for root, _, files in os.walk(models_dir): for file in files: if file.endswith(".py") and file != "__init__.py": full_path = Path(root) / file # Modul név képzése (pl. app.models.identity.user) rel_path = full_path.relative_to(BASE_DIR) module_name = str(rel_path).replace(os.sep, ".").replace(".py", "") try: spec = importlib.util.spec_from_file_location(module_name, full_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) count += 1 except Exception as e: print(f" ⚠️ Nem sikerült importálni: {module_name} -> {e}") print(f"✅ {count} modell fájl sikeresen betöltve a memóriába.\n") async def run_unified_audit(): # 1. Modellek betöltése models_path = BASE_DIR / "app" / "models" dynamic_import_models(models_path) print(f"🔗 Kapcsolódás az adatbázishoz: {settings.POSTGRES_DB}") async with engine.connect() as conn: inspector = await conn.run_sync(inspect) all_db_schemas = await conn.run_sync(lambda c: inspector.get_schema_names()) # Kigyűjtjük a modellekben definiált sémákat expected_schemas = sorted({t.schema for t in Base.metadata.sorted_tables if t.schema}) mismatches = 0 suggestions = [] for sc in expected_schemas: print(f"\n--- 🛰️ DOMAIN AUDIT: '{sc}' ---") if sc not in all_db_schemas: print(f"❌ KRITIKUS: A(z) '{sc}' séma hiányzik!") mismatches += 1 continue db_tables = await conn.run_sync(lambda c: inspector.get_table_names(schema=sc)) model_tables = [t for t in Base.metadata.sorted_tables if t.schema == sc] for table in model_tables: t_name = table.name if t_name not in db_tables: print(f"❌ HIÁNYZÓ TÁBLA: {sc}.{t_name}") mismatches += 1 suggestions.append(f"-- Hozd létre a táblát: {sc}.{t_name}") continue # Oszlopok lekérése a DB-ből db_cols = {c['name']: c for c in await conn.run_sync( lambda c: inspector.get_columns(t_name, schema=sc) )} # Oszlopok lekérése a Modellből for col in table.columns: if col.name not in db_cols: print(f"⚠️ HIÁNYZÓ OSZLOP: {sc}.{t_name}.{col.name}") mismatches += 1 suggestions.append(f"ALTER TABLE {sc}.{t_name} ADD COLUMN {col.name} {col.type};") else: # MÉLY TÍPUS ELLENŐRZÉS db_col = db_cols[col.name] db_type_str = str(db_col['type']).upper() # 1. JSONB Ellenőrzés if isinstance(col.type, JSONB) and "JSONB" not in db_type_str: print(f"🔬 TÍPUS ELTÉRÉS [JSONB]: {sc}.{t_name}.{col.name} (DB: {db_type_str})") mismatches += 1 # 2. NUMERIC Precizitás elif isinstance(col.type, NUMERIC): m_prec, m_scale = col.type.precision, col.type.scale d_prec, d_scale = db_col['type'].precision, db_col['type'].scale if m_prec != d_prec or m_scale != d_scale: print(f"🔬 TÍPUS ELTÉRÉS [NUMERIC]: {sc}.{t_name}.{col.name} (Kód: {m_prec},{m_scale} vs DB: {d_prec},{d_scale})") mismatches += 1 # 3. ENUM Ellenőrzés elif isinstance(col.type, ENUM): enum_name = col.type.name res = await conn.execute(text( "SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :name)"), {"name": enum_name} ) if not res.scalar(): print(f"🔬 HIÁNYZÓ ENUM TÍPUS: {enum_name} ({sc}.{t_name}.{col.name})") mismatches += 1 print(f"✅ {sc}.{t_name:30} | Átvizsgálva.") print("\n" + "="*50) if mismatches == 0: print("✨ GRATULÁLOK! A fájlrendszer és az adatbázis szinkronban van. ✨") else: print(f"⚠️ ÖSSZESEN {mismatches} ELTÉRÉS TALÁLHATÓ!") print("\nJAVÍTÁSI JAVASLATOK (Copy-Paste SQL):") for s in suggestions: print(f" {s}") print("="*50 + "\n") if __name__ == "__main__": asyncio.run(run_unified_audit())