| 425 | # ─── HTTP Handler ──────────────────────────────────────────────────────────── |
| 426 | |
| 427 | class GeminiHandler(BaseHTTPRequestHandler): |
| 428 | def log_message(self, fmt, *args): |
| 429 | log(fmt % args) |
| 430 | |
| 431 | def send_json(self, data, status=200): |
| 432 | body = json.dumps(data, ensure_ascii=False).encode() |
| 433 | self.send_response(status) |
| 434 | self.send_header("Content-Type", "application/json") |
| 435 | self.send_header("Access-Control-Allow-Origin", "*") |
| 436 | self.send_header("Content-Length", str(len(body))) |
| 437 | self.end_headers() |
| 438 | self.wfile.write(body) |
| 439 | |
| 440 | def _authorized(self): |
| 441 | keys = CONFIG.get("api_keys") or [] |
| 442 | if not keys: |
| 443 | return True |
| 444 | auth = self.headers.get("Authorization", "") |
| 445 | key = auth[7:] if auth.startswith("Bearer ") else self.headers.get("x-api-key", "") |
| 446 | return key in keys |
| 447 | |
| 448 | def do_OPTIONS(self): |
| 449 | self.send_response(204) |
| 450 | self.send_header("Access-Control-Allow-Origin", "*") |
| 451 | self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") |
| 452 | self.send_header("Access-Control-Allow-Headers", "*") |
| 453 | self.end_headers() |
| 454 | |
| 455 | def do_GET(self): |
| 456 | try: |
| 457 | if self.path.startswith("/v1/") and not self._authorized(): |
| 458 | self.send_json({"error": {"message": "invalid api key"}}, 401) |
| 459 | return |
| 460 | if self.path == "/v1/models": |
| 461 | self.send_json({"object": "list", "data": [ |
| 462 | {"id": n, "object": "model", "created": 1700000000, |
| 463 | "owned_by": "google", "description": c["desc"]} |
| 464 | for n, c in MODELS.items() |
| 465 | ]}) |
| 466 | elif self.path.startswith("/v1beta/models"): |
| 467 | self._handle_google_models_list() |
| 468 | elif self.path == "/": |
| 469 | self.send_json({"status": "ok", "version": __version__, |
| 470 | "models": list(MODELS.keys())}) |
| 471 | else: |
| 472 | self.send_json({"error": "not found"}, 404) |
| 473 | except (BrokenPipeError, ConnectionResetError): |
| 474 | pass |
| 475 | except Exception as e: |
| 476 | log(f"GET error: {e}") |
| 477 | |
| 478 | def do_POST(self): |
| 479 | try: |
| 480 | if self.path.startswith("/v1/") and not self._authorized(): |
| 481 | self.send_json({"error": {"message": "invalid api key"}}, 401) |
| 482 | return |
| 483 | length = int(self.headers.get("Content-Length", 0)) |
| 484 | body = self.rfile.read(length) if length else b"" |
nothing calls this directly
no outgoing calls
no test coverage detected