status-tracker / app.py
yukee1992's picture
Update app.py
eea784e verified
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)