Spaces:
Sleeping
Sleeping
Update API authentication to use HF secrets
Browse files- gemini_web2api.py +26 -1
gemini_web2api.py
CHANGED
|
@@ -10,7 +10,7 @@ Usage:
|
|
| 10 |
|
| 11 |
Client configuration (Cherry Studio, ChatBox, etc.):
|
| 12 |
Base URL: http://localhost:8081/v1
|
| 13 |
-
API Key: (
|
| 14 |
"""
|
| 15 |
import json
|
| 16 |
import urllib.request
|
|
@@ -41,6 +41,7 @@ DEFAULT_CONFIG = {
|
|
| 41 |
"log_requests": True,
|
| 42 |
"cookie_file": None,
|
| 43 |
"proxy": None,
|
|
|
|
| 44 |
}
|
| 45 |
|
| 46 |
CONFIG = dict(DEFAULT_CONFIG)
|
|
@@ -298,6 +299,25 @@ class GeminiHandler(BaseHTTPRequestHandler):
|
|
| 298 |
def log_message(self, fmt, *args):
|
| 299 |
log(fmt % args)
|
| 300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
def send_json(self, data, status=200):
|
| 302 |
body = json.dumps(data, ensure_ascii=False).encode()
|
| 303 |
self.send_response(status)
|
|
@@ -317,6 +337,8 @@ class GeminiHandler(BaseHTTPRequestHandler):
|
|
| 317 |
def do_GET(self):
|
| 318 |
try:
|
| 319 |
if self.path == "/v1/models":
|
|
|
|
|
|
|
| 320 |
self.send_json({"object": "list", "data": [
|
| 321 |
{"id": n, "object": "model", "created": 1700000000,
|
| 322 |
"owned_by": "google", "description": c["desc"]}
|
|
@@ -334,6 +356,8 @@ class GeminiHandler(BaseHTTPRequestHandler):
|
|
| 334 |
|
| 335 |
def do_POST(self):
|
| 336 |
try:
|
|
|
|
|
|
|
| 337 |
length = int(self.headers.get("Content-Length", 0))
|
| 338 |
body = self.rfile.read(length) if length else b""
|
| 339 |
if self.path == "/v1/chat/completions":
|
|
@@ -558,6 +582,7 @@ def main():
|
|
| 558 |
print(f" Listening: http://0.0.0.0:{port}")
|
| 559 |
print(f" Base URL: http://localhost:{port}/v1")
|
| 560 |
print(f" Models: {', '.join(MODELS.keys())}")
|
|
|
|
| 561 |
print(f" Cookie: {'yes (' + CONFIG['cookie_file'] + ')' if CONFIG.get('cookie_file') else 'none (anonymous)'}")
|
| 562 |
print(f" Proxy: {CONFIG.get('proxy') or 'none (uses system env HTTP_PROXY/HTTPS_PROXY)'}")
|
| 563 |
print(f" Retry: {CONFIG['retry_attempts']}x / {CONFIG['retry_delay_sec']}s")
|
|
|
|
| 10 |
|
| 11 |
Client configuration (Cherry Studio, ChatBox, etc.):
|
| 12 |
Base URL: http://localhost:8081/v1
|
| 13 |
+
API Key: Set via API_KEY environment variable (Hugging Face Secrets)
|
| 14 |
"""
|
| 15 |
import json
|
| 16 |
import urllib.request
|
|
|
|
| 41 |
"log_requests": True,
|
| 42 |
"cookie_file": None,
|
| 43 |
"proxy": None,
|
| 44 |
+
"api_key": os.environ.get("API_KEY"), # Set via Hugging Face Secrets
|
| 45 |
}
|
| 46 |
|
| 47 |
CONFIG = dict(DEFAULT_CONFIG)
|
|
|
|
| 299 |
def log_message(self, fmt, *args):
|
| 300 |
log(fmt % args)
|
| 301 |
|
| 302 |
+
def validate_api_key(self) -> bool:
|
| 303 |
+
"""Validate the API key from Authorization header against configured key.
|
| 304 |
+
Returns True if valid or if no API key is configured (open access)."""
|
| 305 |
+
configured_key = CONFIG.get("api_key")
|
| 306 |
+
if not configured_key:
|
| 307 |
+
return True # No key configured = open access
|
| 308 |
+
auth_header = self.headers.get("Authorization", "")
|
| 309 |
+
if auth_header.startswith("Bearer "):
|
| 310 |
+
provided_key = auth_header[7:].strip()
|
| 311 |
+
else:
|
| 312 |
+
provided_key = auth_header.strip()
|
| 313 |
+
if provided_key == configured_key:
|
| 314 |
+
return True
|
| 315 |
+
log(f"API key rejected from {self.client_address[0]}")
|
| 316 |
+
self.send_json(
|
| 317 |
+
{"error": {"message": "Invalid API key. Provide a valid key via 'Authorization: Bearer <key>' header.",
|
| 318 |
+
"type": "authentication_error", "code": "invalid_api_key"}}, 401)
|
| 319 |
+
return False
|
| 320 |
+
|
| 321 |
def send_json(self, data, status=200):
|
| 322 |
body = json.dumps(data, ensure_ascii=False).encode()
|
| 323 |
self.send_response(status)
|
|
|
|
| 337 |
def do_GET(self):
|
| 338 |
try:
|
| 339 |
if self.path == "/v1/models":
|
| 340 |
+
if not self.validate_api_key():
|
| 341 |
+
return
|
| 342 |
self.send_json({"object": "list", "data": [
|
| 343 |
{"id": n, "object": "model", "created": 1700000000,
|
| 344 |
"owned_by": "google", "description": c["desc"]}
|
|
|
|
| 356 |
|
| 357 |
def do_POST(self):
|
| 358 |
try:
|
| 359 |
+
if not self.validate_api_key():
|
| 360 |
+
return
|
| 361 |
length = int(self.headers.get("Content-Length", 0))
|
| 362 |
body = self.rfile.read(length) if length else b""
|
| 363 |
if self.path == "/v1/chat/completions":
|
|
|
|
| 582 |
print(f" Listening: http://0.0.0.0:{port}")
|
| 583 |
print(f" Base URL: http://localhost:{port}/v1")
|
| 584 |
print(f" Models: {', '.join(MODELS.keys())}")
|
| 585 |
+
print(f" API Key: {'configured (set via API_KEY env)' if CONFIG.get('api_key') else 'none (open access)'}")
|
| 586 |
print(f" Cookie: {'yes (' + CONFIG['cookie_file'] + ')' if CONFIG.get('cookie_file') else 'none (anonymous)'}")
|
| 587 |
print(f" Proxy: {CONFIG.get('proxy') or 'none (uses system env HTTP_PROXY/HTTPS_PROXY)'}")
|
| 588 |
print(f" Retry: {CONFIG['retry_attempts']}x / {CONFIG['retry_delay_sec']}s")
|