add-relay
#3
by glannuzel - opened
app.py
CHANGED
|
@@ -26,7 +26,7 @@ from dataclasses import dataclass, field
|
|
| 26 |
from typing import Any, Optional
|
| 27 |
|
| 28 |
from fastapi import Depends, FastAPI, Header, HTTPException, Request
|
| 29 |
-
from fastapi.responses import HTMLResponse
|
| 30 |
from huggingface_hub import attach_huggingface_oauth, parse_huggingface_oauth, whoami
|
| 31 |
from pydantic import BaseModel
|
| 32 |
|
|
@@ -224,6 +224,48 @@ async def dispatch(req: DispatchReq, request: Request) -> dict[str, Any]:
|
|
| 224 |
return {"status": "queued", "command_id": cmd.id}
|
| 225 |
|
| 226 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
# === UI ======================================================================
|
| 228 |
@app.get("/")
|
| 229 |
async def index() -> HTMLResponse:
|
|
|
|
| 26 |
from typing import Any, Optional
|
| 27 |
|
| 28 |
from fastapi import Depends, FastAPI, Header, HTTPException, Request
|
| 29 |
+
from fastapi.responses import HTMLResponse, RedirectResponse
|
| 30 |
from huggingface_hub import attach_huggingface_oauth, parse_huggingface_oauth, whoami
|
| 31 |
from pydantic import BaseModel
|
| 32 |
|
|
|
|
| 224 |
return {"status": "queued", "command_id": cmd.id}
|
| 225 |
|
| 226 |
|
| 227 |
+
# === OAuth relay for grabette devices ========================================
|
| 228 |
+
@app.get("/oauth/grabette/callback")
|
| 229 |
+
async def grabette_oauth_relay(
|
| 230 |
+
code: str | None = None,
|
| 231 |
+
state: str | None = None,
|
| 232 |
+
error: str | None = None,
|
| 233 |
+
error_description: str | None = None,
|
| 234 |
+
) -> RedirectResponse | HTMLResponse:
|
| 235 |
+
"""Relay the HF OAuth callback to the originating grabette on the local network.
|
| 236 |
+
|
| 237 |
+
The grabette encodes its mDNS hostname into the OAuth state as
|
| 238 |
+
``{hostname}|{session_id}``. This endpoint splits that apart and issues a
|
| 239 |
+
302 redirect so the user's browser (on the same LAN as the grabette) reaches
|
| 240 |
+
``http://{hostname}.local:8000/api/hf-auth/oauth/callback``.
|
| 241 |
+
|
| 242 |
+
Only this Space URL needs to be registered as a redirect_uri in the HF
|
| 243 |
+
OAuth app — one entry covers every grabette regardless of hostname.
|
| 244 |
+
"""
|
| 245 |
+
if not state or "|" not in state:
|
| 246 |
+
return HTMLResponse("Missing or invalid state parameter.", status_code=400)
|
| 247 |
+
|
| 248 |
+
hostname, session_id = state.split("|", 1)
|
| 249 |
+
from urllib.parse import urlencode
|
| 250 |
+
base = f"http://{hostname}.local:8000/api/hf-auth/oauth/callback"
|
| 251 |
+
|
| 252 |
+
if error:
|
| 253 |
+
# Forward the error to the grabette so it can mark the session as failed
|
| 254 |
+
# and stop the polling loop on the frontend.
|
| 255 |
+
params: dict = {"error": error, "state": session_id}
|
| 256 |
+
if error_description:
|
| 257 |
+
params["error_description"] = error_description
|
| 258 |
+
return RedirectResponse(f"{base}?{urlencode(params)}", status_code=302)
|
| 259 |
+
|
| 260 |
+
if not code:
|
| 261 |
+
return HTMLResponse("Missing code.", status_code=400)
|
| 262 |
+
|
| 263 |
+
return RedirectResponse(
|
| 264 |
+
f"{base}?{urlencode({'code': code, 'state': session_id})}",
|
| 265 |
+
status_code=302,
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
|
| 269 |
# === UI ======================================================================
|
| 270 |
@app.get("/")
|
| 271 |
async def index() -> HTMLResponse:
|