import webview import threading import time import subprocess import os import sys import signal import requests import atexit from eveai_client.platform.app import create_app class EveAIClient: """Main client class that orchestrates all platform components.""" def __init__(self): """Initialize the EveAI client.""" self.port = 7001 # Default port self.host = "127.0.0.1" # Only listen on localhost for security self.app = create_app() self.window = None self.server_process = None # Register cleanup at exit to ensure server is terminated atexit.register(self.cleanup) def _run_server(self): """Run Gunicorn server in a separate process.""" # Set environment variables if needed env = os.environ.copy() # Construct the command cmd = [ "gunicorn", "--workers", "1", "--threads", "4", "--bind", f"{self.host}:{self.port}", "--timeout", "120", "wsgi:app" ] # Start the server as a subprocess self.server_process = subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True ) # Log server output for line in iter(self.server_process.stdout.readline, ""): print(f"[Server] {line.strip()}") def _wait_for_server(self, timeout=30): """Wait for server to be ready by polling the endpoint.""" url = f"http://{self.host}:{self.port}/" start_time = time.time() while time.time() - start_time < timeout: try: response = requests.get(url, timeout=1) if response.status_code == 200: print(f"Server at {url} is ready!") return True except requests.RequestException: print("Waiting for server to start...") time.sleep(1) print(f"Server did not start within {timeout} seconds") return False def _on_window_closing(self): """Handle window closing event.""" print("Window closing, shutting down server...") self.cleanup() return True # Allow window to close def start(self): """Start the EveAI client application.""" # Start Gunicorn server in a separate thread server_thread = threading.Thread(target=self._run_server) server_thread.daemon = True server_thread.start() # Wait for the server to be ready to accept connections if not self._wait_for_server(): print("Failed to start server, exiting...") self.cleanup() return print(f"Starting webview with URL: http://{self.host}:{self.port}") # Create and show the main window self.window = webview.create_window( self.app.config['WINDOW_TITLE'], f'http://{self.host}:{self.port}', width=self.app.config['WINDOW_WIDTH'], height=self.app.config['WINDOW_HEIGHT'], min_size=(self.app.config['WINDOW_MIN_WIDTH'], self.app.config['WINDOW_MIN_HEIGHT']), ) # Set window close handler self.window.events.closing += self._on_window_closing print("Webview window created, starting webview...") webview.start(debug=True) print("Webview closed.") def cleanup(self): """Clean up resources before shutdown.""" # Terminate the server process if self.server_process: print("Shutting down server...") try: # Only terminate the specific server process, not its parent self.server_process.terminate() # Wait for termination with timeout try: self.server_process.wait(timeout=5) print("Server process terminated gracefully.") except subprocess.TimeoutExpired: print("Server didn't terminate gracefully, forcing kill...") self.server_process.kill() try: self.server_process.wait(timeout=2) print("Server process killed.") except subprocess.TimeoutExpired: print("Failed to kill server process.") except Exception as e: print(f"Error during server cleanup: {e}") self.server_process = None # Additional cleanup for any resources, cache, etc. print("Cleanup complete.") def main(): """Application entry point.""" client = EveAIClient() try: client.start() finally: # This ensures cleanup runs even if start() raises an exception client.cleanup() if __name__ == '__main__': main()