Compare commits
1 Commits
ac2753146e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2dd17a02c |
@@ -2,6 +2,7 @@
|
||||
OPENLANE_HEADLESS=true
|
||||
OPENLANE_LOG_LEVEL=INFO
|
||||
OPENLANE_TIMEOUT_MS=45000
|
||||
OPENLANE_RUNTIME_CONFIG_FILE=runtime_config.json
|
||||
|
||||
# --- OpenLane Auth & API ---
|
||||
OPENLANE_USERNAME=
|
||||
|
||||
@@ -181,7 +181,7 @@ class Settings:
|
||||
log_level: str = _env_str("OPENLANE_LOG_LEVEL", "INFO")
|
||||
log_file: str | None = _env_optional_str("OPENLANE_LOG_FILE")
|
||||
enable_trace_id_logs: bool = _env_bool("OPENLANE_ENABLE_TRACE_ID_LOGS", True)
|
||||
runtime_config_file: str | None = _env_path_str("OPENLANE_RUNTIME_CONFIG_FILE")
|
||||
runtime_config_file: str | None = _env_path_str("OPENLANE_RUNTIME_CONFIG_FILE") or "runtime_config.json"
|
||||
fingerprint: FingerprintConfig = field(default_factory=FingerprintConfig)
|
||||
openlane: OpenLaneConfig = field(default_factory=OpenLaneConfig)
|
||||
database: DatabaseConfig = field(default_factory=DatabaseConfig)
|
||||
|
||||
@@ -321,7 +321,9 @@ class OpenLaneAuthenticator:
|
||||
"https://app.openlane.com/api/_next/time",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
return page
|
||||
if self._is_api_authenticated(page):
|
||||
return page
|
||||
logger.warning("Access token cookie exists but API returned unauthorized, will re-authenticate")
|
||||
|
||||
# Пробуем прямой refresh access_token без навигации.
|
||||
refresh_token = self._extract_refresh_token_from_cookies(context) or self.config.refresh_token
|
||||
@@ -337,8 +339,10 @@ class OpenLaneAuthenticator:
|
||||
"https://app.openlane.com/api/_next/time",
|
||||
wait_until="domcontentloaded",
|
||||
)
|
||||
logger.info("OpenLane session restored via direct Okta refresh")
|
||||
return page
|
||||
if self._is_api_authenticated(page):
|
||||
logger.info("OpenLane session restored via direct Okta refresh")
|
||||
return page
|
||||
logger.warning("Direct Okta refresh produced token but API still unauthorized")
|
||||
logger.warning("Direct refresh failed, trying sign_in fallback")
|
||||
|
||||
# Если refresh_token был только в env, добавляем cookie вручную.
|
||||
@@ -355,7 +359,9 @@ class OpenLaneAuthenticator:
|
||||
self._wait_for_authenticated_session(page)
|
||||
self._persist_storage_state(context)
|
||||
self._maybe_rotate_token(context)
|
||||
return page
|
||||
if self._is_api_authenticated(page):
|
||||
return page
|
||||
logger.warning("sign_in fallback completed but API is still unauthorized")
|
||||
except OpenLaneAuthError:
|
||||
logger.warning("SPA refresh_token exchange failed")
|
||||
page.close()
|
||||
@@ -370,6 +376,43 @@ class OpenLaneAuthenticator:
|
||||
" 2. Set OPENLANE_USERNAME and OPENLANE_PASSWORD in .env"
|
||||
)
|
||||
|
||||
def _is_api_authenticated(self, page: Page) -> bool:
|
||||
"""Проверяет, что сессия реально авторизована для OpenLane API.
|
||||
|
||||
Нужна для случаев, когда access_token cookie есть (и даже не истёк),
|
||||
но сервер уже считает сессию невалидной.
|
||||
"""
|
||||
try:
|
||||
status = page.evaluate(
|
||||
"""async () => {
|
||||
try {
|
||||
const resp = await fetch('https://app.openlane.com/api/v4/search?page=1&per_page=1&source_tab=marketplace&sale_types=marketplace', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json, application/vnd.backlotcars.v3'
|
||||
}
|
||||
});
|
||||
return resp.status;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}"""
|
||||
)
|
||||
except Exception:
|
||||
logger.warning("Failed to validate OpenLane API auth state", exc_info=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
code = int(status)
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
|
||||
# 401/403 — точно неавторизованы.
|
||||
if code in (401, 403, 0):
|
||||
return False
|
||||
return True
|
||||
|
||||
def login(self, context: BrowserContext) -> Page:
|
||||
self.validate_credentials()
|
||||
page = context.new_page()
|
||||
|
||||
Reference in New Issue
Block a user