@things-factory/integration-base 9.2.25 → 9.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,6 +43,14 @@ class HeadlessConnector {
43
43
  .filter(Boolean)
44
44
  }
45
45
  };
46
+ // 연결 시점의 인증 설정 상태 로깅 (자격증명 미해석으로 loginRequired=false가 되는 문제 진단용)
47
+ connection_manager_1.ConnectionManager.logger.info(`[HeadlessConnector] connect '${connection.name}' — loginRequired: ${loginInfo.loginRequired}, ` +
48
+ `loginPagePath: ${loginPagePath}, hasPassword: ${Boolean(password)}, ` +
49
+ `hasCachedCookies: ${Boolean(connection.cookies)}`);
50
+ if (!loginInfo.loginRequired) {
51
+ connection_manager_1.ConnectionManager.logger.warn(`[HeadlessConnector] connection '${connection.name}' has NO username — login will be SKIPPED entirely. ` +
52
+ `If this endpoint requires auth, check that credentials are resolved (getResolvedParameters).`);
53
+ }
46
54
  async function acquireBrowser() {
47
55
  try {
48
56
  const pool = (0, headless_pool_1.getHeadlessPool)();
@@ -147,13 +155,36 @@ class HeadlessConnector {
147
155
  connection_manager_1.ConnectionManager.logger.info('User is already logged in, skipping login process.');
148
156
  return page;
149
157
  }
158
+ // 캐시된 쿠키로 세션 복원 시도
159
+ // 주의: isCookieValid는 만료시간만 확인하므로, 서버측에서 무효화된(죽은) 쿠키도 통과함.
160
+ // 쿠키 적용 후 실제로 로그인 상태가 유지되는지(로그인 페이지로 튕기지 않는지) 반드시 검증하고,
161
+ // 실패하면 신규 로그인으로 폴백한다.
162
+ let sessionRestored = false;
150
163
  if (cookies && isCookieValid(cookies)) {
151
- await this.applyCookiesAndVerifySession(page, cookies, loginInfo);
152
- return page;
164
+ try {
165
+ await this.applyCookiesAndVerifySession(page, cookies, loginInfo);
166
+ const urlAfterCookie = page.url();
167
+ const loginPagePaths = [loginInfo.loginPagePath, '/login', '/signin', '/intro.do'].filter(Boolean);
168
+ if (loginPagePaths.some(path => urlAfterCookie.includes(path))) {
169
+ connection_manager_1.ConnectionManager.logger.warn(`[Session] Cached cookies rejected by server for connection '${connection.name}' — ` +
170
+ `landed on login page after reload (URL: ${urlAfterCookie}). Falling back to fresh login.`);
171
+ connection.cookies = null;
172
+ }
173
+ else {
174
+ connection_manager_1.ConnectionManager.logger.info(`[Session] Session restored from ${cookies.length} cached cookies for connection '${connection.name}' (URL: ${urlAfterCookie})`);
175
+ sessionRestored = true;
176
+ }
177
+ }
178
+ catch (cookieError) {
179
+ connection_manager_1.ConnectionManager.logger.warn(`[Session] Cookie session reuse failed for connection '${connection.name}': ${cookieError.message}. Falling back to fresh login.`);
180
+ connection.cookies = null;
181
+ }
182
+ }
183
+ if (!sessionRestored) {
184
+ await this.performLogin(page, uri, loginInfo);
185
+ cookies = await page.cookies();
186
+ connection.cookies = cookies;
153
187
  }
154
- await this.performLogin(page, uri, loginInfo);
155
- cookies = await page.cookies();
156
- connection.cookies = cookies;
157
188
  }
158
189
  else {
159
190
  // 로그인이 필요하지 않은 경우에도 기본 페이지로 이동
@@ -469,7 +500,13 @@ class HeadlessConnector {
469
500
  if (response) {
470
501
  const status = response.status();
471
502
  if (status >= 200 && status < 400) {
472
- connection_manager_1.ConnectionManager.logger.info(`Login successful with status code: ${status}`);
503
+ // 로그인 응답이 2xx/3xx여도 실제로는 실패 페이지(아이디/비밀번호 오류 등)로 떨어질 있으므로 최종 URL을 함께 기록
504
+ const postLoginUrl = page.url();
505
+ connection_manager_1.ConnectionManager.logger.info(`Login successful with status code: ${status} — post-login URL: ${postLoginUrl}`);
506
+ if (postLoginUrl.includes(loginInfo.loginPagePath)) {
507
+ connection_manager_1.ConnectionManager.logger.warn(`Login response was ${status} but page is still on login page (${postLoginUrl}) — ` +
508
+ `credentials may be wrong, or the site requires additional steps (captcha, keyboard security, OTP).`);
509
+ }
473
510
  return;
474
511
  }
475
512
  else if (status >= 400) {
@@ -487,15 +524,26 @@ class HeadlessConnector {
487
524
  }
488
525
  }
489
526
  catch (error) {
490
- connection_manager_1.ConnectionManager.logger.warn(`Login attempt ${attempt} failed:`, error);
527
+ // 실패 시점의 페이지 상태(URL/타이틀)를 함께 기록하여 셀렉터 불일치, 보안모듈, 리디렉션 등 원인 추적 가능하게 함
528
+ let failureContext = '';
529
+ try {
530
+ const failUrl = page.url();
531
+ const failTitle = await page.title().catch(() => 'unknown');
532
+ failureContext = ` (URL: ${failUrl}, title: ${failTitle})`;
533
+ }
534
+ catch (contextError) {
535
+ failureContext = ' (failed to capture page state)';
536
+ }
537
+ connection_manager_1.ConnectionManager.logger.warn(`Login attempt ${attempt}/${loginInfo.retries} failed${failureContext}:`, error);
491
538
  try {
492
539
  await page.screenshot({ path: `logs/login-failure-attempt-${attempt}.png` });
540
+ connection_manager_1.ConnectionManager.logger.info(`Login failure screenshot saved: logs/login-failure-attempt-${attempt}.png`);
493
541
  }
494
542
  catch (error) {
495
543
  connection_manager_1.ConnectionManager.logger.error('Failed to capture screenshot:', error);
496
544
  }
497
545
  if (attempt === loginInfo.retries) {
498
- throw new Error(`Login failed after ${loginInfo.retries} attempts`);
546
+ throw new Error(`Login failed after ${loginInfo.retries} attempts: ${error.message}${failureContext}`);
499
547
  }
500
548
  }
501
549
  }
@@ -1 +1 @@
1
- {"version":3,"file":"headless-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/headless-connector.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionManager } from '../connection-manager'\nimport { Connector } from '../types'\nimport { getHeadlessPool, getPoolStats } from '../resource-pool/headless-pool'\nimport { Browser, Page } from 'puppeteer'\n\n/*\n Functionality of the headless-connector:\n - Provides a mechanism to acquire an active session page.\n - Performs login when necessary to obtain valid cookies.\n - Applies these cookies to the page for session management.\n - Pages are acquired from the `headlessPool`, which manages browser instances.\n - During the login process, pages from the pool are used for actions like form filling and navigation.\n - Valid cookies are saved after login and reused for subsequent page acquisitions.\n - Users must explicitly release the page after use through the `releasePage` method.\n - Released pages are returned to the `headlessPool` for reuse.\n*/\n\nexport class HeadlessConnector implements Connector {\n async ready(connectionConfigs) {\n await Promise.all(connectionConfigs.map(this.connect.bind(this)))\n ConnectionManager.logger.info('headless-connector connections are ready')\n }\n\n async connect(connection) {\n const {\n endpoint: uri = '1',\n params: {\n username = '',\n password = '',\n loginPagePath = '/login',\n loginApiUrl = null,\n usernameSelector = '#username',\n passwordSelector = '#password',\n submitSelector = '#submit',\n successSelector = null,\n shadowDomSelectors = '', // Comma separated shadow DOM selectors\n timeout = 15000, // Default timeout for operations\n retries = 3 // Default number of retries for login or page actions\n } = {}\n } = connection\n\n const loginInfo = {\n loginRequired: Boolean(username), // Determine if login is required\n username,\n password,\n loginPagePath,\n loginApiUrl,\n timeout,\n retries,\n loginSelectors: {\n usernameSelector,\n passwordSelector,\n submitSelector,\n successSelector,\n shadowDomSelectors: shadowDomSelectors\n .split(',')\n .map(selector => selector.trim())\n .filter(Boolean)\n }\n }\n\n async function acquireBrowser() {\n try {\n const pool = getHeadlessPool()\n \n // Pool 상태 모니터링 (디버깅용)\n try {\n const stats = getPoolStats()\n ConnectionManager.logger.debug(`Pool stats before acquire - borrowed: ${stats.borrowed}, available: ${stats.available}, pending: ${stats.pending}`)\n } catch (statsError) {\n ConnectionManager.logger.warn('Failed to get pool stats:', statsError)\n }\n \n const browser = await pool.acquire()\n ConnectionManager.logger.info('Browser acquired successfully from pool')\n return browser\n } catch (error) {\n ConnectionManager.logger.error('Failed to acquire browser from pool:', error)\n \n // 풀 상태 로깅으로 디버깅 도움\n try {\n const stats = getPoolStats()\n ConnectionManager.logger.error(`Pool stats during acquire error - borrowed: ${stats.borrowed}, available: ${stats.available}, pending: ${stats.pending}, max: ${stats.max}`)\n } catch (statsError) {\n ConnectionManager.logger.error('Failed to get pool stats during error:', statsError)\n }\n \n throw error\n }\n }\n\n async function releaseBrowser(browser: Browser) {\n if (!browser) {\n ConnectionManager.logger.warn('Attempted to release null/undefined browser')\n return\n }\n\n try {\n // 브라우저가 아직 연결되어 있는지 확인\n if (!browser.isConnected()) {\n ConnectionManager.logger.warn('Attempted to release disconnected browser')\n return\n }\n\n // Pool 상태 모니터링 (릴리즈 전)\n try {\n const statsBefore = getPoolStats()\n ConnectionManager.logger.debug(`Pool stats before release - borrowed: ${statsBefore.borrowed}, available: ${statsBefore.available}`)\n } catch (statsError) {\n ConnectionManager.logger.warn('Failed to get pool stats before release:', statsError)\n }\n\n const pool = getHeadlessPool()\n await pool.release(browser)\n ConnectionManager.logger.info('Browser successfully released to pool')\n \n // Pool 상태 모니터링 (릴리즈 후)\n try {\n const statsAfter = getPoolStats()\n ConnectionManager.logger.debug(`Pool stats after release - borrowed: ${statsAfter.borrowed}, available: ${statsAfter.available}`)\n } catch (statsError) {\n ConnectionManager.logger.warn('Failed to get pool stats after release:', statsError)\n }\n } catch (error) {\n ConnectionManager.logger.error('Failed to release browser to pool:', error)\n \n // 에러 발생시 풀 상태 로깅\n try {\n const stats = getPoolStats()\n ConnectionManager.logger.error(`Pool stats during release error - borrowed: ${stats.borrowed}, available: ${stats.available}, pending: ${stats.pending}`)\n } catch (statsError) {\n ConnectionManager.logger.error('Failed to get pool stats during release error:', statsError)\n }\n \n // 풀 릴리즈가 실패한 경우 브라우저 강제 종료 시도\n try {\n if (browser && browser.isConnected()) {\n await browser.close()\n ConnectionManager.logger.warn('Forcibly closed browser due to pool release failure')\n }\n } catch (forceCloseError) {\n ConnectionManager.logger.error('Failed to force close browser after pool release failure:', forceCloseError)\n }\n }\n }\n\n ConnectionManager.addConnectionInstance(connection, {\n endpoint: connection.endpoint,\n params: connection.params,\n acquireSessionPage: async () => {\n const browser = await acquireBrowser()\n let page\n let cookies = connection.cookies\n\n try {\n page = await browser.newPage()\n await this.setupPage(page, uri, timeout)\n\n if (loginInfo.loginRequired) {\n // 먼저 기본 페이지로 이동\n await page.goto(uri, { waitUntil: 'networkidle2', timeout })\n\n // 현재 세션의 Authorization 헤더 확인\n const headers = await page.evaluate(() => {\n return fetch(window.location.href, { method: 'GET' })\n .then(response => response.headers.get('Authorization'))\n .catch(() => null)\n })\n\n if (headers) {\n ConnectionManager.logger.info('User is already logged in, skipping login process.')\n return page\n }\n\n if (cookies && isCookieValid(cookies)) {\n await this.applyCookiesAndVerifySession(page, cookies, loginInfo)\n return page\n }\n\n await this.performLogin(page, uri, loginInfo)\n cookies = await page.cookies()\n connection.cookies = cookies\n } else {\n // 로그인이 필요하지 않은 경우에도 기본 페이지로 이동\n await page.goto(uri, { waitUntil: 'networkidle2', timeout })\n }\n\n return page\n } catch (error) {\n ConnectionManager.logger.error('Failed to acquire session page:', error)\n \n // CRITICAL: 에러 발생 시 확실한 리소스 정리\n if (page) {\n try {\n await page.close()\n ConnectionManager.logger.info('Page closed during acquireSessionPage error handling')\n } catch (closeError) {\n ConnectionManager.logger.error('Failed to close page during acquireSessionPage error:', closeError)\n }\n }\n \n try {\n await releaseBrowser(browser)\n ConnectionManager.logger.info('Browser released during acquireSessionPage error handling')\n } catch (releaseError) {\n ConnectionManager.logger.error('Failed to release browser during acquireSessionPage error:', releaseError)\n }\n \n throw error\n }\n },\n releasePage: async (page: Page) => {\n try {\n if (page && !page.isClosed()) {\n const browser = page.browser()\n \n // 페이지를 먼저 닫기\n try {\n await page.close()\n ConnectionManager.logger.info('Page closed successfully')\n } catch (closeError) {\n ConnectionManager.logger.error('Failed to close page:', closeError)\n }\n \n // 브라우저를 풀에 반환\n try {\n await releaseBrowser(browser)\n ConnectionManager.logger.info('Browser released successfully')\n } catch (releaseError) {\n ConnectionManager.logger.error('Failed to release browser:', releaseError)\n // 풀 릴리즈가 실패한 경우 브라우저 강제 종료 시도\n try {\n if (browser && browser.isConnected()) {\n await browser.close()\n ConnectionManager.logger.warn('Forcibly closed browser due to pool release failure')\n }\n } catch (forceCloseError) {\n ConnectionManager.logger.error('Failed to force close browser:', forceCloseError)\n }\n }\n }\n } catch (error) {\n ConnectionManager.logger.error('Critical error in releasePage:', error)\n }\n },\n // 세션 회복을 위한 재로그인 메서드\n reAuthenticateSession: async () => {\n ConnectionManager.logger.info(`Re-authenticating session for connection: ${connection.name}`)\n \n let browser = null\n let page = null\n\n try {\n browser = await acquireBrowser()\n page = await browser.newPage()\n await this.setupPage(page, uri, loginInfo.timeout)\n \n if (loginInfo.loginRequired) {\n await this.performLogin(page, uri, loginInfo)\n const newCookies = await page.cookies()\n connection.cookies = newCookies\n ConnectionManager.logger.info(`Session re-authenticated successfully for connection: ${connection.name}`)\n }\n \n // CRITICAL: 페이지와 함께 브라우저 정보도 반환하여 정확한 릴리즈 추적\n return {\n page,\n browser,\n requiresManualRelease: true // 수동 릴리즈 필요 표시\n }\n } catch (error) {\n ConnectionManager.logger.error(`Failed to re-authenticate session for connection: ${connection.name}`, error)\n \n // 에러 시 확실한 리소스 정리\n if (page) {\n try {\n if (!page.isClosed()) {\n await page.close()\n ConnectionManager.logger.info('Page closed during reauth error handling')\n }\n } catch (closeError) {\n ConnectionManager.logger.error('Failed to close page during reauth error:', closeError)\n }\n }\n \n if (browser) {\n try {\n await releaseBrowser(browser)\n ConnectionManager.logger.info('Browser released during reauth error handling')\n } catch (releaseError) {\n ConnectionManager.logger.error('Failed to release browser during reauth error:', releaseError)\n // 풀 릴리즈가 실패한 경우 브라우저 강제 종료 시도\n try {\n if (browser.isConnected()) {\n await browser.close()\n ConnectionManager.logger.warn('Forcibly closed browser during reauth error due to pool release failure')\n }\n } catch (forceCloseError) {\n ConnectionManager.logger.error('Failed to force close browser during reauth error:', forceCloseError)\n }\n }\n }\n \n throw error\n }\n },\n // 세션 유효성 검사\n validateSession: async (page: Page) => {\n try {\n if (!loginInfo.loginRequired) {\n return true\n }\n\n // 페이지가 닫혔거나 연결이 끊어진 경우\n if (!page || page.isClosed()) {\n ConnectionManager.logger.warn(`Page is closed or null for connection: ${connection.name}`)\n return false\n }\n\n // URL 기반 로그인 리디렉션 체크\n let currentUrl: string\n try {\n currentUrl = page.url()\n } catch (urlError) {\n ConnectionManager.logger.warn(`Cannot get page URL for connection: ${connection.name}`, urlError)\n return false\n }\n \n if (currentUrl.includes(loginInfo.loginPagePath) || \n currentUrl.includes('/login') || \n currentUrl.includes('/signin') || \n currentUrl.includes('/auth')) {\n ConnectionManager.logger.info(`Redirected to login page: ${currentUrl}`)\n return false\n }\n\n // 로그인 폼 요소 기반 체크\n const needsLogin = await page.evaluate(() => {\n try {\n // 로그인 페이지 특정 요소들이 있는지 확인\n const loginIndicators = ['#username', '#password', '.login-form', '[type=\"password\"]']\n return loginIndicators.some(selector => {\n const element = document.querySelector(selector) as HTMLElement\n return element && element.offsetParent !== null // 보이는 요소만 체크\n })\n } catch (evalError) {\n return true // 에러 시 안전하게 재로그인 필요로 간주\n }\n })\n\n // 추가 세션 검증: 간단한 API 호출로 인증 상태 확인\n try {\n const testResponse = await page.evaluate(async () => {\n try {\n const response = await fetch(window.location.origin, { \n method: 'HEAD',\n credentials: 'include'\n })\n return {\n status: response.status,\n ok: response.ok\n }\n } catch (fetchError) {\n return { status: 0, ok: false }\n }\n })\n \n // 인증이 필요한 엔드포인트에서 401/403 반환시 세션 무효\n if (testResponse.status === 401 || testResponse.status === 403) {\n ConnectionManager.logger.info(`Authentication failed with status ${testResponse.status} for connection: ${connection.name}`)\n return false\n }\n } catch (apiTestError) {\n ConnectionManager.logger.warn(`Session API test failed for connection: ${connection.name}`, apiTestError)\n // API 테스트 실패는 세션 무효로 간주하지 않음 (네트워크 문제일 수 있음)\n }\n\n return !needsLogin\n } catch (error) {\n ConnectionManager.logger.warn(`Session validation failed for connection: ${connection.name}`, error)\n return false\n }\n },\n acquireBrowser,\n releaseBrowser\n })\n\n ConnectionManager.logger.info(\n `headless-connector connection(${connection.name}:${connection.endpoint}) is connected`\n )\n }\n\n async setupPage(page, uri, timeout) {\n await page.setRequestInterception(true)\n\n await page.setBypassCSP(true)\n\n await page.setUserAgent(\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.204 Safari/537.36'\n )\n\n await page.evaluateOnNewDocument(() => {\n Object.defineProperty(navigator, 'webdriver', { get: () => false })\n })\n\n await page.setExtraHTTPHeaders({\n 'Upgrade-Insecure-Requests': '0',\n 'accept-language': 'en-US,en;q=0.9',\n referer: uri\n })\n\n await page.setDefaultNavigationTimeout(timeout)\n\n page.on('console', async msg => {\n ConnectionManager.logger.info(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('request', request => {\n if (request.isInterceptResolutionHandled()) {\n return\n }\n\n const resourceType = request.resourceType()\n\n // fetch(), XMLHttpRequest 등의 API 호출은 항상 허용\n if (resourceType === 'fetch' || resourceType === 'xhr') {\n request.continue()\n return\n }\n\n const headers = request.headers()\n if (headers['access-control-request-headers']) {\n const filteredHeaders = headers['access-control-request-headers']\n .split(',')\n .map(header => header.trim())\n .filter(header => header.toLowerCase() !== 'upgrade-insecure-requests')\n\n if (filteredHeaders.length > 0) {\n headers['access-control-request-headers'] = filteredHeaders.join(', ')\n } else {\n delete headers['access-control-request-headers']\n }\n }\n\n if (headers['upgrade-insecure-requests']) {\n delete headers['upgrade-insecure-requests']\n }\n\n // 이미지, 스타일시트, 폰트는 차단하되 API 요청은 허용\n if (['image', 'stylesheet', 'font'].includes(resourceType)) {\n request.abort()\n } else {\n request.continue()\n }\n })\n\n page.on('requestfailed', request => {\n try {\n console.log('Request failed:')\n console.log(`- URL: ${request.url()}`)\n console.log(`- Method: ${request.method()}`)\n console.log(`- Failure Text: ${request.failure()?.errorText}`)\n console.log(`- Headers:`, request.headers())\n\n if (request.postData()) {\n console.log(`- Post Data: ${request.postData()}`)\n }\n } catch (error) {\n console.error('Error in requestfailed handler:', error)\n }\n })\n }\n\n async applyCookiesAndVerifySession(page, cookies, loginInfo) {\n await page.setCookie(...cookies)\n await page.reload({ waitUntil: 'networkidle2', timeout: loginInfo.timeout })\n\n if (loginInfo.loginRequired && loginInfo.loginSelectors.successSelector) {\n const success = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.successSelector\n )\n if (!success) {\n throw new Error('Session invalid, login required')\n }\n }\n }\n\n async performLogin(page, uri, loginInfo) {\n for (let attempt = 1; attempt <= loginInfo.retries; attempt++) {\n try {\n await page.goto(`${uri}${loginInfo.loginPagePath}`, { waitUntil: 'networkidle2', timeout: loginInfo.timeout })\n\n const usernameInput = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.usernameSelector\n )\n const passwordInput = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.passwordSelector\n )\n const submitButton = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.submitSelector\n )\n\n if (!usernameInput || !passwordInput || !submitButton) {\n throw new Error('Failed to locate input elements in shadow DOM')\n }\n\n await usernameInput.type(loginInfo.username)\n await passwordInput.type(loginInfo.password)\n\n var loginApiUrl =\n loginInfo.loginApiUrl ||\n (await page.evaluate(button => {\n const form = button.closest('form')\n return form ? form.action : null\n }, submitButton))\n\n if (loginApiUrl && !loginApiUrl.startsWith('http')) {\n loginApiUrl = `${uri}${loginApiUrl}`\n }\n\n if (!loginApiUrl) {\n throw new Error('❌ Unable to detect login API URL!')\n }\n\n console.log('✅ Detected login API URL:', loginApiUrl)\n\n const [response] = await Promise.all([\n page.waitForResponse(response => {\n const url = response.url()\n const method = response.request().method()\n\n return url === loginApiUrl && method === 'POST' && [200, 201, 204, 302, 304].includes(response.status())\n }),\n page.evaluate(button => button.click(), submitButton),\n page.waitForNavigation({ waitUntil: 'networkidle2', timeout: loginInfo.timeout }) // 로그인 후 리디렉션 감지\n ])\n\n if (response) {\n const status = response.status()\n if (status >= 200 && status < 400) {\n ConnectionManager.logger.info(`Login successful with status code: ${status}`)\n return\n } else if (status >= 400) {\n throw new Error(`Login failed with status code: ${status}`)\n }\n } else {\n throw new Error('No response received during login')\n }\n\n if (loginInfo.loginSelectors.successSelector) {\n const success = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.successSelector\n )\n if (!success) {\n throw new Error('Login failed: Success selector not found')\n }\n }\n } catch (error) {\n ConnectionManager.logger.warn(`Login attempt ${attempt} failed:`, error)\n\n try {\n await page.screenshot({ path: `logs/login-failure-attempt-${attempt}.png` })\n } catch (error) {\n ConnectionManager.logger.error('Failed to capture screenshot:', error)\n }\n\n if (attempt === loginInfo.retries) {\n throw new Error(`Login failed after ${loginInfo.retries} attempts`)\n }\n }\n }\n }\n\n async resolveShadowDom(page, shadowSelectors, targetSelector) {\n let context\n\n if (!shadowSelectors || shadowSelectors.length === 0) {\n // No Shadow DOM path; use document root as the context\n context = page.mainFrame() // Puppeteer uses frames to represent document\n return context.$(targetSelector) // Search directly in the document root\n }\n\n context = page // Start with the page as the context\n for (const selector of shadowSelectors) {\n const shadowHost = await context.$(selector)\n if (!shadowHost) {\n throw new Error(`Shadow host not found: ${selector}`)\n }\n context = await page.evaluateHandle(host => host.shadowRoot, shadowHost)\n }\n return context.evaluateHandle((shadowRoot, selector) => shadowRoot.querySelector(selector), targetSelector)\n }\n\n async disconnect(connection) {\n try {\n // 연결 해제 전 리소스 정리 로그\n ConnectionManager.logger.info(`Starting disconnect process for connection: ${connection.name}`)\n \n // 풀 상태 확인 (정보성 로깅)\n try {\n const pool = getHeadlessPool()\n ConnectionManager.logger.info(`Pool status before disconnect - available resources exist: ${!!pool}`)\n } catch (poolError) {\n ConnectionManager.logger.warn(`Could not access pool during disconnect: ${poolError.message}`)\n }\n\n ConnectionManager.removeConnectionInstance(connection)\n ConnectionManager.logger.info(`headless-connector connection(${connection.name}) is disconnected`)\n } catch (error) {\n ConnectionManager.logger.error(`Error disconnecting headless-connector connection(${connection.name}):`, error)\n // Still try to remove the connection instance even if cleanup failed\n try {\n ConnectionManager.removeConnectionInstance(connection)\n } catch (removeError) {\n ConnectionManager.logger.error(`Failed to remove connection instance ${connection.name}:`, removeError)\n }\n }\n }\n\n get parameterSpec() {\n return [\n {\n type: 'string',\n name: 'username',\n label: 'username',\n useDomainAttribute: true\n },\n {\n type: 'secret',\n name: 'password',\n label: 'password',\n useDomainAttribute: true\n },\n {\n type: 'string',\n name: 'loginPagePath',\n label: 'login-page-path'\n },\n {\n type: 'string',\n name: 'loginApiUrl',\n label: 'login-api-url'\n },\n {\n type: 'string',\n name: 'usernameSelector',\n label: 'username-selector'\n },\n {\n type: 'string',\n name: 'passwordSelector',\n label: 'password-selector'\n },\n {\n type: 'string',\n name: 'submitSelector',\n label: 'submit-selector'\n },\n {\n type: 'string',\n name: 'successSelector',\n label: 'success-selector'\n },\n {\n type: 'string',\n name: 'shadowDomSelectors',\n label: 'shadow-dom-selectors'\n },\n {\n type: 'number',\n name: 'timeout',\n label: 'timeout',\n value: 15000\n },\n {\n type: 'number',\n name: 'retries',\n label: 'maximum-retries',\n value: 3\n }\n ]\n }\n\n get taskPrefixes() {\n return ['headless']\n }\n\n get description() {\n return 'Headless Pool Connector with login capabilities'\n }\n\n get help() {\n return 'integration/connector/headless-connector'\n }\n}\n\nConnectionManager.registerConnector('headless-connector', new HeadlessConnector())\n\nfunction isCookieValid(cookies: any[]): boolean {\n if (!cookies || cookies.length === 0) return false\n const now = Date.now() / 1000 // Current time in seconds\n return cookies.some((cookie: any) => cookie.expires && cookie.expires > now)\n}\n"]}
1
+ {"version":3,"file":"headless-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/headless-connector.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionManager } from '../connection-manager'\nimport { Connector } from '../types'\nimport { getHeadlessPool, getPoolStats } from '../resource-pool/headless-pool'\nimport { Browser, Page } from 'puppeteer'\n\n/*\n Functionality of the headless-connector:\n - Provides a mechanism to acquire an active session page.\n - Performs login when necessary to obtain valid cookies.\n - Applies these cookies to the page for session management.\n - Pages are acquired from the `headlessPool`, which manages browser instances.\n - During the login process, pages from the pool are used for actions like form filling and navigation.\n - Valid cookies are saved after login and reused for subsequent page acquisitions.\n - Users must explicitly release the page after use through the `releasePage` method.\n - Released pages are returned to the `headlessPool` for reuse.\n*/\n\nexport class HeadlessConnector implements Connector {\n async ready(connectionConfigs) {\n await Promise.all(connectionConfigs.map(this.connect.bind(this)))\n ConnectionManager.logger.info('headless-connector connections are ready')\n }\n\n async connect(connection) {\n const {\n endpoint: uri = '1',\n params: {\n username = '',\n password = '',\n loginPagePath = '/login',\n loginApiUrl = null,\n usernameSelector = '#username',\n passwordSelector = '#password',\n submitSelector = '#submit',\n successSelector = null,\n shadowDomSelectors = '', // Comma separated shadow DOM selectors\n timeout = 15000, // Default timeout for operations\n retries = 3 // Default number of retries for login or page actions\n } = {}\n } = connection\n\n const loginInfo = {\n loginRequired: Boolean(username), // Determine if login is required\n username,\n password,\n loginPagePath,\n loginApiUrl,\n timeout,\n retries,\n loginSelectors: {\n usernameSelector,\n passwordSelector,\n submitSelector,\n successSelector,\n shadowDomSelectors: shadowDomSelectors\n .split(',')\n .map(selector => selector.trim())\n .filter(Boolean)\n }\n }\n\n // 연결 시점의 인증 설정 상태 로깅 (자격증명 미해석으로 loginRequired=false가 되는 문제 진단용)\n ConnectionManager.logger.info(\n `[HeadlessConnector] connect '${connection.name}' — loginRequired: ${loginInfo.loginRequired}, ` +\n `loginPagePath: ${loginPagePath}, hasPassword: ${Boolean(password)}, ` +\n `hasCachedCookies: ${Boolean(connection.cookies)}`\n )\n if (!loginInfo.loginRequired) {\n ConnectionManager.logger.warn(\n `[HeadlessConnector] connection '${connection.name}' has NO username — login will be SKIPPED entirely. ` +\n `If this endpoint requires auth, check that credentials are resolved (getResolvedParameters).`\n )\n }\n\n async function acquireBrowser() {\n try {\n const pool = getHeadlessPool()\n \n // Pool 상태 모니터링 (디버깅용)\n try {\n const stats = getPoolStats()\n ConnectionManager.logger.debug(`Pool stats before acquire - borrowed: ${stats.borrowed}, available: ${stats.available}, pending: ${stats.pending}`)\n } catch (statsError) {\n ConnectionManager.logger.warn('Failed to get pool stats:', statsError)\n }\n \n const browser = await pool.acquire()\n ConnectionManager.logger.info('Browser acquired successfully from pool')\n return browser\n } catch (error) {\n ConnectionManager.logger.error('Failed to acquire browser from pool:', error)\n \n // 풀 상태 로깅으로 디버깅 도움\n try {\n const stats = getPoolStats()\n ConnectionManager.logger.error(`Pool stats during acquire error - borrowed: ${stats.borrowed}, available: ${stats.available}, pending: ${stats.pending}, max: ${stats.max}`)\n } catch (statsError) {\n ConnectionManager.logger.error('Failed to get pool stats during error:', statsError)\n }\n \n throw error\n }\n }\n\n async function releaseBrowser(browser: Browser) {\n if (!browser) {\n ConnectionManager.logger.warn('Attempted to release null/undefined browser')\n return\n }\n\n try {\n // 브라우저가 아직 연결되어 있는지 확인\n if (!browser.isConnected()) {\n ConnectionManager.logger.warn('Attempted to release disconnected browser')\n return\n }\n\n // Pool 상태 모니터링 (릴리즈 전)\n try {\n const statsBefore = getPoolStats()\n ConnectionManager.logger.debug(`Pool stats before release - borrowed: ${statsBefore.borrowed}, available: ${statsBefore.available}`)\n } catch (statsError) {\n ConnectionManager.logger.warn('Failed to get pool stats before release:', statsError)\n }\n\n const pool = getHeadlessPool()\n await pool.release(browser)\n ConnectionManager.logger.info('Browser successfully released to pool')\n \n // Pool 상태 모니터링 (릴리즈 후)\n try {\n const statsAfter = getPoolStats()\n ConnectionManager.logger.debug(`Pool stats after release - borrowed: ${statsAfter.borrowed}, available: ${statsAfter.available}`)\n } catch (statsError) {\n ConnectionManager.logger.warn('Failed to get pool stats after release:', statsError)\n }\n } catch (error) {\n ConnectionManager.logger.error('Failed to release browser to pool:', error)\n \n // 에러 발생시 풀 상태 로깅\n try {\n const stats = getPoolStats()\n ConnectionManager.logger.error(`Pool stats during release error - borrowed: ${stats.borrowed}, available: ${stats.available}, pending: ${stats.pending}`)\n } catch (statsError) {\n ConnectionManager.logger.error('Failed to get pool stats during release error:', statsError)\n }\n \n // 풀 릴리즈가 실패한 경우 브라우저 강제 종료 시도\n try {\n if (browser && browser.isConnected()) {\n await browser.close()\n ConnectionManager.logger.warn('Forcibly closed browser due to pool release failure')\n }\n } catch (forceCloseError) {\n ConnectionManager.logger.error('Failed to force close browser after pool release failure:', forceCloseError)\n }\n }\n }\n\n ConnectionManager.addConnectionInstance(connection, {\n endpoint: connection.endpoint,\n params: connection.params,\n acquireSessionPage: async () => {\n const browser = await acquireBrowser()\n let page\n let cookies = connection.cookies\n\n try {\n page = await browser.newPage()\n await this.setupPage(page, uri, timeout)\n\n if (loginInfo.loginRequired) {\n // 먼저 기본 페이지로 이동\n await page.goto(uri, { waitUntil: 'networkidle2', timeout })\n\n // 현재 세션의 Authorization 헤더 확인\n const headers = await page.evaluate(() => {\n return fetch(window.location.href, { method: 'GET' })\n .then(response => response.headers.get('Authorization'))\n .catch(() => null)\n })\n\n if (headers) {\n ConnectionManager.logger.info('User is already logged in, skipping login process.')\n return page\n }\n\n // 캐시된 쿠키로 세션 복원 시도\n // 주의: isCookieValid는 만료시간만 확인하므로, 서버측에서 무효화된(죽은) 쿠키도 통과함.\n // 쿠키 적용 후 실제로 로그인 상태가 유지되는지(로그인 페이지로 튕기지 않는지) 반드시 검증하고,\n // 실패하면 신규 로그인으로 폴백한다.\n let sessionRestored = false\n if (cookies && isCookieValid(cookies)) {\n try {\n await this.applyCookiesAndVerifySession(page, cookies, loginInfo)\n\n const urlAfterCookie = page.url()\n const loginPagePaths = [loginInfo.loginPagePath, '/login', '/signin', '/intro.do'].filter(Boolean)\n if (loginPagePaths.some(path => urlAfterCookie.includes(path))) {\n ConnectionManager.logger.warn(\n `[Session] Cached cookies rejected by server for connection '${connection.name}' — ` +\n `landed on login page after reload (URL: ${urlAfterCookie}). Falling back to fresh login.`\n )\n connection.cookies = null\n } else {\n ConnectionManager.logger.info(\n `[Session] Session restored from ${cookies.length} cached cookies for connection '${connection.name}' (URL: ${urlAfterCookie})`\n )\n sessionRestored = true\n }\n } catch (cookieError) {\n ConnectionManager.logger.warn(\n `[Session] Cookie session reuse failed for connection '${connection.name}': ${cookieError.message}. Falling back to fresh login.`\n )\n connection.cookies = null\n }\n }\n\n if (!sessionRestored) {\n await this.performLogin(page, uri, loginInfo)\n cookies = await page.cookies()\n connection.cookies = cookies\n }\n } else {\n // 로그인이 필요하지 않은 경우에도 기본 페이지로 이동\n await page.goto(uri, { waitUntil: 'networkidle2', timeout })\n }\n\n return page\n } catch (error) {\n ConnectionManager.logger.error('Failed to acquire session page:', error)\n \n // CRITICAL: 에러 발생 시 확실한 리소스 정리\n if (page) {\n try {\n await page.close()\n ConnectionManager.logger.info('Page closed during acquireSessionPage error handling')\n } catch (closeError) {\n ConnectionManager.logger.error('Failed to close page during acquireSessionPage error:', closeError)\n }\n }\n \n try {\n await releaseBrowser(browser)\n ConnectionManager.logger.info('Browser released during acquireSessionPage error handling')\n } catch (releaseError) {\n ConnectionManager.logger.error('Failed to release browser during acquireSessionPage error:', releaseError)\n }\n \n throw error\n }\n },\n releasePage: async (page: Page) => {\n try {\n if (page && !page.isClosed()) {\n const browser = page.browser()\n \n // 페이지를 먼저 닫기\n try {\n await page.close()\n ConnectionManager.logger.info('Page closed successfully')\n } catch (closeError) {\n ConnectionManager.logger.error('Failed to close page:', closeError)\n }\n \n // 브라우저를 풀에 반환\n try {\n await releaseBrowser(browser)\n ConnectionManager.logger.info('Browser released successfully')\n } catch (releaseError) {\n ConnectionManager.logger.error('Failed to release browser:', releaseError)\n // 풀 릴리즈가 실패한 경우 브라우저 강제 종료 시도\n try {\n if (browser && browser.isConnected()) {\n await browser.close()\n ConnectionManager.logger.warn('Forcibly closed browser due to pool release failure')\n }\n } catch (forceCloseError) {\n ConnectionManager.logger.error('Failed to force close browser:', forceCloseError)\n }\n }\n }\n } catch (error) {\n ConnectionManager.logger.error('Critical error in releasePage:', error)\n }\n },\n // 세션 회복을 위한 재로그인 메서드\n reAuthenticateSession: async () => {\n ConnectionManager.logger.info(`Re-authenticating session for connection: ${connection.name}`)\n \n let browser = null\n let page = null\n\n try {\n browser = await acquireBrowser()\n page = await browser.newPage()\n await this.setupPage(page, uri, loginInfo.timeout)\n \n if (loginInfo.loginRequired) {\n await this.performLogin(page, uri, loginInfo)\n const newCookies = await page.cookies()\n connection.cookies = newCookies\n ConnectionManager.logger.info(`Session re-authenticated successfully for connection: ${connection.name}`)\n }\n \n // CRITICAL: 페이지와 함께 브라우저 정보도 반환하여 정확한 릴리즈 추적\n return {\n page,\n browser,\n requiresManualRelease: true // 수동 릴리즈 필요 표시\n }\n } catch (error) {\n ConnectionManager.logger.error(`Failed to re-authenticate session for connection: ${connection.name}`, error)\n \n // 에러 시 확실한 리소스 정리\n if (page) {\n try {\n if (!page.isClosed()) {\n await page.close()\n ConnectionManager.logger.info('Page closed during reauth error handling')\n }\n } catch (closeError) {\n ConnectionManager.logger.error('Failed to close page during reauth error:', closeError)\n }\n }\n \n if (browser) {\n try {\n await releaseBrowser(browser)\n ConnectionManager.logger.info('Browser released during reauth error handling')\n } catch (releaseError) {\n ConnectionManager.logger.error('Failed to release browser during reauth error:', releaseError)\n // 풀 릴리즈가 실패한 경우 브라우저 강제 종료 시도\n try {\n if (browser.isConnected()) {\n await browser.close()\n ConnectionManager.logger.warn('Forcibly closed browser during reauth error due to pool release failure')\n }\n } catch (forceCloseError) {\n ConnectionManager.logger.error('Failed to force close browser during reauth error:', forceCloseError)\n }\n }\n }\n \n throw error\n }\n },\n // 세션 유효성 검사\n validateSession: async (page: Page) => {\n try {\n if (!loginInfo.loginRequired) {\n return true\n }\n\n // 페이지가 닫혔거나 연결이 끊어진 경우\n if (!page || page.isClosed()) {\n ConnectionManager.logger.warn(`Page is closed or null for connection: ${connection.name}`)\n return false\n }\n\n // URL 기반 로그인 리디렉션 체크\n let currentUrl: string\n try {\n currentUrl = page.url()\n } catch (urlError) {\n ConnectionManager.logger.warn(`Cannot get page URL for connection: ${connection.name}`, urlError)\n return false\n }\n \n if (currentUrl.includes(loginInfo.loginPagePath) || \n currentUrl.includes('/login') || \n currentUrl.includes('/signin') || \n currentUrl.includes('/auth')) {\n ConnectionManager.logger.info(`Redirected to login page: ${currentUrl}`)\n return false\n }\n\n // 로그인 폼 요소 기반 체크\n const needsLogin = await page.evaluate(() => {\n try {\n // 로그인 페이지 특정 요소들이 있는지 확인\n const loginIndicators = ['#username', '#password', '.login-form', '[type=\"password\"]']\n return loginIndicators.some(selector => {\n const element = document.querySelector(selector) as HTMLElement\n return element && element.offsetParent !== null // 보이는 요소만 체크\n })\n } catch (evalError) {\n return true // 에러 시 안전하게 재로그인 필요로 간주\n }\n })\n\n // 추가 세션 검증: 간단한 API 호출로 인증 상태 확인\n try {\n const testResponse = await page.evaluate(async () => {\n try {\n const response = await fetch(window.location.origin, { \n method: 'HEAD',\n credentials: 'include'\n })\n return {\n status: response.status,\n ok: response.ok\n }\n } catch (fetchError) {\n return { status: 0, ok: false }\n }\n })\n \n // 인증이 필요한 엔드포인트에서 401/403 반환시 세션 무효\n if (testResponse.status === 401 || testResponse.status === 403) {\n ConnectionManager.logger.info(`Authentication failed with status ${testResponse.status} for connection: ${connection.name}`)\n return false\n }\n } catch (apiTestError) {\n ConnectionManager.logger.warn(`Session API test failed for connection: ${connection.name}`, apiTestError)\n // API 테스트 실패는 세션 무효로 간주하지 않음 (네트워크 문제일 수 있음)\n }\n\n return !needsLogin\n } catch (error) {\n ConnectionManager.logger.warn(`Session validation failed for connection: ${connection.name}`, error)\n return false\n }\n },\n acquireBrowser,\n releaseBrowser\n })\n\n ConnectionManager.logger.info(\n `headless-connector connection(${connection.name}:${connection.endpoint}) is connected`\n )\n }\n\n async setupPage(page, uri, timeout) {\n await page.setRequestInterception(true)\n\n await page.setBypassCSP(true)\n\n await page.setUserAgent(\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.204 Safari/537.36'\n )\n\n await page.evaluateOnNewDocument(() => {\n Object.defineProperty(navigator, 'webdriver', { get: () => false })\n })\n\n await page.setExtraHTTPHeaders({\n 'Upgrade-Insecure-Requests': '0',\n 'accept-language': 'en-US,en;q=0.9',\n referer: uri\n })\n\n await page.setDefaultNavigationTimeout(timeout)\n\n page.on('console', async msg => {\n ConnectionManager.logger.info(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('request', request => {\n if (request.isInterceptResolutionHandled()) {\n return\n }\n\n const resourceType = request.resourceType()\n\n // fetch(), XMLHttpRequest 등의 API 호출은 항상 허용\n if (resourceType === 'fetch' || resourceType === 'xhr') {\n request.continue()\n return\n }\n\n const headers = request.headers()\n if (headers['access-control-request-headers']) {\n const filteredHeaders = headers['access-control-request-headers']\n .split(',')\n .map(header => header.trim())\n .filter(header => header.toLowerCase() !== 'upgrade-insecure-requests')\n\n if (filteredHeaders.length > 0) {\n headers['access-control-request-headers'] = filteredHeaders.join(', ')\n } else {\n delete headers['access-control-request-headers']\n }\n }\n\n if (headers['upgrade-insecure-requests']) {\n delete headers['upgrade-insecure-requests']\n }\n\n // 이미지, 스타일시트, 폰트는 차단하되 API 요청은 허용\n if (['image', 'stylesheet', 'font'].includes(resourceType)) {\n request.abort()\n } else {\n request.continue()\n }\n })\n\n page.on('requestfailed', request => {\n try {\n console.log('Request failed:')\n console.log(`- URL: ${request.url()}`)\n console.log(`- Method: ${request.method()}`)\n console.log(`- Failure Text: ${request.failure()?.errorText}`)\n console.log(`- Headers:`, request.headers())\n\n if (request.postData()) {\n console.log(`- Post Data: ${request.postData()}`)\n }\n } catch (error) {\n console.error('Error in requestfailed handler:', error)\n }\n })\n }\n\n async applyCookiesAndVerifySession(page, cookies, loginInfo) {\n await page.setCookie(...cookies)\n await page.reload({ waitUntil: 'networkidle2', timeout: loginInfo.timeout })\n\n if (loginInfo.loginRequired && loginInfo.loginSelectors.successSelector) {\n const success = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.successSelector\n )\n if (!success) {\n throw new Error('Session invalid, login required')\n }\n }\n }\n\n async performLogin(page, uri, loginInfo) {\n for (let attempt = 1; attempt <= loginInfo.retries; attempt++) {\n try {\n await page.goto(`${uri}${loginInfo.loginPagePath}`, { waitUntil: 'networkidle2', timeout: loginInfo.timeout })\n\n const usernameInput = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.usernameSelector\n )\n const passwordInput = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.passwordSelector\n )\n const submitButton = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.submitSelector\n )\n\n if (!usernameInput || !passwordInput || !submitButton) {\n throw new Error('Failed to locate input elements in shadow DOM')\n }\n\n await usernameInput.type(loginInfo.username)\n await passwordInput.type(loginInfo.password)\n\n var loginApiUrl =\n loginInfo.loginApiUrl ||\n (await page.evaluate(button => {\n const form = button.closest('form')\n return form ? form.action : null\n }, submitButton))\n\n if (loginApiUrl && !loginApiUrl.startsWith('http')) {\n loginApiUrl = `${uri}${loginApiUrl}`\n }\n\n if (!loginApiUrl) {\n throw new Error('❌ Unable to detect login API URL!')\n }\n\n console.log('✅ Detected login API URL:', loginApiUrl)\n\n const [response] = await Promise.all([\n page.waitForResponse(response => {\n const url = response.url()\n const method = response.request().method()\n\n return url === loginApiUrl && method === 'POST' && [200, 201, 204, 302, 304].includes(response.status())\n }),\n page.evaluate(button => button.click(), submitButton),\n page.waitForNavigation({ waitUntil: 'networkidle2', timeout: loginInfo.timeout }) // 로그인 후 리디렉션 감지\n ])\n\n if (response) {\n const status = response.status()\n if (status >= 200 && status < 400) {\n // 로그인 응답이 2xx/3xx여도 실제로는 실패 페이지(아이디/비밀번호 오류 등)로 떨어질 수 있으므로 최종 URL을 함께 기록\n const postLoginUrl = page.url()\n ConnectionManager.logger.info(\n `Login successful with status code: ${status} — post-login URL: ${postLoginUrl}`\n )\n if (postLoginUrl.includes(loginInfo.loginPagePath)) {\n ConnectionManager.logger.warn(\n `Login response was ${status} but page is still on login page (${postLoginUrl}) — ` +\n `credentials may be wrong, or the site requires additional steps (captcha, keyboard security, OTP).`\n )\n }\n return\n } else if (status >= 400) {\n throw new Error(`Login failed with status code: ${status}`)\n }\n } else {\n throw new Error('No response received during login')\n }\n\n if (loginInfo.loginSelectors.successSelector) {\n const success = await this.resolveShadowDom(\n page,\n loginInfo.loginSelectors.shadowDomSelectors,\n loginInfo.loginSelectors.successSelector\n )\n if (!success) {\n throw new Error('Login failed: Success selector not found')\n }\n }\n } catch (error) {\n // 실패 시점의 페이지 상태(URL/타이틀)를 함께 기록하여 셀렉터 불일치, 보안모듈, 리디렉션 등 원인 추적 가능하게 함\n let failureContext = ''\n try {\n const failUrl = page.url()\n const failTitle = await page.title().catch(() => 'unknown')\n failureContext = ` (URL: ${failUrl}, title: ${failTitle})`\n } catch (contextError) {\n failureContext = ' (failed to capture page state)'\n }\n ConnectionManager.logger.warn(`Login attempt ${attempt}/${loginInfo.retries} failed${failureContext}:`, error)\n\n try {\n await page.screenshot({ path: `logs/login-failure-attempt-${attempt}.png` })\n ConnectionManager.logger.info(`Login failure screenshot saved: logs/login-failure-attempt-${attempt}.png`)\n } catch (error) {\n ConnectionManager.logger.error('Failed to capture screenshot:', error)\n }\n\n if (attempt === loginInfo.retries) {\n throw new Error(`Login failed after ${loginInfo.retries} attempts: ${error.message}${failureContext}`)\n }\n }\n }\n }\n\n async resolveShadowDom(page, shadowSelectors, targetSelector) {\n let context\n\n if (!shadowSelectors || shadowSelectors.length === 0) {\n // No Shadow DOM path; use document root as the context\n context = page.mainFrame() // Puppeteer uses frames to represent document\n return context.$(targetSelector) // Search directly in the document root\n }\n\n context = page // Start with the page as the context\n for (const selector of shadowSelectors) {\n const shadowHost = await context.$(selector)\n if (!shadowHost) {\n throw new Error(`Shadow host not found: ${selector}`)\n }\n context = await page.evaluateHandle(host => host.shadowRoot, shadowHost)\n }\n return context.evaluateHandle((shadowRoot, selector) => shadowRoot.querySelector(selector), targetSelector)\n }\n\n async disconnect(connection) {\n try {\n // 연결 해제 전 리소스 정리 로그\n ConnectionManager.logger.info(`Starting disconnect process for connection: ${connection.name}`)\n \n // 풀 상태 확인 (정보성 로깅)\n try {\n const pool = getHeadlessPool()\n ConnectionManager.logger.info(`Pool status before disconnect - available resources exist: ${!!pool}`)\n } catch (poolError) {\n ConnectionManager.logger.warn(`Could not access pool during disconnect: ${poolError.message}`)\n }\n\n ConnectionManager.removeConnectionInstance(connection)\n ConnectionManager.logger.info(`headless-connector connection(${connection.name}) is disconnected`)\n } catch (error) {\n ConnectionManager.logger.error(`Error disconnecting headless-connector connection(${connection.name}):`, error)\n // Still try to remove the connection instance even if cleanup failed\n try {\n ConnectionManager.removeConnectionInstance(connection)\n } catch (removeError) {\n ConnectionManager.logger.error(`Failed to remove connection instance ${connection.name}:`, removeError)\n }\n }\n }\n\n get parameterSpec() {\n return [\n {\n type: 'string',\n name: 'username',\n label: 'username',\n useDomainAttribute: true\n },\n {\n type: 'secret',\n name: 'password',\n label: 'password',\n useDomainAttribute: true\n },\n {\n type: 'string',\n name: 'loginPagePath',\n label: 'login-page-path'\n },\n {\n type: 'string',\n name: 'loginApiUrl',\n label: 'login-api-url'\n },\n {\n type: 'string',\n name: 'usernameSelector',\n label: 'username-selector'\n },\n {\n type: 'string',\n name: 'passwordSelector',\n label: 'password-selector'\n },\n {\n type: 'string',\n name: 'submitSelector',\n label: 'submit-selector'\n },\n {\n type: 'string',\n name: 'successSelector',\n label: 'success-selector'\n },\n {\n type: 'string',\n name: 'shadowDomSelectors',\n label: 'shadow-dom-selectors'\n },\n {\n type: 'number',\n name: 'timeout',\n label: 'timeout',\n value: 15000\n },\n {\n type: 'number',\n name: 'retries',\n label: 'maximum-retries',\n value: 3\n }\n ]\n }\n\n get taskPrefixes() {\n return ['headless']\n }\n\n get description() {\n return 'Headless Pool Connector with login capabilities'\n }\n\n get help() {\n return 'integration/connector/headless-connector'\n }\n}\n\nConnectionManager.registerConnector('headless-connector', new HeadlessConnector())\n\nfunction isCookieValid(cookies: any[]): boolean {\n if (!cookies || cookies.length === 0) return false\n const now = Date.now() / 1000 // Current time in seconds\n return cookies.some((cookie: any) => cookie.expires && cookie.expires > now)\n}\n"]}
@@ -503,6 +503,17 @@ async function performFullConnectionReset(domain, connectionName, logger, pageRe
503
503
  if (!connector) {
504
504
  throw new Error(`Connector not found for type: ${type}`);
505
505
  }
506
+ // CRITICAL: 캐시된 엔티티에 남아있는 쿠키 제거
507
+ // 서버측에서 세션이 무효화된 경우 쿠키는 만료시간상 유효해 보여도 죽은 쿠키이므로,
508
+ // 이를 지우지 않으면 acquireSessionPage가 로그인을 건너뛰고 죽은 쿠키를 재사용하여 reset이 무의미해짐
509
+ const staleCookies = connectionEntity.cookies;
510
+ if (staleCookies) {
511
+ logger.info(`[Reset] Clearing ${Array.isArray(staleCookies) ? staleCookies.length : '?'} cached cookies on connection entity to force fresh login`);
512
+ connectionEntity.cookies = null;
513
+ }
514
+ else {
515
+ logger.info(`[Reset] No cached cookies on connection entity — fresh login will be performed`);
516
+ }
506
517
  // 기존 연결 완전 해제
507
518
  await connector.disconnect(connectionEntity);
508
519
  logger.info(`Connection '${connectionName}' disconnected for reset`);
@@ -511,8 +522,19 @@ async function performFullConnectionReset(domain, connectionName, logger, pageRe
511
522
  // 코드에서는 즉시 연결을 시도하므로 명시적 대기 필요
512
523
  await new Promise(resolve => setTimeout(resolve, 3000));
513
524
  // 새로운 연결 생성 (완전한 초기화)
514
- await connector.connect(connectionEntity);
515
- logger.info(`Connection '${connectionName}' reconnected after reset`);
525
+ // CRITICAL: connector.connect(entity)를 직접 호출하면 getResolvedParameters()를 거치지 않아
526
+ // 자격증명(username/password)이 해석되지 않은 연결될 수 있음 → loginRequired=false로 로그인 자체가 생략됨.
527
+ // 반드시 엔티티의 connect()를 사용하여 해석된 파라미터로 연결한다.
528
+ if (typeof connectionEntity.connect === 'function') {
529
+ await connectionEntity.connect();
530
+ logger.info(`Connection '${connectionName}' reconnected after reset (with resolved credentials)`);
531
+ }
532
+ else {
533
+ logger.warn(`[Reset] connectionEntity.connect() not available — falling back to raw connector.connect(). ` +
534
+ `Credentials may not be resolved (loginRequired could be false).`);
535
+ await connector.connect(connectionEntity);
536
+ logger.info(`Connection '${connectionName}' reconnected after reset`);
537
+ }
516
538
  // 새 브라우저 인스턴스 초기화 대기
517
539
  await new Promise(resolve => setTimeout(resolve, 2000));
518
540
  // 새로운 connection 인스턴스 가져오기