From 9f48c401e9dfa585c3cf17af1321952b59adbb39 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Wed, 17 Dec 2025 12:05:51 +0100 Subject: [PATCH] CRITICAL FIX: Robust DB + drops attribute + no duplicate logging (complete rewrite) --- backend/scripts/sync_all_zones.py | 42 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/backend/scripts/sync_all_zones.py b/backend/scripts/sync_all_zones.py index f2b4d5c..0340c24 100644 --- a/backend/scripts/sync_all_zones.py +++ b/backend/scripts/sync_all_zones.py @@ -65,20 +65,18 @@ SWITCH_CONFIG = { # Setup logging (avoid duplicate handlers) logger = logging.getLogger("pounce_zone_sync") logger.setLevel(logging.INFO) -if not logger.handlers: # Only add handlers once +if not logger.handlers: formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s') - # Console handler console = logging.StreamHandler() console.setFormatter(formatter) logger.addHandler(console) - # File handler try: LOG_FILE.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(LOG_FILE) file_handler.setFormatter(formatter) logger.addHandler(file_handler) except Exception: - pass # File logging optional + pass class ZoneSyncResult: @@ -88,15 +86,39 @@ class ZoneSyncResult: self.success = False self.domain_count = 0 self.drops_count = 0 + self.drops: list = [] # CRITICAL: List of (domain, tld) tuples for DB storage self.error: Optional[str] = None self.duration_seconds = 0 async def get_db_session(): - """Create async database session""" - from app.config import settings + """Create async database session - ROBUST VERSION (reads .env directly)""" + # Read DATABASE_URL directly from .env to avoid import issues when running standalone + env_file = Path("/home/user/pounce/backend/.env") + if not env_file.exists(): + env_file = Path(__file__).parent.parent / ".env" - engine = create_async_engine(settings.database_url.replace("sqlite://", "sqlite+aiosqlite://")) + db_url = None + if env_file.exists(): + for line in env_file.read_text().splitlines(): + if line.startswith("DATABASE_URL="): + db_url = line.split("=", 1)[1].strip().strip('"').strip("'") + break + + # Default to SQLite if not found + if not db_url: + db_url = "sqlite:///./domainwatch.db" + logger.warning(f"DATABASE_URL not found in .env, using default: {db_url}") + + # Convert to async driver + if "sqlite://" in db_url and "aiosqlite" not in db_url: + db_url = db_url.replace("sqlite://", "sqlite+aiosqlite://") + elif "postgresql://" in db_url and "asyncpg" not in db_url: + db_url = db_url.replace("postgresql://", "postgresql+asyncpg://") + + logger.info(f"DB connection: {db_url[:50]}...") + + engine = create_async_engine(db_url, echo=False) async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) return async_session() @@ -556,7 +578,7 @@ async def main(): results.append(result) # Store drops IMMEDIATELY after each TLD (crash-safe) - if hasattr(result, 'drops') and result.drops: + if result.drops: await store_tld_drops(result.drops, tld) # Rate limit: wait between downloads @@ -571,7 +593,7 @@ async def main(): results.append(result) # Store drops IMMEDIATELY - if hasattr(result, 'drops') and result.drops: + if result.drops: await store_tld_drops(result.drops, tld) # Cleanup stray files @@ -621,4 +643,4 @@ async def main(): if __name__ == "__main__": exit_code = asyncio.run(main()) - sys.exit(exit_code) \ No newline at end of file + sys.exit(exit_code)