133 lines
5.8 KiB
Python
133 lines
5.8 KiB
Python
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()) |