#!/usr/bin/env python3 """ Comprehensive Production Deployment with OAuth Completion This script handles the complete production deployment of the Atom AI Assistant including OAuth service completion, SSL/TLS configuration, monitoring setup, and production validation. Usage: python deploy_production_with_oauth.py """ from datetime import datetime import json import os import secrets import subprocess import sys import time from typing import Any, Dict, List, Tuple import requests class ProductionDeploymentWithOAuth: """Complete production deployment with OAuth service completion""" def __init__(self): self.base_url = "http://localhost:5058" self.deployment_log = [] self.start_time = datetime.now() self.remaining_services = ["outlook", "teams", "github"] def log_step(self, step_name: str, status: str, message: str = ""): """Log deployment step with timestamp""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = { "timestamp": timestamp, "step": step_name, "status": status, "message": message } self.deployment_log.append(log_entry) status_icon = "✅" if status == "success" else "❌" if status == "failed" else "⚠️" print(f"{status_icon} [{timestamp}] {step_name}: {message}") def validate_current_oauth_status(self) -> Dict[str, Any]: """Validate current OAuth system status""" self.log_step("oauth_status_validation", "running", "Validating current OAuth system status") # First, check environment variables missing_credentials = self._check_oauth_credentials() if missing_credentials: self.log_step( "oauth_status_validation", "warning", f"Missing OAuth credentials: {', '.join(missing_credentials)}" ) return { "success": False, "error": "Missing OAuth credentials", "missing_credentials": missing_credentials } try: response = requests.get( f"{self.base_url}/api/auth/oauth-status?user_id=production_deploy", timeout=10 ) if response.status_code == 200: data = response.json() connected_services = data.get("connected_services", 0) total_services = data.get("total_services", 0) self.log_step( "oauth_status_validation", "success", f"Current OAuth status: {connected_services}/{total_services} services connected" ) return { "success": True, "connected_services": connected_services, "total_services": total_services, "success_rate": connected_services / total_services if total_services > 0 else 0, "data": data } else: self.log_step( "oauth_status_validation", "failed", f"OAuth status endpoint returned HTTP {response.status_code}" ) return {"success": False, "error": f"HTTP {response.status_code}"} except Exception as e: self.log_step( "oauth_status_validation", "failed", f"OAuth status validation failed: {str(e)}" ) return {"success": False, "error": str(e)} def _check_oauth_credentials(self) -> List[str]: """Check for missing OAuth credentials in environment""" required_credentials = { "OUTLOOK_CLIENT_ID": "Microsoft Outlook", "OUTLOOK_CLIENT_SECRET": "Microsoft Outlook", "TEAMS_CLIENT_ID": "Microsoft Teams", "TEAMS_CLIENT_SECRET": "Microsoft Teams", "GITHUB_CLIENT_ID": "GitHub", "GITHUB_CLIENT_SECRET": "GitHub" } missing = [] for env_var, service in required_credentials.items(): if not os.getenv(env_var): missing.append(f"{env_var} ({service})") return missing def configure_remaining_oauth_services(self) -> bool: """Configure remaining OAuth services with placeholder credentials""" self.log_step( "oauth_service_completion", "running", f"Configuring remaining OAuth services: {', '.join(self.remaining_services)}" ) # Create configuration template for remaining services # Now reads from environment variables instead of TODO placeholders production_domain = os.getenv("PRODUCTION_DOMAIN", "your-production-domain.com") oauth_config = { "outlook": { "client_id": os.getenv("OUTLOOK_CLIENT_ID", ""), "client_secret": os.getenv("OUTLOOK_CLIENT_SECRET", ""), "redirect_uri": f"https://{production_domain}/api/auth/outlook/oauth2callback", "scopes": ["https://graph.microsoft.com/Mail.Read", "https://graph.microsoft.com/Calendars.Read"], "configured": bool(os.getenv("OUTLOOK_CLIENT_ID") and os.getenv("OUTLOOK_CLIENT_SECRET")) }, "teams": { "client_id": os.getenv("TEAMS_CLIENT_ID", ""), "client_secret": os.getenv("TEAMS_CLIENT_SECRET", ""), "redirect_uri": f"https://{production_domain}/api/auth/teams/oauth2callback", "scopes": ["https://graph.microsoft.com/Team.ReadBasic.All"], "configured": bool(os.getenv("TEAMS_CLIENT_ID") and os.getenv("TEAMS_CLIENT_SECRET")) }, "github": { "client_id": os.getenv("GITHUB_CLIENT_ID", ""), "client_secret": os.getenv("GITHUB_CLIENT_SECRET", ""), "redirect_uri": f"https://{production_domain}/api/auth/github/oauth2callback", "scopes": ["repo", "user", "read:org"], "configured": bool(os.getenv("GITHUB_CLIENT_ID") and os.getenv("GITHUB_CLIENT_SECRET")) } } # Save OAuth configuration template config_file = "oauth_remaining_services_config.json" with open(config_file, "w") as f: json.dump(oauth_config, f, indent=2) self.log_step( "oauth_service_completion", "success", f"OAuth configuration template created: {config_file}" ) # Create setup instructions instructions = self._generate_oauth_setup_instructions() instructions_file = "OAUTH_SERVICE_SETUP_INSTRUCTIONS.md" with open(instructions_file, "w") as f: f.write(instructions) self.log_step( "oauth_service_completion", "info", f"Setup instructions created: {instructions_file}" ) return True def _generate_oauth_setup_instructions(self) -> str: """Generate OAuth service setup instructions""" return f"""# OAuth Service Setup Instructions ## Remaining Services to Configure ### 1. Microsoft Outlook/Teams **Steps:** 1. Go to [Azure Portal](https://portal.azure.com) 2. Navigate to Azure Active Directory > App registrations 3. Create a new application registration 4. Configure redirect URIs: - `https://your-production-domain.com/api/auth/outlook/oauth2callback` - `https://your-production-domain.com/api/auth/teams/oauth2callback` 5. Add required API permissions: - Microsoft Graph > Mail.Read - Microsoft Graph > Calendars.Read - Microsoft Graph > Team.ReadBasic.All 6. Copy Client ID and Client Secret to environment variables ### 2. GitHub **Steps:** 1. Go to [GitHub Developer Settings](https://github.com/settings/developers) 2. Create a new OAuth App 3. Configure: - Application name: Atom AI Assistant - Homepage URL: https://your-production-domain.com - Authorization callback URL: `https://your-production-domain.com/api/auth/github/oauth2callback` 4. Copy Client ID and Client Secret to environment variables ## Environment Variables to Set ```bash # Microsoft Outlook/Teams OUTLOOK_CLIENT_ID=your_microsoft_client_id OUTLOOK_CLIENT_SECRET=your_microsoft_client_secret TEAMS_CLIENT_ID=your_teams_client_id TEAMS_CLIENT_SECRET=your_teams_client_secret # GitHub GITHUB_CLIENT_ID=your_github_client_id GITHUB_CLIENT_SECRET=your_github_client_secret # Production Domain PRODUCTION_DOMAIN=your-production-domain.com ``` ## Verification Steps 1. Update the environment variables above 2. Restart the backend server 3. Run OAuth validation: `python test_oauth_validation.py` 4. Verify all 10 services show as connected Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ def setup_production_environment(self) -> bool: """Setup production environment configuration""" self.log_step("production_environment", "running", "Setting up production environment") try: # Generate production environment template env_template = self._generate_production_env_template() env_file = ".env.production.template" with open(env_file, "w") as f: f.write(env_template) self.log_step( "production_environment", "success", f"Production environment template created: {env_file}" ) # Create production deployment configuration deployment_config = self._generate_deployment_config() config_file = "production_deployment_config.json" with open(config_file, "w") as f: json.dump(deployment_config, f, indent=2) self.log_step( "production_environment", "success", f"Deployment configuration created: {config_file}" ) return True except Exception as e: self.log_step( "production_environment", "failed", f"Production environment setup failed: {str(e)}" ) return False def _generate_production_env_template(self) -> str: """Generate production environment template""" return f"""# Production Environment Configuration # Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} # Application Settings FLASK_ENV=production DEBUG=False SECRET_KEY={secrets.token_urlsafe(32)} # Server Configuration HOST=0.0.0.0 PORT=5058 PRODUCTION_DOMAIN=your-production-domain.com # Database Configuration DATABASE_URL=postgresql://username:password@localhost/atom_production # or for SQLite: # DATABASE_URL=sqlite:///./data/atom_production.db # Security Configuration ATOM_OAUTH_ENCRYPTION_KEY={secrets.token_urlsafe(32)} CSRF_ENABLED=True SESSION_SECURE=True # OAuth Configuration - Update with real credentials GOOGLE_CLIENT_ID=your_google_client_id GOOGLE_CLIENT_SECRET=your_google_client_secret SLACK_CLIENT_ID=your_slack_client_id SLACK_CLIENT_SECRET=your_slack_client_secret TRELLO_API_KEY=your_trello_api_key TRELLO_API_SECRET=your_trello_api_secret ASANA_CLIENT_ID=your_asana_client_id ASANA_CLIENT_SECRET=your_asana_client_secret NOTION_CLIENT_ID=your_notion_client_id NOTION_CLIENT_SECRET=your_notion_client_secret DROPBOX_CLIENT_ID=your_dropbox_client_id DROPBOX_CLIENT_SECRET=your_dropbox_client_secret # Remaining OAuth Services - Configure with real credentials # Microsoft Outlook (Calendar & Email integration) OUTLOOK_CLIENT_ID= OUTLOOK_CLIENT_SECRET= # Microsoft Teams (Chat & Collaboration integration) TEAMS_CLIENT_ID= TEAMS_CLIENT_SECRET= # GitHub (Repository & Issue integration) GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= # AI Provider Configuration OPENAI_API_KEY=your_openai_api_key ANTHROPIC_API_KEY=your_anthropic_api_key DEEPSEEK_API_KEY=your_deepseek_api_key GOOGLE_AI_API_KEY=your_google_ai_api_key # Monitoring & Analytics ENABLE_METRICS=True LOG_LEVEL=INFO HEALTH_CHECK_INTERVAL=30 # SSL/TLS Configuration (for production) SSL_CERT_PATH=/path/to/ssl/certificate.crt SSL_KEY_PATH=/path/to/ssl/private.key # Rate Limiting RATE_LIMIT_REQUESTS=1000 RATE_LIMIT_WINDOW=3600 """ def _generate_deployment_config(self) -> Dict[str, Any]: """Generate deployment configuration""" return { "deployment_id": f"atom_production_{self.start_time.strftime('%Y%m%d_%H%M%S')}", "timestamp": self.start_time.isoformat(), "components": { "backend": { "status": "ready", "port": 5058, "health_endpoint": "/healthz", "dependencies": ["database", "oauth_services"] }, "database": { "status": "configured", "type": "sqlite", # or "postgresql" "path": "./data/atom_production.db" }, "oauth_services": { "status": "partial", "connected": 7, "total": 10, "remaining": self.remaining_services }, "security": { "status": "implemented", "features": ["csrf_protection", "token_encryption", "secure_sessions"] }, "monitoring": { "status": "configured", "endpoints": ["/healthz", "/api/services/status", "/api/auth/oauth-status"] } }, "deployment_steps": [ "environment_configuration", "oauth_service_completion", "ssl_tls_setup", "monitoring_setup", "backup_configuration", "final_validation" ], "requirements": { "ssl_certificate": "required", "domain_configuration": "required", "environment_variables": "required", "database_backup": "recommended" } } def setup_ssl_tls_configuration(self) -> bool: """Setup SSL/TLS configuration for production""" self.log_step("ssl_tls_setup", "running", "Setting up SSL/TLS configuration") try: # Create SSL/TLS setup instructions ssl_instructions = self._generate_ssl_setup_instructions() ssl_file = "SSL_TLS_SETUP_GUIDE.md" with open(ssl_file, "w") as f: f.write(ssl_instructions) self.log_step( "ssl_tls_setup", "success", f"SSL/TLS setup guide created: {ssl_file}" ) # Create nginx configuration template nginx_config = self._generate_nginx_config() nginx_file = "nginx_production.conf" with open(nginx_file, "w") as f: f.write(nginx_config) self.log_step( "ssl_tls_setup", "success", f"NGINX configuration template created: {nginx_file}" ) return True except Exception as e: self.log_step( "ssl_tls_setup", "failed", f"SSL/TLS setup failed: {str(e)}" ) return False def _generate_ssl_setup_instructions(self) -> str: """Generate SSL/TLS setup instructions""" return f"""# SSL/TLS Setup Guide for Production ## Options for SSL/TLS Certificate ### 1. Let's Encrypt (Free) ```bash # Install certbot sudo apt update sudo apt install certbot python3-certbot-nginx # Get certificate sudo certbot --nginx -d your-production-domain.com # Auto-renewal sudo crontab -e # Add: 0 12 * * * /usr/bin/certbot renew --quiet ``` ### 2. Commercial Certificate 1. Purchase SSL certificate from provider (DigiCert, Comodo, etc.) 2. Generate CSR and private key 3. Submit CSR to certificate authority 4. Install issued certificate ### 3. Self-Signed (Development Only) ```bash # Generate self-signed certificate (NOT for production) openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes ``` ## NGINX Configuration See `nginx_production.conf` for complete configuration template. ## Environment Variables ```bash SSL_CERT_PATH=/etc/ssl/certs/your-domain.crt SSL_KEY_PATH=/etc/ssl/private/your-domain.key ``` ## Verification ```bash # Test SSL configuration openssl s_client -connect your-production-domain.com:443 # Check certificate validity openssl x509 -in /path/to/certificate.crt -text -noout ``` Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ def _generate_nginx_config(self) -> str: """Generate NGINX configuration template""" return f"""# NGINX Production Configuration for Atom AI Assistant # Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} server {{ listen 80; server_name your-production-domain.com; return 301 https://$server_name$request_uri; }} server {{ listen 443 ssl http2; server_name your-production-domain.com; # SSL Configuration ssl_certificate /etc/ssl/certs/your-domain.crt; ssl_certificate_key /etc/ssl/private/your-domain.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; ssl_prefer_server_ciphers off; # Security Headers add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Referrer-Policy "strict-origin-when-cross-origin"; # Proxy to Flask application location / {{ proxy_pass http://localhost:5058; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }} # Static files location /static/ {{ alias /path/to/your/static/files/; expires 1y; add_header Cache-Control "public, immutable"; }} # Rate limiting limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; location /api/ {{ limit_req zone=api burst=20 nodelay; proxy_pass http://localhost:5058; }} # Health checks location /healthz {{ access_log off; proxy_pass http://localhost:5058; }} # OAuth callbacks - no rate limiting location /api/auth