#!/usr/bin/env python3 """ Startup script for Brief Extractor GUI server """ import sys import os import logging from pathlib import Path # Add server and core paths to Python path project_root = Path(__file__).parent sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root / 'server')) # Set up logging before importing modules logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) async def async_main(): """Async main function with proper signal handling""" import asyncio import signal # Import after path setup from server.app import create_app from server.config_runtime import server_config # Validate configuration if not server_config.validate_auth_config(): if not server_config.DEV_MODE: logger.error("MSAL authentication configuration is incomplete") logger.error("Please set MSAL_CLIENT_ID, MSAL_CLIENT_SECRET, and MSAL_TENANT_ID in .env") sys.exit(1) else: logger.warning("Running in DEV_MODE - MSAL authentication bypassed") # Create application logger.info("Creating Brief Extractor GUI application...") app = create_app() # Import and configure Hypercorn import hypercorn.asyncio from hypercorn import Config config = Config() config.bind = [f"{server_config.HOST}:{server_config.PORT}"] config.workers = server_config.WORKERS config.use_reloader = server_config.DEBUG config.accesslog = "-" # Log to stdout config.errorlog = "-" # Log to stderr # Log startup information logger.info(f"Starting Brief Extractor GUI server") logger.info(f"Server: http://{server_config.HOST}:{server_config.PORT}") logger.info(f"Development mode: {server_config.DEV_MODE}") logger.info(f"Max concurrent jobs: {server_config.MAX_CONCURRENT_JOBS}") logger.info(f"Max upload size: {server_config.MAX_UPLOAD_SIZE_MB}MB") logger.info(f"File retention: {server_config.FILE_RETENTION_HOURS} hours") logger.info(f"Workers: {server_config.WORKERS}") # Set up proper signal handling for graceful shutdown shutdown_event = asyncio.Event() def signal_handler(): logger.info("Shutdown signal received, stopping server...") shutdown_event.set() # Force shutdown after 3 seconds if graceful shutdown fails def force_shutdown(): import time time.sleep(3) logger.warning("Graceful shutdown timed out, forcing exit...") os._exit(1) import threading threading.Thread(target=force_shutdown, daemon=True).start() # Register signal handlers if sys.platform != 'win32': loop = asyncio.get_running_loop() loop.add_signal_handler(signal.SIGINT, signal_handler) loop.add_signal_handler(signal.SIGTERM, signal_handler) try: # Start server with shutdown trigger await hypercorn.asyncio.serve(app, config, shutdown_trigger=shutdown_event.wait) logger.info("Server stopped gracefully") except asyncio.CancelledError: logger.info("Server cancelled") except Exception as e: logger.error(f"Server error: {e}", exc_info=True) raise def main(): """Main entry point""" import asyncio import signal # Set up immediate signal handling before async loop def immediate_shutdown(signum, frame): logger.info(f"Immediate shutdown signal {signum} received") os._exit(0) signal.signal(signal.SIGINT, immediate_shutdown) signal.signal(signal.SIGTERM, immediate_shutdown) try: asyncio.run(async_main()) except KeyboardInterrupt: logger.info("Server stopped by user") os._exit(0) except Exception as e: logger.error(f"Server failed to start: {e}", exc_info=True) os._exit(1) if __name__ == '__main__': main()