feat: one-click upstream auth refresh from custom page viewer
- Add linked_upstream_id to CustomPage model with DB migration
- New POST /api/custom-pages/{pid}/refresh-auth endpoint extracts
credentials from active remote browser and updates linked upstream
- PageViewer toolbar shows key icon button when page has linked upstream
- CustomPages form adds upstream dropdown for remote_browser pages
- Auth capture extracts New-Api-User from localStorage uid/user/self API
- Upstream client sends New-Api-User header in cookie auth mode
- Fix auth capture dialog: transparent background, field persistence,
login URL defaults to base_url/login, focus on click for keyboard input
- Fix upstream test ASCII encoding with non-header characters validation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -21,6 +21,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/auth-capture", tags=["auth-capture"])
|
||||
|
||||
SENSITIVE_CANDIDATE_FIELDS = frozenset({"value", "cookie_value"})
|
||||
|
||||
|
||||
class CaptureSessionCreate(BaseModel):
|
||||
url: str = Field(..., description="Target login page URL to open in browser")
|
||||
@@ -41,6 +43,14 @@ class CaptureExtractResponse(BaseModel):
|
||||
candidates: list[dict] = []
|
||||
|
||||
|
||||
def _sanitize_candidate(candidate: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
key: value
|
||||
for key, value in candidate.items()
|
||||
if key not in SENSITIVE_CANDIDATE_FIELDS
|
||||
}
|
||||
|
||||
|
||||
def _browser_error(exc: Exception) -> HTTPException:
|
||||
if isinstance(exc, BrowserDependencyError):
|
||||
return HTTPException(503, str(exc))
|
||||
@@ -108,8 +118,9 @@ async def extract_credentials(
|
||||
raise _browser_error(exc)
|
||||
|
||||
if not include_raw:
|
||||
# Strip raw data — only keep curated candidates
|
||||
return CaptureExtractResponse(candidates=result.get("candidates", []))
|
||||
# Strip raw data — only keep curated candidates with masked previews
|
||||
candidates = [_sanitize_candidate(candidate) for candidate in result.get("candidates", [])]
|
||||
return CaptureExtractResponse(candidates=candidates)
|
||||
return CaptureExtractResponse(**result)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user