feat: remote browser login persistence + balance display + UI consistency
- Retain login state in remote browser profiles (don't delete on disconnect)
- Add GET /api/browser-sessions/{id}/clipboard for clipboard sync
- Add POST /api/browser-sessions/{id}/autofill-login for manual credential fill
- Add DELETE /api/browser-sessions/profiles/{custom_page_id} for login clear
- Add balance tracking with configurable divisor (balance_divisor)
- Health check on session reuse, idle TTL eviction, background cleanup
- Add first-frame watchdog (10s timeout) to prevent infinite loading
- Reconnect browser on active=true when session was closed
- UI: uniform text-only inline buttons (websites + upstreams pages)
- Fix page switch race with closingRemoteSessionPromise
This commit is contained in:
@@ -2,9 +2,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from typing import List
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -55,6 +58,11 @@ def _to_response(u: Upstream) -> UpstreamResponse:
|
||||
last_status=u.last_status,
|
||||
last_checked_at=u.last_checked_at,
|
||||
last_error=u.last_error,
|
||||
balance=u.balance,
|
||||
balance_updated_at=u.balance_updated_at,
|
||||
balance_endpoint=u.balance_endpoint or "",
|
||||
balance_response_path=u.balance_response_path or "",
|
||||
balance_divisor=u.balance_divisor or 1.0,
|
||||
created_at=u.created_at,
|
||||
updated_at=u.updated_at,
|
||||
)
|
||||
@@ -82,6 +90,9 @@ def create_upstream(
|
||||
enabled=body.enabled,
|
||||
check_interval_seconds=body.check_interval_seconds,
|
||||
timeout_seconds=body.timeout_seconds,
|
||||
balance_endpoint=body.balance_endpoint,
|
||||
balance_response_path=body.balance_response_path,
|
||||
balance_divisor=body.balance_divisor,
|
||||
)
|
||||
db.add(u)
|
||||
db.commit()
|
||||
@@ -156,6 +167,16 @@ def test_upstream(uid: int, db: Session = Depends(get_db), _=Depends(get_current
|
||||
try:
|
||||
client.login()
|
||||
groups = client.get_available_groups(u.groups_endpoint)
|
||||
# Also try balance if configured
|
||||
if u.balance_endpoint and u.balance_response_path:
|
||||
try:
|
||||
raw_balance = client.get_balance(u.balance_endpoint, u.balance_response_path)
|
||||
if raw_balance is not None:
|
||||
divisor = u.balance_divisor or 1.0
|
||||
u.balance = raw_balance / divisor
|
||||
u.balance_updated_at = datetime.now(timezone.utc) if raw_balance is not None else None
|
||||
except Exception as exc:
|
||||
logger.warning("upstream %s balance fetch failed during test: %s", u.name, exc)
|
||||
u.last_status = "healthy"
|
||||
u.last_error = None
|
||||
u.last_checked_at = datetime.now(timezone.utc)
|
||||
@@ -189,6 +210,16 @@ def check_now(uid: int, db: Session = Depends(get_db), _=Depends(get_current_use
|
||||
groups = client.get_available_groups(u.groups_endpoint)
|
||||
raw_rates = client.get_group_rates(u.rate_endpoint)
|
||||
snapshot = build_snapshot(u.id, u.base_url, u.api_prefix, groups, raw_rates)
|
||||
# Also try balance if configured
|
||||
if u.balance_endpoint and u.balance_response_path:
|
||||
try:
|
||||
raw_balance = client.get_balance(u.balance_endpoint, u.balance_response_path)
|
||||
if raw_balance is not None:
|
||||
divisor = u.balance_divisor or 1.0
|
||||
u.balance = raw_balance / divisor
|
||||
u.balance_updated_at = datetime.now(timezone.utc) if raw_balance is not None else None
|
||||
except Exception as exc:
|
||||
logger.warning("upstream %s balance fetch failed during check-now: %s", u.name, exc)
|
||||
except Exception as exc:
|
||||
u.consecutive_failures = (u.consecutive_failures or 0) + 1
|
||||
u.last_error = str(exc)
|
||||
|
||||
Reference in New Issue
Block a user