python-enviroment / server.py
Karan6933's picture
Upload 7 files
442ee00 verified
Raw
History Blame Contribute Delete
3.49 kB
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
import json
import asyncio
import os
import uuid
import shutil
from runner import ProcessRunner
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
PROJECTS_DIR = os.path.join(os.path.dirname(__file__), "projects")
if not os.path.exists(PROJECTS_DIR):
os.makedirs(PROJECTS_DIR)
active_runners = {}
def cleanup_old_projects():
try:
projects = []
for name in os.listdir(PROJECTS_DIR):
path = os.path.join(PROJECTS_DIR, name)
if os.path.isdir(path):
mtime = os.stat(path).st_mtime
projects.append((mtime, name, path))
# Sort by modification time, newest first
projects.sort(reverse=True)
# Keep the 4 newest, delete the rest so we have max 5 after creating a new one
for _, name, path in projects[4:]:
if name not in active_runners:
shutil.rmtree(path, ignore_errors=True)
except Exception as e:
print(f"Error cleaning up projects: {e}")
@app.websocket("/ws/run")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
cleanup_old_projects()
project_id = str(uuid.uuid4())
runner = ProcessRunner(project_id, PROJECTS_DIR)
active_runners[project_id] = runner
try:
while True:
data = await websocket.receive_text()
payload = json.loads(data)
files = payload.get("files", [])
if not files:
await websocket.send_text(json.dumps({"type": "error", "message": "No files provided"}))
continue
async def log_callback(msg: str):
await websocket.send_text(json.dumps({"type": "log", "content": msg}))
await log_callback(f"✦ Preparing environment for project {project_id}...\n")
await runner.setup(files)
await log_callback("✦ Synchronizing dependencies...\n")
await runner.install_dependencies(log_callback)
await log_callback("🚀 Launching application...\n")
await runner.run(log_callback)
# Keep connection open and monitor for timeout or closure
# In a real production app, we'd have more complex process management here
try:
# Wait for disconnect or some signal from client
while True:
await asyncio.wait_for(websocket.receive_text(), timeout=3600)
except asyncio.TimeoutError:
await log_callback("\n[System] Session heartbeat timeout (1 hour).\n")
except WebSocketDisconnect:
print(f"Client disconnected: {project_id}")
except Exception as e:
print(f"Error: {e}")
try:
await websocket.send_text(json.dumps({"type": "error", "message": str(e)}))
except:
pass
finally:
await runner.stop()
# runner.cleanup() # Optional: keep files for debugging or clean them up
if project_id in active_runners:
del active_runners[project_id]
if __name__ == "__main__":
import uvicorn
# Hugging Face Spaces expose port 7860 by default
uvicorn.run(app, host="0.0.0.0", port=7860)