diff --git a/src/app/interceptors/api-headers.interceptor.ts b/src/app/interceptors/api-headers.interceptor.ts index 6578617..a630faf 100644 --- a/src/app/interceptors/api-headers.interceptor.ts +++ b/src/app/interceptors/api-headers.interceptor.ts @@ -38,6 +38,21 @@ function getAnonymousSessionId(): string { return id; } +/** + * @marketplaces/auth's AuthService checks for a persisted session in its own + * constructor (a synchronous HTTP call to GET /users/sessions/:id before the + * constructor returns). If this interceptor injects AuthService for that + * exact call, Angular sees AuthService requesting itself mid-construction + * and throws NG0200 (circular dependency) - silently, since it's swallowed + * by TelegramSessionApiService's catchError(() => of(null)), which reads as + * "session invalid" and logs the user straight back out on every load. + * These endpoints are the identity mechanism itself (the session id is + * already the URL/body), so they never needed a WebSessionID header from an + * existing session in the first place - skipping AuthService injection here + * is correct, not a workaround. + */ +const AUTH_SESSION_PATH = '/users/sessions'; + export const apiHeadersInterceptor: HttpInterceptorFn = (req, next) => { const apiConfig = inject(ApiConfigService); if (!apiConfig.isApiRequest(req.url)) { @@ -46,12 +61,10 @@ export const apiHeadersInterceptor: HttpInterceptorFn = (req, next) => { const locationService = inject(LocationService); const languageService = inject(LanguageService); - const authService = inject(AuthService); const regionId = locationService.regionId(); const lang = languageService.currentLanguage(); const currency = languageService.currentCurrency(); - const session = authService.session(); let headers = req.headers; @@ -62,7 +75,12 @@ export const apiHeadersInterceptor: HttpInterceptorFn = (req, next) => { headers = headers.set('X-Language', LANG_HEADER_MAP[lang] ?? lang.toUpperCase()); } headers = headers.set('Currency', currency || 'RUB'); - headers = headers.set('WebSessionID', session?.sessionId || getAnonymousSessionId()); + + if (!req.url.includes(AUTH_SESSION_PATH)) { + const authService = inject(AuthService); + const session = authService.session(); + headers = headers.set('WebSessionID', session?.sessionId || getAnonymousSessionId()); + } return next(req.clone({ headers })); }; diff --git a/src/app/interceptors/mock-data.interceptor.ts b/src/app/interceptors/mock-data.interceptor.ts index 34b4d71..df503fa 100644 --- a/src/app/interceptors/mock-data.interceptor.ts +++ b/src/app/interceptors/mock-data.interceptor.ts @@ -741,10 +741,16 @@ export const mockDataInterceptor: HttpInterceptorFn = (req, next) => { const userSessionMatch = url.match(/\/users\/sessions\/([^/?]+)$/); if (userSessionMatch && req.method === 'GET') { const webSessionID = decodeURIComponent(userSessionMatch[1]); + // An id never seen via POST /users/sessions didn't start a fresh QR + // login in this session - it's a cookie carried over from an earlier + // visit (AuthService.checkSession's one-shot check on page load), which + // a real backend would already recognize. Only ids the mock itself put + // through the polling flow need the checks>=3 gate below. + const isReturningSession = !mockWebSessionChecks.has(webSessionID); const checks = (mockWebSessionChecks.get(webSessionID) ?? 0) + 1; mockWebSessionChecks.set(webSessionID, checks); - if (checks >= 3) { + if (isReturningSession || checks >= 3) { return respond({ webSessionID, status: true,