diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0962bf8..f350487 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,7 +6,10 @@ "Bash(git -C /mnt/a/GiTea/AccountForger log --oneline)", "Bash(cat:*)", "Bash(sqlite3:*)", - "Bash(find:*)" + "Bash(find:*)", + "Bash(pkill:*)", + "Bash(lsof:*)", + "Bash(ls:*)" ], "deny": [], "ask": [], diff --git a/browser/playwright_manager.py b/browser/playwright_manager.py index ba34ce9..f7eadbb 100644 --- a/browser/playwright_manager.py +++ b/browser/playwright_manager.py @@ -71,7 +71,10 @@ class PlaywrightManager: self.browser = None self.context = None self.page = None - + + # Feature 5: Flag für Disconnect-Tracking (verhindert doppelte Counter-Dekrementierung) + self._browser_was_disconnected = False + # Zähler für Wiederhholungsversuche self.retry_counter = {} @@ -141,12 +144,29 @@ class PlaywrightManager: if self.page is not None: return self.page + # Feature 5: Reset Disconnect-Flag bei neuem Browser-Start + self._browser_was_disconnected = False + # Feature 5: Browser-Instanz Schutz - Nur eine Instanz gleichzeitig if PlaywrightManager._active_count >= 1: - raise RuntimeError( - "Browser bereits aktiv. Nur eine Browser-Instanz gleichzeitig erlaubt. " - "Beenden Sie den aktuellen Prozess." - ) + # Safety-Check: Prüfe ob Counter "hängt" (Absturz-Schutz) + # Wenn ProcessGuard NICHT locked ist, aber Counter > 0, dann ist Counter "tot" + from utils.process_guard import get_guard + guard = get_guard() + + if not guard.is_locked(): + # Counter hängt! Process Guard ist frei, aber Counter sagt Browser läuft + logger.warning( + f"⚠️ SAFETY-RESET: _active_count war {PlaywrightManager._active_count}, " + f"aber ProcessGuard ist nicht locked. Counter wird zurückgesetzt." + ) + PlaywrightManager._active_count = 0 + else: + # Guard ist locked UND Counter ist > 0 → echte parallele Instanz + raise RuntimeError( + "Browser bereits aktiv. Nur eine Browser-Instanz gleichzeitig erlaubt. " + "Beenden Sie den aktuellen Prozess." + ) try: self.playwright = sync_playwright().start() @@ -257,6 +277,10 @@ class PlaywrightManager: # Event-Listener für Konsolen-Logs self.page.on("console", lambda msg: logger.debug(f"BROWSER CONSOLE: {msg.text}")) + # Feature 5: Browser-Disconnect-Handler registrieren (für manuelles Schließen) + self.browser.on("disconnected", self._on_browser_disconnected) + logger.debug("Browser-Disconnect-Handler registriert") + # Feature 5: Browser-Instanz Counter erhöhen PlaywrightManager._active_count += 1 logger.info(f"Browser gestartet (aktive Instanzen: {PlaywrightManager._active_count})") @@ -966,7 +990,33 @@ class PlaywrightManager: self.protection_applied = False self.protection_style = None logger.info("Browser-Schutz entfernt") - + + def _on_browser_disconnected(self): + """ + Event-Handler: Wird aufgerufen wenn Browser-Verbindung getrennt wird. + + Dekrementiert den aktiven Browser-Counter, auch wenn der Browser + manuell geschlossen wurde (z.B. durch User-Klick auf X-Button). + + Dies stellt sicher dass der Missbrauchs-Schutz korrekt funktioniert, + selbst wenn close() nicht explizit aufgerufen wird. + """ + # Verhindere doppelte Dekrementierung + if self._browser_was_disconnected: + logger.debug("Browser bereits als disconnected markiert, überspringe Counter-Dekrementierung") + return + + self._browser_was_disconnected = True + + if PlaywrightManager._active_count > 0: + PlaywrightManager._active_count -= 1 + logger.info( + f"🔌 Browser disconnected - Counter dekrementiert " + f"(aktive Instanzen: {PlaywrightManager._active_count})" + ) + else: + logger.warning("Browser disconnected aber Counter war bereits 0") + def close(self): """Schließt den Browser und gibt Ressourcen frei.""" try: @@ -1012,9 +1062,15 @@ class PlaywrightManager: self.playwright = None # Feature 5: Browser-Instanz Counter dekrementieren - if PlaywrightManager._active_count > 0: - PlaywrightManager._active_count -= 1 - logger.info(f"Browser geschlossen (aktive Instanzen: {PlaywrightManager._active_count})") + # (nur wenn nicht bereits durch disconnected-Event dekrementiert) + if not self._browser_was_disconnected: + if PlaywrightManager._active_count > 0: + PlaywrightManager._active_count -= 1 + logger.info(f"Browser geschlossen (aktive Instanzen: {PlaywrightManager._active_count})") + else: + logger.warning("Browser-Counter war bereits 0 beim Schließen") + else: + logger.debug("Counter wurde bereits durch disconnected-Event dekrementiert") logger.info("Browser-Sitzung erfolgreich geschlossen") diff --git a/controllers/platform_controllers/facebook_controller.py b/controllers/platform_controllers/facebook_controller.py index 889ccb6..f1e6d25 100644 --- a/controllers/platform_controllers/facebook_controller.py +++ b/controllers/platform_controllers/facebook_controller.py @@ -105,80 +105,100 @@ class FacebookController(BasePlatformController): # Validiere Eingaben is_valid, error_msg = self.validate_inputs(params) if not is_valid: + # Guard freigeben da Worker nicht gestartet wird + from utils.process_guard import get_guard + get_guard().end(success=False) self.get_generator_tab().show_error(error_msg) return - # UI aktualisieren - generator_tab = self.get_generator_tab() - generator_tab.set_running(True) - if hasattr(generator_tab, 'clear_log'): - generator_tab.clear_log() - if hasattr(generator_tab, 'set_progress'): - generator_tab.set_progress(0) - - # Schmiedeanimation-Dialog erstellen und anzeigen - parent_widget = generator_tab.window() # Hauptfenster als Parent - self.forge_dialog = ForgeAnimationDialog(parent_widget, "Facebook") - self.forge_dialog.cancel_clicked.connect(self.stop_account_creation) - self.forge_dialog.closed.connect(self.stop_account_creation) - - # Fensterposition vom Hauptfenster holen - if parent_widget: - window_pos = parent_widget.pos() - params["window_position"] = (window_pos.x(), window_pos.y()) - - # Fingerprint VOR Account-Erstellung generieren try: - from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService - from domain.entities.browser_fingerprint import BrowserFingerprint - import uuid + # UI aktualisieren + generator_tab = self.get_generator_tab() + generator_tab.set_running(True) + if hasattr(generator_tab, 'clear_log'): + generator_tab.clear_log() + if hasattr(generator_tab, 'set_progress'): + generator_tab.set_progress(0) - fingerprint_service = FingerprintGeneratorService() + # Schmiedeanimation-Dialog erstellen und anzeigen + parent_widget = generator_tab.window() # Hauptfenster als Parent + self.forge_dialog = ForgeAnimationDialog(parent_widget, "Facebook") + self.forge_dialog.cancel_clicked.connect(self.stop_account_creation) + self.forge_dialog.closed.connect(self.stop_account_creation) - # Generiere einen neuen Fingerprint für diesen Account - fingerprint_data = fingerprint_service.generate_fingerprint() + # Fensterposition vom Hauptfenster holen + if parent_widget: + window_pos = parent_widget.pos() + params["window_position"] = (window_pos.x(), window_pos.y()) - # Erstelle BrowserFingerprint Entity mit allen notwendigen Daten - fingerprint = BrowserFingerprint.from_dict(fingerprint_data) - fingerprint.fingerprint_id = str(uuid.uuid4()) - fingerprint.account_bound = True - fingerprint.rotation_seed = str(uuid.uuid4()) + # Fingerprint VOR Account-Erstellung generieren + try: + from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService + from domain.entities.browser_fingerprint import BrowserFingerprint + import uuid - # Konvertiere zu Dictionary für Übertragung - params["fingerprint"] = fingerprint.to_dict() + fingerprint_service = FingerprintGeneratorService() + + # Generiere einen neuen Fingerprint für diesen Account + fingerprint_data = fingerprint_service.generate_fingerprint() + + # Erstelle BrowserFingerprint Entity mit allen notwendigen Daten + fingerprint = BrowserFingerprint.from_dict(fingerprint_data) + fingerprint.fingerprint_id = str(uuid.uuid4()) + fingerprint.account_bound = True + fingerprint.rotation_seed = str(uuid.uuid4()) + + # Konvertiere zu Dictionary für Übertragung + params["fingerprint"] = fingerprint.to_dict() + + logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}") + except Exception as e: + logger.error(f"Fehler beim Generieren des Fingerprints: {e}") + # Fortfahren ohne Fingerprint - wird später generiert + + # Worker-Thread starten mit optionalen Parametern + session_controller = getattr(self, 'session_controller', None) + generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None + + self.worker_thread = FacebookWorkerThread( + params, + session_controller=session_controller, + generator_tab=generator_tab_ref + ) + + # Updates an Forge-Dialog weiterleiten + self.worker_thread.update_signal.connect(self.forge_dialog.set_status) + self.worker_thread.log_signal.connect(self.forge_dialog.add_log) + self.worker_thread.error_signal.connect(self._handle_error) + self.worker_thread.finished_signal.connect(self._handle_finished) + self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress) + + # Auch an Generator-Tab für Backup + self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg)) + if hasattr(generator_tab, 'set_progress'): + self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value)) + + self.worker_thread.start() + + # Dialog anzeigen und Animation starten + self.forge_dialog.start_animation() + self.forge_dialog.show() - logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}") except Exception as e: - logger.error(f"Fehler beim Generieren des Fingerprints: {e}") - # Fortfahren ohne Fingerprint - wird später generiert + # Kritischer Fehler VOR Worker-Start → Guard freigeben! + logger.error(f"Fehler beim Start der Account-Erstellung: {e}", exc_info=True) - # Worker-Thread starten mit optionalen Parametern - session_controller = getattr(self, 'session_controller', None) - generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None + from utils.process_guard import get_guard + get_guard().end(success=False) - self.worker_thread = FacebookWorkerThread( - params, - session_controller=session_controller, - generator_tab=generator_tab_ref - ) + # Dialog schließen falls vorhanden + if hasattr(self, 'forge_dialog') and self.forge_dialog: + self.forge_dialog.close() - # Updates an Forge-Dialog weiterleiten - self.worker_thread.update_signal.connect(self.forge_dialog.set_status) - self.worker_thread.log_signal.connect(self.forge_dialog.add_log) - self.worker_thread.error_signal.connect(self._handle_error) - self.worker_thread.finished_signal.connect(self._handle_finished) - self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress) - - # Auch an Generator-Tab für Backup - self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg)) - if hasattr(generator_tab, 'set_progress'): - self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value)) - - self.worker_thread.start() - - # Dialog anzeigen und Animation starten - self.forge_dialog.start_animation() - self.forge_dialog.show() + # UI zurücksetzen + generator_tab = self.get_generator_tab() + generator_tab.set_running(False) + generator_tab.show_error(f"Fehler beim Start: {str(e)}") def handle_account_created(self, result: Dict[str, Any]) -> bool: """ diff --git a/controllers/platform_controllers/instagram_controller.py b/controllers/platform_controllers/instagram_controller.py index 30b76bc..a2d06af 100644 --- a/controllers/platform_controllers/instagram_controller.py +++ b/controllers/platform_controllers/instagram_controller.py @@ -197,76 +197,96 @@ class InstagramController(BasePlatformController): # Validiere Eingaben is_valid, error_msg = self.validate_inputs(params) if not is_valid: + # Guard freigeben da Worker nicht gestartet wird + from utils.process_guard import get_guard + get_guard().end(success=False) self.get_generator_tab().show_error(error_msg) return - # UI aktualisieren - generator_tab = self.get_generator_tab() - generator_tab.set_running(True) - generator_tab.clear_log() - generator_tab.set_progress(0) - - # Schmiedeanimation-Dialog erstellen und anzeigen - parent_widget = generator_tab.window() # Hauptfenster als Parent - self.forge_dialog = ForgeAnimationDialog(parent_widget, "Instagram") - self.forge_dialog.cancel_clicked.connect(self.stop_account_creation) - self.forge_dialog.closed.connect(self.stop_account_creation) - - # Fensterposition vom Hauptfenster holen - if parent_widget: - window_pos = parent_widget.pos() - params["window_position"] = (window_pos.x(), window_pos.y()) - - # Fingerprint VOR Account-Erstellung generieren try: - from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService - from domain.entities.browser_fingerprint import BrowserFingerprint - import uuid - - fingerprint_service = FingerprintGeneratorService() - - # Generiere einen neuen Fingerprint für diesen Account - fingerprint_data = fingerprint_service.generate_fingerprint() - - # Erstelle BrowserFingerprint Entity mit allen notwendigen Daten - fingerprint = BrowserFingerprint.from_dict(fingerprint_data) - fingerprint.fingerprint_id = str(uuid.uuid4()) - fingerprint.account_bound = True - fingerprint.rotation_seed = str(uuid.uuid4()) - - # Konvertiere zu Dictionary für Übertragung - params["fingerprint"] = fingerprint.to_dict() - - logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}") - except Exception as e: - logger.error(f"Fehler beim Generieren des Fingerprints: {e}") - # Fortfahren ohne Fingerprint - wird später generiert - - # Worker-Thread starten mit optionalen Parametern - session_controller = getattr(self, 'session_controller', None) - generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None - - self.worker_thread = InstagramWorkerThread( - params, - session_controller=session_controller, - generator_tab=generator_tab_ref - ) - # Updates an Forge-Dialog weiterleiten - self.worker_thread.update_signal.connect(self.forge_dialog.set_status) - self.worker_thread.log_signal.connect(self.forge_dialog.add_log) - self.worker_thread.error_signal.connect(self._handle_error) - self.worker_thread.finished_signal.connect(self._handle_finished) - self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress) - - # Auch an Generator-Tab für Backup - self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg)) - self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value)) + # UI aktualisieren + generator_tab = self.get_generator_tab() + generator_tab.set_running(True) + generator_tab.clear_log() + generator_tab.set_progress(0) - self.worker_thread.start() - - # Dialog anzeigen und Animation starten - self.forge_dialog.start_animation() - self.forge_dialog.show() + # Schmiedeanimation-Dialog erstellen und anzeigen + parent_widget = generator_tab.window() # Hauptfenster als Parent + self.forge_dialog = ForgeAnimationDialog(parent_widget, "Instagram") + self.forge_dialog.cancel_clicked.connect(self.stop_account_creation) + self.forge_dialog.closed.connect(self.stop_account_creation) + + # Fensterposition vom Hauptfenster holen + if parent_widget: + window_pos = parent_widget.pos() + params["window_position"] = (window_pos.x(), window_pos.y()) + + # Fingerprint VOR Account-Erstellung generieren + try: + from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService + from domain.entities.browser_fingerprint import BrowserFingerprint + import uuid + + fingerprint_service = FingerprintGeneratorService() + + # Generiere einen neuen Fingerprint für diesen Account + fingerprint_data = fingerprint_service.generate_fingerprint() + + # Erstelle BrowserFingerprint Entity mit allen notwendigen Daten + fingerprint = BrowserFingerprint.from_dict(fingerprint_data) + fingerprint.fingerprint_id = str(uuid.uuid4()) + fingerprint.account_bound = True + fingerprint.rotation_seed = str(uuid.uuid4()) + + # Konvertiere zu Dictionary für Übertragung + params["fingerprint"] = fingerprint.to_dict() + + logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}") + except Exception as e: + logger.error(f"Fehler beim Generieren des Fingerprints: {e}") + # Fortfahren ohne Fingerprint - wird später generiert + + # Worker-Thread starten mit optionalen Parametern + session_controller = getattr(self, 'session_controller', None) + generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None + + self.worker_thread = InstagramWorkerThread( + params, + session_controller=session_controller, + generator_tab=generator_tab_ref + ) + # Updates an Forge-Dialog weiterleiten + self.worker_thread.update_signal.connect(self.forge_dialog.set_status) + self.worker_thread.log_signal.connect(self.forge_dialog.add_log) + self.worker_thread.error_signal.connect(self._handle_error) + self.worker_thread.finished_signal.connect(self._handle_finished) + self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress) + + # Auch an Generator-Tab für Backup + self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg)) + self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value)) + + self.worker_thread.start() + + # Dialog anzeigen und Animation starten + self.forge_dialog.start_animation() + self.forge_dialog.show() + + except Exception as e: + # Kritischer Fehler VOR Worker-Start → Guard freigeben! + logger.error(f"Fehler beim Start der Account-Erstellung: {e}", exc_info=True) + + from utils.process_guard import get_guard + get_guard().end(success=False) + + # Dialog schließen falls vorhanden + if hasattr(self, 'forge_dialog') and self.forge_dialog: + self.forge_dialog.close() + + # UI zurücksetzen + generator_tab = self.get_generator_tab() + generator_tab.set_running(False) + generator_tab.show_error(f"Fehler beim Start: {str(e)}") def stop_account_creation(self): """Stoppt die Instagram-Account-Erstellung mit Guard-Freigabe.""" diff --git a/controllers/profile_export_controller.py b/controllers/profile_export_controller.py index 91f7203..b1a3ad4 100644 --- a/controllers/profile_export_controller.py +++ b/controllers/profile_export_controller.py @@ -274,40 +274,7 @@ class ProfileExportController: logger.error(f"Fehler beim Export von {username}: {e}") failed_accounts.append(username) - # 5. Summary-Datei erstellen - summary_path = os.path.join(save_directory, "export_summary.txt") - with open(summary_path, 'w', encoding='utf-8') as f: - f.write(f"AccountForge Batch-Export\n") - f.write(f"="*50 + "\n\n") - f.write(f"Exportiert am: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n") - f.write(f"Anzahl Accounts: {len(accounts_data)}\n") - f.write(f"Erfolgreich: {len(accounts_data) - len(failed_accounts)}\n") - if failed_accounts: - f.write(f"Fehlgeschlagen: {len(failed_accounts)}\n") - f.write(f"\nFormate: {', '.join(formats).upper()}\n") - f.write(f"\n" + "="*50 + "\n\n") - - # Gruppiere nach Plattform - platforms = {} - for account_data in accounts_data: - platform = account_data.get("platform", "unknown").lower() - if platform not in platforms: - platforms[platform] = [] - platforms[platform].append(account_data.get("username", "")) - - for platform, usernames in sorted(platforms.items()): - f.write(f"{platform.capitalize()}:\n") - for username in usernames: - if username in failed_accounts: - f.write(f" ✗ {username} (FEHLER)\n") - else: - f.write(f" ✓ {username}\n") - f.write(f"\n") - - exported_files.append("export_summary.txt") - logger.info("Summary-Datei erstellt") - - # 6. Erfolgs-Dialog anzeigen + # 5. Erfolgs-Dialog anzeigen show_export_success( parent_widget, exported_files, diff --git a/database/accounts.db b/database/accounts.db index 078b327..2212174 100644 Binary files a/database/accounts.db and b/database/accounts.db differ diff --git a/run_migration.py b/run_migration.py deleted file mode 100644 index 06397ff..0000000 --- a/run_migration.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python3 -""" -Run method rotation system database migration. -This script applies the rotation system database schema to the existing database. -""" - -import sys -import os -import sqlite3 -import logging -from pathlib import Path - -# Add project root to path -project_root = Path(__file__).parent -sys.path.insert(0, str(project_root)) - -# Setup logging -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - - -def run_migration(): - """Run the method rotation system migration""" - try: - # Database path - db_path = project_root / "database" / "accounts.db" - migration_path = project_root / "database" / "migrations" / "add_method_rotation_system.sql" - - if not db_path.exists(): - logger.error(f"Database not found at {db_path}") - return False - - if not migration_path.exists(): - logger.error(f"Migration file not found at {migration_path}") - return False - - # Read migration SQL - with open(migration_path, 'r', encoding='utf-8') as f: - migration_sql = f.read() - - # Connect to database - logger.info(f"Connecting to database at {db_path}") - conn = sqlite3.connect(str(db_path)) - - try: - # Check if migration has already been run - cursor = conn.cursor() - cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='method_strategies'") - if cursor.fetchone(): - logger.info("Method rotation tables already exist, skipping migration") - return True - - # Run migration - logger.info("Running method rotation system migration...") - conn.executescript(migration_sql) - conn.commit() - - # Verify migration - cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%method%' OR name LIKE '%rotation%'") - tables = cursor.fetchall() - - expected_tables = [ - 'method_strategies', - 'rotation_sessions', - 'rotation_events', - 'method_performance_analytics', - 'method_cooldowns', - 'platform_method_states' - ] - - created_tables = [table[0] for table in tables] - missing_tables = [t for t in expected_tables if t not in created_tables] - - if missing_tables: - logger.error(f"Migration incomplete, missing tables: {missing_tables}") - return False - - logger.info(f"Migration successful! Created tables: {created_tables}") - - # Verify default data was inserted - cursor.execute("SELECT COUNT(*) FROM method_strategies") - strategy_count = cursor.fetchone()[0] - - cursor.execute("SELECT COUNT(*) FROM platform_method_states") - state_count = cursor.fetchone()[0] - - logger.info(f"Default data inserted: {strategy_count} strategies, {state_count} platform states") - - return True - - finally: - conn.close() - - except Exception as e: - logger.error(f"Migration failed: {e}") - return False - - -def check_migration_status(): - """Check if migration has been applied""" - try: - db_path = project_root / "database" / "accounts.db" - - if not db_path.exists(): - logger.warning(f"Database not found at {db_path}") - return False - - conn = sqlite3.connect(str(db_path)) - cursor = conn.cursor() - - # Check for rotation tables - cursor.execute(""" - SELECT name FROM sqlite_master - WHERE type='table' AND ( - name LIKE '%method%' OR - name LIKE '%rotation%' - ) - ORDER BY name - """) - - tables = [row[0] for row in cursor.fetchall()] - conn.close() - - if tables: - logger.info(f"Method rotation tables found: {tables}") - return True - else: - logger.info("No method rotation tables found") - return False - - except Exception as e: - logger.error(f"Failed to check migration status: {e}") - return False - - -def main(): - """Main function""" - logger.info("Method Rotation System Database Migration") - logger.info("=" * 50) - - # Check current status - logger.info("Checking current migration status...") - if check_migration_status(): - logger.info("Migration already applied") - return - - # Run migration - logger.info("Applying migration...") - if run_migration(): - logger.info("Migration completed successfully!") - logger.info("Method rotation system is now ready to use") - else: - logger.error("Migration failed!") - sys.exit(1) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_logo_display.py b/test_logo_display.py deleted file mode 100644 index 14f89b0..0000000 --- a/test_logo_display.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -""" -Debug script to check what's happening with logo switching -""" - -import os -import sys - -# Add project root to path -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from themes.theme_config import ThemeConfig - -def check_logo_files(): - """Check the actual logo files and their paths.""" - print("=" * 60) - print("LOGO FILE ANALYSIS") - print("=" * 60) - - base_dir = os.path.dirname(os.path.abspath(__file__)) - - # Check what's in the config - light_theme = ThemeConfig.get_theme('light') - dark_theme = ThemeConfig.get_theme('dark') - - print(f"\nTheme Config:") - print(f" Light theme logo_path: {light_theme.get('logo_path', 'NOT SET')}") - print(f" Dark theme logo_path: {dark_theme.get('logo_path', 'NOT SET')}") - - # Check actual files - icons_dir = os.path.join(base_dir, "resources", "icons") - print(f"\nIcon files in {icons_dir}:") - - for file in os.listdir(icons_dir): - if "intelsight" in file.lower(): - file_path = os.path.join(icons_dir, file) - file_size = os.path.getsize(file_path) - print(f" - {file} ({file_size} bytes)") - - # Test the path resolution - print("\n" + "=" * 60) - print("PATH RESOLUTION TEST") - print("=" * 60) - - # Simulate what happens in get_icon_path - for theme_name in ['light', 'dark']: - theme = ThemeConfig.get_theme(theme_name) - logo_name = theme.get('logo_path', 'intelsight-logo.svg').replace('.svg', '') - full_path = os.path.join(base_dir, "resources", "icons", f"{logo_name}.svg") - - print(f"\n{theme_name.upper()} theme:") - print(f" logo_name from config: {logo_name}") - print(f" full_path: {full_path}") - print(f" file exists: {os.path.exists(full_path)}") - - if os.path.exists(full_path): - # Check if it's actually an SVG - with open(full_path, 'r') as f: - first_line = f.readline().strip() - is_svg = '