feat: auth capture — remote browser credential extraction
- BrowserSessionService: add create_ephemeral() for temp sessions
- New auth_capture_service.py: extract cookies, localStorage, sessionStorage from page
- New auth_capture router: POST /sessions, GET /sessions/{id}/extract, DELETE /sessions/{id}
- Frontend AuthCaptureDialog: URL input → browser view → extract → pick candidate
- Upstreams.vue: '提取' button next to Bearer Token field
- No sensitive values logged
This commit is contained in:
@@ -302,12 +302,15 @@ class BrowserSessionService:
|
||||
continue
|
||||
return None
|
||||
|
||||
def _get(self, session_id: str) -> BrowserSession:
|
||||
def get_session(self, session_id: str) -> BrowserSession:
|
||||
"""Retrieve a session by id — raises KeyError if missing."""
|
||||
session = self._sessions.get(session_id)
|
||||
if not session:
|
||||
raise KeyError("browser session not found")
|
||||
return session
|
||||
|
||||
_get = get_session # alias for internal use
|
||||
|
||||
def _ensure_open(self, session: BrowserSession) -> None:
|
||||
if session.page.is_closed():
|
||||
self._discard_session(session.id)
|
||||
@@ -332,5 +335,47 @@ class BrowserSessionService:
|
||||
safe_origin = re.sub(r"[^a-z0-9_.-]+", "_", origin).strip("_") or "page"
|
||||
return f"page-{custom_page_id}-{safe_origin[:80]}"
|
||||
|
||||
async def create_ephemeral(
|
||||
self,
|
||||
url: str,
|
||||
width: int = 1280,
|
||||
height: int = 720,
|
||||
) -> BrowserSession:
|
||||
"""Create a temporary browser session without a CustomPage record.
|
||||
|
||||
The session uses an isolated random-named profile so it never collides
|
||||
with persistent custom-page profiles. Caller MUST close() when done.
|
||||
"""
|
||||
if not url.startswith(("http://", "https://")):
|
||||
raise ValueError("Only http/https URLs are allowed")
|
||||
width = max(320, min(width, 2560))
|
||||
height = max(240, min(height, 1600))
|
||||
async with self._lock:
|
||||
await self._ensure_playwright()
|
||||
session_id = uuid4().hex
|
||||
profile_key = f"auth-capture-{session_id[:12]}"
|
||||
context = await self._playwright.chromium.launch_persistent_context(
|
||||
str(self._profile_dir(profile_key)),
|
||||
headless=get_settings().browser_headless,
|
||||
viewport={"width": width, "height": height},
|
||||
args=["--no-sandbox", "--disable-dev-shm-usage"],
|
||||
)
|
||||
page = context.pages[0] if context.pages else await context.new_page()
|
||||
session = BrowserSession(
|
||||
id=session_id,
|
||||
custom_page_id=0,
|
||||
profile_key=profile_key,
|
||||
context=context,
|
||||
page=page,
|
||||
lock=asyncio.Lock(),
|
||||
)
|
||||
self._sessions[session.id] = session
|
||||
try:
|
||||
await page.goto(url, wait_until="domcontentloaded", timeout=45000)
|
||||
except Exception:
|
||||
await self.close(session.id)
|
||||
raise
|
||||
return session
|
||||
|
||||
|
||||
browser_sessions = BrowserSessionService()
|
||||
|
||||
Reference in New Issue
Block a user