import gradio as gr import threading import uvicorn import os import sys import uuid import json import time from datetime import datetime from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse import requests print("=" * 60) print("๐Ÿš€ STARTING STATUS TRACKER") print("=" * 60) print(f"Python version: {sys.version}") print(f"Gradio version: {gr.__version__}") # ============================================= # CREATE FASTAPI APP # ============================================= app = FastAPI(title="Status Tracker API") # Add CORS middleware - CRITICAL for n8n access app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ============================================= # IN-MEMORY STORAGE # ============================================= projects = {} # ============================================= # API ENDPOINTS # ============================================= @app.get("/") async def root(): """Root endpoint with API documentation""" return { "name": "Status Tracker API", "version": "1.0.0", "status": "running", "active_projects": len(projects), "endpoints": { "health": "GET /health", "test": "GET /test", "register": "POST /register", "update": "POST /update", "status": "GET /status/{project_id}" }, "timestamp": datetime.now().isoformat() } @app.get("/health") async def health(): """Health check endpoint""" return { "status": "healthy", "active_projects": len(projects), "timestamp": datetime.now().isoformat() } @app.get("/test") async def test(): """Simple test endpoint to verify API is working""" return { "message": "API is working!", "timestamp": datetime.now().isoformat() } @app.post("/register") async def register_project(request: Request): """Register a new project with the tracker""" try: data = await request.json() project_id = data.get("project_id") or str(uuid.uuid4())[:8] print(f"\n๐Ÿ“ Registering project: {project_id}") print(f" Webhook: {data.get('n8n_webhook')}") print(f" Services: {data.get('services', [])}") print(f" Chat ID: {data.get('chat_id')}") # ADDED: Debug print projects[project_id] = { "project_id": project_id, "n8n_webhook": data.get("n8n_webhook"), "chat_id": data.get("chat_id"), # ADDED: Store chat_id "services": {service: {"status": "pending", "file_urls": []} for service in data.get("services", [])}, "created_at": datetime.now().isoformat(), "completed_at": None, "last_update": datetime.now().isoformat() } return { "status": "registered", "project_id": project_id, "webhook": data.get("n8n_webhook") } except Exception as e: print(f"โŒ Registration error: {e}") return JSONResponse(status_code=500, content={"error": str(e)}) @app.post("/update") async def update_status(request: Request): """Update status for a service""" try: data = await request.json() project_id = data.get("project_id") service_type = data.get("service_type") print(f"\n๐Ÿ“ฅ Update for {project_id} - {service_type}: {data.get('status')}") if project_id not in projects: return JSONResponse(status_code=404, content={"error": "Project not found"}) project = projects[project_id] project["last_update"] = datetime.now().isoformat() # Update service status if service_type in project["services"]: project["services"][service_type] = { "status": data.get("status", "unknown"), "file_urls": data.get("file_urls", []), "updated_at": datetime.now().isoformat() } else: # Add new service if not in original list project["services"][service_type] = { "status": data.get("status", "unknown"), "file_urls": data.get("file_urls", []), "updated_at": datetime.now().isoformat() } # Check if all services are complete all_completed = all( s.get("status") == "completed" for s in project["services"].values() ) print(f" All completed: {all_completed}") if all_completed and not project.get("completed_at"): project["completed_at"] = datetime.now().isoformat() print(f"๐ŸŽ‰ Project {project_id} COMPLETED!") # Send webhook to n8n if project.get("n8n_webhook"): try: webhook_data = { "project_id": project_id, "status": "completed", "completed_at": project["completed_at"], "services": project["services"], "chat_id": project.get("chat_id") # ADDED: Include chat_id in webhook } response = requests.post( project["n8n_webhook"], json=webhook_data, timeout=10, headers={"Content-Type": "application/json"} ) print(f"๐Ÿ“ค Webhook sent: {response.status_code}") except Exception as e: print(f"โš ๏ธ Webhook error: {e}") return { "status": "updated", "project_id": project_id, "all_completed": all_completed } except Exception as e: print(f"โŒ Update error: {e}") return JSONResponse(status_code=500, content={"error": str(e)}) @app.get("/status/{project_id}") async def get_status(project_id: str): """Get current status of a project""" if project_id not in projects: return JSONResponse(status_code=404, content={"error": "Project not found"}) project = projects[project_id] all_completed = all( s.get("status") == "completed" for s in project["services"].values() ) return { "project_id": project_id, "created_at": project["created_at"], "completed_at": project.get("completed_at"), "services": project["services"], "all_completed": all_completed } @app.get("/projects") async def list_projects(): """List all active projects""" return { "active_projects": len(projects), "projects": list(projects.keys()) } # ============================================= # BACKGROUND CLEANUP TASK # ============================================= def cleanup_old_projects(): """Remove completed projects after 1 hour""" while True: time.sleep(300) # Check every 5 minutes now = datetime.now() to_delete = [] for pid, project in projects.items(): # Remove completed projects after 1 hour if project.get("completed_at"): completed_time = datetime.fromisoformat(project["completed_at"]) if (now - completed_time).total_seconds() > 3600: to_delete.append(pid) # Remove stuck projects after 2 hours elif (now - datetime.fromisoformat(project["created_at"])).total_seconds() > 7200: to_delete.append(pid) for pid in to_delete: del projects[pid] print(f"๐Ÿงน Cleaned up project: {pid}") # Start cleanup thread cleanup_thread = threading.Thread(target=cleanup_old_projects, daemon=True) cleanup_thread.start() print("โœ… Cleanup thread started") # ============================================= # STARTUP COMPLETE # ============================================= print("\n" + "=" * 60) print("โœ… Status Tracker API ready!") print(f"๐Ÿ“Š Active projects: {len(projects)}") print("\n๐Ÿ“ก Endpoints (all public):") print(" - GET / - API info") print(" - GET /health - Health check") print(" - GET /test - Test endpoint") print(" - POST /register - Register project") print(" - POST /update - Update status") print(" - GET /status/ID - Get project status") print(" - GET /projects - List all projects") print("\n๐ŸŒ Base URL: https://yukee1992-status-tracker.hf.space") print("=" * 60) # ============================================= # RUN # ============================================= if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)