""" VTX License Server — FastAPI application for Hugging Face Spaces. POST /api/validate GET /api/health Keys are loaded from the private ManChildTechnologies/VTX-BetaKeys dataset. """ from __future__ import annotations import os import threading from typing import Any from fastapi import FastAPI from fastapi.responses import JSONResponse from pydantic import BaseModel, Field import uvicorn from beta_keys_schema import today_iso, validation_result from beta_keys_store import keys_status, load_keys, update_key_record app = FastAPI( title="VTX License Server", description="Beta key validation API for VTX Studio", version="1.1.0", ) _refresh_timer: threading.Timer | None = None class ValidateRequest(BaseModel): beta_key: str = Field( ..., description="VTX Studio beta license key", examples=["VTX-4D8A9B", "VTX-BETA-XXXX-XXXX"], ) def validate_beta_key(beta_key: str, *, track_activation: bool = True) -> dict[str, Any]: key = str(beta_key or "").strip() if not key: return {"valid": False, "active": False, "status": "INVALID"} record = load_keys().get(key) result = validation_result(record) if not result.get("valid") or not result.get("active"): return result if track_activation: _schedule_activation_update(key) return result def _schedule_activation_update(beta_key: str) -> None: def _worker() -> None: try: update_key_record( beta_key, lambda record: {**record, "last_activation": today_iso()}, commit_message=f"Track activation for {beta_key}", ) except Exception: pass thread = threading.Thread(target=_worker, name=f"vtx-activation-{beta_key}", daemon=True) thread.start() def _background_refresh() -> None: global _refresh_timer try: load_keys(force=True) except Exception: pass _refresh_timer = threading.Timer(float(os.environ.get("VTX_KEYS_REFRESH_SECONDS", "30") or 30), _background_refresh) _refresh_timer.daemon = True _refresh_timer.start() @app.on_event("startup") async def startup_refresh() -> None: try: load_keys(force=True) except Exception: pass _background_refresh() @app.on_event("shutdown") async def shutdown_refresh() -> None: global _refresh_timer if _refresh_timer is not None: _refresh_timer.cancel() _refresh_timer = None @app.post("/api/validate") async def api_validate(payload: ValidateRequest) -> dict[str, Any]: return validate_beta_key(payload.beta_key) @app.get("/api/health") async def api_health() -> JSONResponse: try: keys = load_keys() status = keys_status() status["ok"] = True status["service"] = "VTX-License-Server" status["keys_loaded"] = len(keys) return JSONResponse(content=status) except Exception as exc: status = keys_status() status["ok"] = bool(status.get("keys_loaded")) status["service"] = "VTX-License-Server" status["error"] = str(exc) code = 200 if status["ok"] else 500 return JSONResponse(content=status, status_code=code) if __name__ == "__main__": port = int(os.environ.get("PORT", os.environ.get("HF_SPACE_PORT", "7860"))) uvicorn.run(app, host="0.0.0.0", port=port)