Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| # Combined static file server + Token API for Hugging Face Spaces (Docker) | |
| # Serves token-service.html at '/' | |
| # API endpoints under '/api': | |
| # - POST /api/set_token { token: str, timestamp?: int, source?: str } | |
| # - GET /api/token { token: str|null, nonce: str, updated_at: int|null } | |
| # - GET /api/status { is_set: bool, updated_at: int|null } | |
| # Uses only Python standard library. | |
| import json | |
| import os | |
| import time | |
| import uuid | |
| from http.server import SimpleHTTPRequestHandler, HTTPServer | |
| from urllib.parse import urlparse | |
| from pathlib import Path | |
| ROOT = Path(__file__).parent.resolve() | |
| STATIC_DIR = ROOT / "static" | |
| PORT = int(os.environ.get("PORT", "7860")) # HF Spaces typically sets PORT=7860 | |
| HOST = os.environ.get("HOST", "0.0.0.0") | |
| SET_SECRET = os.environ.get("TOKEN_SET_SECRET") # optional; require header X-Token-Set-Secret | |
| storage = { | |
| "token": None, | |
| "updated_at": None, | |
| } | |
| def now_ms() -> int: | |
| return int(time.time() * 1000) | |
| class Handler(SimpleHTTPRequestHandler): | |
| # Serve from STATIC_DIR by default | |
| def translate_path(self, path): | |
| # Map '/' to token-service.html | |
| parsed = urlparse(path) | |
| p = parsed.path | |
| if p == "/": | |
| return str(STATIC_DIR / "token-service.html") | |
| # Ensure path traversal safe | |
| safe = Path(p.lstrip("/")) | |
| return str((STATIC_DIR / safe).resolve()) | |
| # CORS helpers | |
| def _set_cors(self): | |
| self.send_header("Access-Control-Allow-Origin", "*") | |
| self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") | |
| self.send_header("Access-Control-Allow-Headers", "*, Content-Type, X-Token-Set-Secret") | |
| self.send_header("Access-Control-Max-Age", "600") | |
| def _send_json(self, code: int, obj): | |
| body = json.dumps(obj, ensure_ascii=False).encode("utf-8") | |
| self.send_response(code) | |
| self._set_cors() | |
| self.send_header("Content-Type", "application/json; charset=utf-8") | |
| self.send_header("Content-Length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| def do_OPTIONS(self): | |
| if self.path.startswith("/api/"): | |
| self.send_response(204) | |
| self._set_cors() | |
| self.end_headers() | |
| else: | |
| super().do_OPTIONS() | |
| def do_GET(self): | |
| if self.path.startswith("/api/"): | |
| parsed = urlparse(self.path) | |
| if parsed.path == "/api/token": | |
| self._send_json(200, { | |
| "token": storage["token"], | |
| "nonce": str(uuid.uuid4()), | |
| "updated_at": storage["updated_at"], | |
| }) | |
| return | |
| if parsed.path == "/api/status": | |
| self._send_json(200, { | |
| "is_set": storage["token"] is not None, | |
| "updated_at": storage["updated_at"], | |
| }) | |
| return | |
| self._send_json(404, {"error": "not_found"}) | |
| return | |
| # Static files | |
| return super().do_GET() | |
| def do_POST(self): | |
| if self.path.startswith("/api/"): | |
| parsed = urlparse(self.path) | |
| if parsed.path == "/api/set_token": | |
| if SET_SECRET: | |
| header_secret = self.headers.get("X-Token-Set-Secret") | |
| if header_secret != SET_SECRET: | |
| self._send_json(403, {"error": "forbidden"}) | |
| return | |
| try: | |
| length = int(self.headers.get("Content-Length") or 0) | |
| data = json.loads(self.rfile.read(length) or b"{}") | |
| except Exception: | |
| self._send_json(400, {"error": "invalid_json"}) | |
| return | |
| token = data.get("token") | |
| if not token or not isinstance(token, str): | |
| self._send_json(400, {"error": "missing_token"}) | |
| return | |
| storage["token"] = token | |
| storage["updated_at"] = int(data.get("timestamp") or now_ms()) | |
| print(f"[token-server] token set at {storage['updated_at']}") | |
| self._send_json(200, {"success": True}) | |
| return | |
| self._send_json(404, {"error": "not_found"}) | |
| return | |
| # Not an API path | |
| self.send_error(405, "Method Not Allowed") | |
| # Silence access log a bit | |
| def log_message(self, fmt, *args): | |
| if self.path.startswith("/api/"): | |
| return | |
| super().log_message(fmt, *args) | |
| def main(): | |
| # Ensure static dir exists | |
| STATIC_DIR.mkdir(parents=True, exist_ok=True) | |
| httpd = HTTPServer((HOST, PORT), Handler) | |
| print(f"[hf-token-service] listening on http://{HOST}:{PORT}") | |
| print(" - Static: / -> static/token-service.html") | |
| print(" - API: /api/token, /api/status, /api/set_token") | |
| try: | |
| httpd.serve_forever() | |
| except KeyboardInterrupt: | |
| print("\n[hf-token-service] shutting down") | |
| if __name__ == "__main__": | |
| main() | |