|
| 1 | +import asyncio |
| 2 | +import os |
| 3 | +import uuid |
| 4 | +from fastapi import FastAPI, HTTPException, UploadFile, File, Depends, Form |
| 5 | +from fastapi.security import APIKeyHeader |
| 6 | +from fastapi.middleware.cors import CORSMiddleware |
| 7 | +from pydantic import BaseModel |
| 8 | +from typing import Dict, List, Optional |
| 9 | +import logging |
| 10 | +from vial_manager import VialManager |
| 11 | +from webxos_wallet import WebXOSWallet |
| 12 | +from auth_manager import AuthManager |
| 13 | +from export_manager import ExportManager |
| 14 | +from langchain_agent import create_langchain_agent |
| 15 | + |
| 16 | +# Configure logging |
| 17 | +logging.basicConfig(level=logging.INFO) |
| 18 | +logger = logging.getLogger(__name__) |
| 19 | + |
| 20 | +app = FastAPI(title="Vial MCP API", version="2.1") |
| 21 | + |
| 22 | +# CORS configuration |
| 23 | +app.add_middleware( |
| 24 | + CORSMiddleware, |
| 25 | + allow_origins=["*"], |
| 26 | + allow_credentials=True, |
| 27 | + allow_methods=["*"], |
| 28 | + allow_headers=["*"], |
| 29 | +) |
| 30 | + |
| 31 | +# Initialize managers |
| 32 | +wallet = WebXOSWallet() |
| 33 | +auth_manager = AuthManager() |
| 34 | +vial_manager = VialManager(wallet) |
| 35 | +export_manager = ExportManager(vial_manager, wallet) |
| 36 | +langchain_agent = create_langchain_agent() |
| 37 | + |
| 38 | +# Authentication |
| 39 | +API_KEY_NAME = "Authorization" |
| 40 | +api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False) |
| 41 | + |
| 42 | +async def get_api_key(api_key: str = Depends(api_key_header)): |
| 43 | + if api_key and api_key.startswith("Bearer "): |
| 44 | + token = api_key.replace("Bearer ", "") |
| 45 | + if auth_manager.validate_token(token): |
| 46 | + return token |
| 47 | + raise HTTPException(status_code=401, detail="Invalid or missing API key") |
| 48 | + |
| 49 | +# Pydantic models |
| 50 | +class AuthRequest(BaseModel): |
| 51 | + client: str |
| 52 | + deviceId: str |
| 53 | + sessionId: str |
| 54 | + networkId: str |
| 55 | + |
| 56 | +class CommsRequest(BaseModel): |
| 57 | + message: str |
| 58 | + network_id: str |
| 59 | + |
| 60 | +# API Endpoints |
| 61 | +@app.post("/auth") |
| 62 | +async def authenticate(auth: AuthRequest): |
| 63 | + try: |
| 64 | + token, address = auth_manager.authenticate(auth.networkId, auth.sessionId) |
| 65 | + logger.info(f"Authenticated session: {token}") |
| 66 | + return {"token": token, "address": address} |
| 67 | + except Exception as e: |
| 68 | + logger.error(f"Auth error: {str(e)}") |
| 69 | + with open("errorlog.md", "a") as f: |
| 70 | + f.write(f"- **[2025-08-10T20:23:00Z]** Auth error: {str(e)}\n") |
| 71 | + raise HTTPException(status_code=500, detail=str(e)) |
| 72 | + |
| 73 | +@app.post("/void") |
| 74 | +async def void_network(token: str = Depends(get_api_key)): |
| 75 | + try: |
| 76 | + auth_manager.void_session(token) |
| 77 | + vial_manager.reset_vials() |
| 78 | + logger.info("Network voided") |
| 79 | + return {"status": "voided"} |
| 80 | + except Exception as e: |
| 81 | + logger.error(f"Void error: {str(e)}") |
| 82 | + with open("errorlog.md", "a") as f: |
| 83 | + f.write(f"- **[2025-08-10T20:23:00Z]** Void error: {str(e)}\n") |
| 84 | + raise HTTPException(status_code=500, detail=str(e)) |
| 85 | + |
| 86 | +@app.get("/health") |
| 87 | +async def health_check(): |
| 88 | + try: |
| 89 | + return {"status": "ok"} |
| 90 | + except Exception as e: |
| 91 | + logger.error(f"Health check error: {str(e)}") |
| 92 | + with open("errorlog.md", "a") as f: |
| 93 | + f.write(f"- **[2025-08-10T20:23:00Z]** Health check error: {str(e)}\n") |
| 94 | + raise HTTPException(status_code=500, detail=str(e)) |
| 95 | + |
| 96 | +@app.post("/train") |
| 97 | +async def train_vials(file: UploadFile = File(...), networkId: str = Form(...), token: str = Depends(get_api_key)): |
| 98 | + try: |
| 99 | + if not auth_manager.validate_session(token, networkId): |
| 100 | + raise HTTPException(status_code=403, detail="Invalid network ID") |
| 101 | + |
| 102 | + content = await file.read() |
| 103 | + content_str = content.decode('utf-8') |
| 104 | + |
| 105 | + balance_earned = vial_manager.train_vials(networkId, content_str, file.filename) |
| 106 | + logger.info(f"Trained vials for network: {networkId}") |
| 107 | + return {"vials": vial_manager.get_vials(), "balance": balance_earned} |
| 108 | + except Exception as e: |
| 109 | + logger.error(f"Train error: {str(e)}") |
| 110 | + with open("errorlog.md", "a") as f: |
| 111 | + f.write(f"- **[2025-08-10T20:23:00Z]** Train error: {str(e)}\n") |
| 112 | + raise HTTPException(status_code=500, detail=str(e)) |
| 113 | + |
| 114 | +@app.get("/export") |
| 115 | +async def export_vials(networkId: str, token: str = Depends(get_api_key)): |
| 116 | + try: |
| 117 | + if not auth_manager.validate_session(token, networkId): |
| 118 | + raise HTTPException(status_code=403, detail="Invalid network ID") |
| 119 | + |
| 120 | + markdown = export_manager.export_to_markdown(token, networkId) |
| 121 | + logger.info(f"Exported vials for network: {networkId}") |
| 122 | + return {"markdown": markdown} |
| 123 | + except Exception as e: |
| 124 | + logger.error(f"Export error: {str(e)}") |
| 125 | + with open("errorlog.md", "a") as f: |
| 126 | + f.write(f"- **[2025-08-10T20:23:00Z]** Export error: {str(e)}\n") |
| 127 | + raise HTTPException(status_code=500, detail=str(e)) |
| 128 | + |
| 129 | +@app.post("/upload") |
| 130 | +async def upload_file(file: UploadFile = File(...), networkId: str = Form(...), token: str = Depends(get_api_key)): |
| 131 | + try: |
| 132 | + if not auth_manager.validate_session(token, networkId): |
| 133 | + raise HTTPException(status_code=403, detail="Invalid network ID") |
| 134 | + |
| 135 | + content = await file.read() |
| 136 | + file_path = f"/uploads/{file.filename}" |
| 137 | + logger.info(f"File uploaded: {file_path}") |
| 138 | + return {"filePath": file_path} |
| 139 | + except Exception as e: |
| 140 | + logger.error(f"Upload error: {str(e)}") |
| 141 | + with open("errorlog.md", "a") as f: |
| 142 | + f.write(f"- **[2025-08-10T20:23:00Z]** Upload error: {str(e)}\n") |
| 143 | + raise HTTPException(status_code=500, detail=str(e)) |
| 144 | + |
| 145 | +@app.post("/comms_hub") |
| 146 | +async def comms_hub(request: CommsRequest, token: str = Depends(get_api_key)): |
| 147 | + try: |
| 148 | + if not auth_manager.validate_session(token, request.network_id): |
| 149 | + raise HTTPException(status_code=403, detail="Invalid network ID") |
| 150 | + |
| 151 | + response = await langchain_agent.arun(request.message) |
| 152 | + logger.info(f"Comms processed: {request.message}") |
| 153 | + return {"response": response} |
| 154 | + except Exception as e: |
| 155 | + logger.error(f"Comms error: {str(e)}") |
| 156 | + with open("errorlog.md", "a") as f: |
| 157 | + f.write(f"- **[2025-08-10T20:23:00Z]** Comms error: {str(e)}\n") |
| 158 | + raise HTTPException(status_code=500, detail=str(e)) |
| 159 | + |
| 160 | +if __name__ == "__main__": |
| 161 | + import uvicorn |
| 162 | + uvicorn.run(app, host="0.0.0.0", port=5000) |
0 commit comments