@things-factory/integration-base 9.1.1 → 9.1.4
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.
|
@@ -51,25 +51,18 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
|
|
|
51
51
|
logger.debug(`Validating session for connection '${connectionName}' (attempt: ${attempt + 1})`);
|
|
52
52
|
const isSessionValid = await validateSession(page);
|
|
53
53
|
if (!isSessionValid) {
|
|
54
|
-
logger.warn(`Session invalid for connection '${connectionName}', attempting
|
|
55
|
-
await safeReleasePageResource(pageResource, releasePage, logger);
|
|
54
|
+
logger.warn(`Session invalid for connection '${connectionName}', attempting full connection reset (attempt: ${attempt + 1})`);
|
|
56
55
|
try {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
page = reauthResult;
|
|
64
|
-
pageResource = { page, requiresManualRelease: false };
|
|
65
|
-
}
|
|
66
|
-
logger.info(`Re-authentication successful for connection '${connectionName}'`);
|
|
56
|
+
const resetResult = await performFullConnectionReset(domain, connectionName, logger, pageResource, releasePage);
|
|
57
|
+
pageResource = resetResult.pageResource;
|
|
58
|
+
page = resetResult.page;
|
|
59
|
+
logger.info(`Full connection reset successful for connection '${connectionName}'`);
|
|
67
60
|
}
|
|
68
|
-
catch (
|
|
69
|
-
logger.error(`
|
|
70
|
-
//
|
|
61
|
+
catch (resetError) {
|
|
62
|
+
logger.error(`Connection reset failed for connection '${connectionName}':`, resetError);
|
|
63
|
+
// 재설정 실패 시 이번 시도는 실패로 처리하고 다음 시도로 넘어감
|
|
71
64
|
if (attempt === maxRetries) {
|
|
72
|
-
throw new Error(`
|
|
65
|
+
throw new Error(`Connection reset failed after ${maxRetries + 1} attempts: ${resetError.message}`);
|
|
73
66
|
}
|
|
74
67
|
continue;
|
|
75
68
|
}
|
|
@@ -111,7 +104,7 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
|
|
|
111
104
|
requestOptions.body = typeof requestBody === 'string' ? requestBody : JSON.stringify(requestBody);
|
|
112
105
|
}
|
|
113
106
|
}
|
|
114
|
-
// fetch 요청 실행 - try-catch로 네트워크 에러 처리
|
|
107
|
+
// fetch 요청 실행 - try-catch로 네트워크 에러 처리
|
|
115
108
|
const response = await page.evaluate(async (urlString, opts, loginPagePath) => {
|
|
116
109
|
try {
|
|
117
110
|
const response = await fetch(urlString, { ...opts, redirect: 'manual' });
|
|
@@ -144,10 +137,28 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
|
|
|
144
137
|
status: redirectResponse.status,
|
|
145
138
|
statusText: redirectResponse.statusText,
|
|
146
139
|
headers: Object.fromEntries(redirectResponse.headers.entries()),
|
|
147
|
-
data: redirectResponse.ok
|
|
140
|
+
data: redirectResponse.ok
|
|
141
|
+
? await redirectResponse
|
|
142
|
+
.json()
|
|
143
|
+
.catch(() => redirectResponse.text())
|
|
144
|
+
.catch(() => null)
|
|
145
|
+
: null,
|
|
148
146
|
error: redirectResponse.ok ? null : `HTTP ${redirectResponse.status}: ${redirectResponse.statusText}`
|
|
149
147
|
};
|
|
150
148
|
}
|
|
149
|
+
// status가 0인 경우는 네트워크 레벨 에러 (CORS, 연결 실패 등)
|
|
150
|
+
if (response.status === 0) {
|
|
151
|
+
logger.info(`[status0 ERROR] response: ${JSON.stringify(response)}`);
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
status: 0,
|
|
155
|
+
statusText: response.statusText || 'Network Error',
|
|
156
|
+
headers: {},
|
|
157
|
+
error: 'Network connection failed - possible CORS, SSL, or connectivity issue',
|
|
158
|
+
data: null,
|
|
159
|
+
networkError: true
|
|
160
|
+
};
|
|
161
|
+
}
|
|
151
162
|
if (!response.ok) {
|
|
152
163
|
return {
|
|
153
164
|
...result,
|
|
@@ -183,6 +194,7 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
|
|
|
183
194
|
}, url.toString(), requestOptions, loginPagePath);
|
|
184
195
|
// 네트워크 에러 체크 (fetch 자체가 실패한 경우)
|
|
185
196
|
if (response.networkError) {
|
|
197
|
+
logger.info(`[networkError ERROR] response: ${JSON.stringify(response)}`);
|
|
186
198
|
if (attempt < maxRetries) {
|
|
187
199
|
logger.warn(`Network error detected: ${response.error}, retrying... (${attempt + 1}/${maxRetries + 1})`);
|
|
188
200
|
await safeReleasePageResource(pageResource, releasePage, logger);
|
|
@@ -198,25 +210,18 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
|
|
|
198
210
|
// 로그인 리디렉션 감지 처리
|
|
199
211
|
if (response.redirectedToLogin) {
|
|
200
212
|
if (attempt < maxRetries) {
|
|
201
|
-
logger.warn(`Login redirect detected: ${response.location}, performing
|
|
202
|
-
|
|
203
|
-
// CRITICAL: 실제 재인증 수행
|
|
213
|
+
logger.warn(`Login redirect detected: ${response.location}, performing full connection reset... (${attempt + 1}/${maxRetries + 1})`);
|
|
214
|
+
// CRITICAL: 커넥션 재연결 방식으로 완전한 초기화
|
|
204
215
|
try {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
page = reauthResult;
|
|
212
|
-
pageResource = { page, requiresManualRelease: false };
|
|
213
|
-
}
|
|
214
|
-
logger.info(`Re-authentication successful after login redirect for connection '${connectionName}'`);
|
|
216
|
+
const resetResult = await performFullConnectionReset(domain, connectionName, logger, pageResource, releasePage);
|
|
217
|
+
pageResource = resetResult.pageResource;
|
|
218
|
+
page = resetResult.page;
|
|
219
|
+
logger.info(`Full connection reset successful after login redirect for connection '${connectionName}'`);
|
|
215
220
|
}
|
|
216
|
-
catch (
|
|
217
|
-
logger.error(`
|
|
221
|
+
catch (resetError) {
|
|
222
|
+
logger.error(`Connection reset failed after login redirect for connection '${connectionName}':`, resetError);
|
|
218
223
|
if (attempt === maxRetries) {
|
|
219
|
-
throw new Error(`
|
|
224
|
+
throw new Error(`Connection reset failed after login redirect: ${resetError.message}`);
|
|
220
225
|
}
|
|
221
226
|
continue;
|
|
222
227
|
}
|
|
@@ -230,25 +235,18 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
|
|
|
230
235
|
// 세션 타임아웃 관련 에러 체크
|
|
231
236
|
if (!response.ok && isSessionTimeoutError(response.status)) {
|
|
232
237
|
if (attempt < maxRetries) {
|
|
233
|
-
logger.warn(`Session timeout detected (${response.status}), performing
|
|
234
|
-
|
|
235
|
-
// CRITICAL: 실제 재인증 수행
|
|
238
|
+
logger.warn(`Session timeout detected (${response.status}), performing full connection reset... (${attempt + 1}/${maxRetries + 1})`);
|
|
239
|
+
// CRITICAL: 커넥션 재연결 방식으로 완전한 초기화
|
|
236
240
|
try {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
page = reauthResult;
|
|
244
|
-
pageResource = { page, requiresManualRelease: false };
|
|
245
|
-
}
|
|
246
|
-
logger.info(`Re-authentication successful after session timeout for connection '${connectionName}'`);
|
|
241
|
+
const resetResult = await performFullConnectionReset(domain, connectionName, logger, pageResource, releasePage);
|
|
242
|
+
pageResource = resetResult.pageResource;
|
|
243
|
+
page = resetResult.page;
|
|
244
|
+
logger.info(`Full connection reset successful after session timeout for connection '${connectionName}'`);
|
|
247
245
|
}
|
|
248
|
-
catch (
|
|
249
|
-
logger.error(`
|
|
246
|
+
catch (resetError) {
|
|
247
|
+
logger.error(`Connection reset failed after session timeout for connection '${connectionName}':`, resetError);
|
|
250
248
|
if (attempt === maxRetries) {
|
|
251
|
-
throw new Error(`
|
|
249
|
+
throw new Error(`Connection reset failed after session timeout: ${resetError.message}`);
|
|
252
250
|
}
|
|
253
251
|
continue;
|
|
254
252
|
}
|
|
@@ -381,4 +379,38 @@ async function safeReleasePageResource(pageResource, releasePage, logger) {
|
|
|
381
379
|
}
|
|
382
380
|
}
|
|
383
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* 커넥션을 완전 재초기화하는 함수
|
|
384
|
+
* 세션 만료 시 커넥션 재연결과 동일한 완전한 초기화를 수행
|
|
385
|
+
*/
|
|
386
|
+
async function performFullConnectionReset(domain, connectionName, logger, pageResource, releasePage) {
|
|
387
|
+
// 기존 페이지 리소스 해제
|
|
388
|
+
await safeReleasePageResource(pageResource, releasePage, logger);
|
|
389
|
+
// 커넥션 재연결 방식으로 완전한 초기화
|
|
390
|
+
const connectionEntity = await connection_manager_1.ConnectionManager.getConnectionEntityByName(domain, connectionName);
|
|
391
|
+
if (!connectionEntity) {
|
|
392
|
+
throw new Error('Connection entity not found for reset');
|
|
393
|
+
}
|
|
394
|
+
// 기존 연결 완전 해제
|
|
395
|
+
await connectionEntity.disconnect();
|
|
396
|
+
// 새로운 연결 생성 (완전한 초기화)
|
|
397
|
+
await connectionEntity.connect();
|
|
398
|
+
// 새로운 connection 인스턴스 가져오기
|
|
399
|
+
const newConnection = await connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
|
|
400
|
+
if (!newConnection || !newConnection.acquireSessionPage) {
|
|
401
|
+
throw new Error('Failed to acquire session page after connection reset');
|
|
402
|
+
}
|
|
403
|
+
const newSessionResult = await newConnection.acquireSessionPage();
|
|
404
|
+
let newPageResource;
|
|
405
|
+
let newPage;
|
|
406
|
+
if (newSessionResult && typeof newSessionResult === 'object' && newSessionResult.page) {
|
|
407
|
+
newPageResource = newSessionResult;
|
|
408
|
+
newPage = newSessionResult.page;
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
newPage = newSessionResult;
|
|
412
|
+
newPageResource = { page: newPage, requiresManualRelease: false };
|
|
413
|
+
}
|
|
414
|
+
return { pageResource: newPageResource, page: newPage };
|
|
415
|
+
}
|
|
384
416
|
//# sourceMappingURL=headless-request-with-recovery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-request-with-recovery.js","sourceRoot":"","sources":["../../../../server/engine/task/utils/headless-request-with-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import { access } from '@things-factory/utils'\nimport { ConnectionManager } from '../../connection-manager'\n\nexport interface HeadlessRequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n path: string\n headers?: Record<string, string>\n body?: any\n queryParams?: Record<string, any>\n maxRetries?: number\n accessor?: string // POST 요청에서 data 접근용\n contentType?: string // POST 요청에서 content-type 지정용\n}\n\n/**\n * 세션 회복 기능을 포함한 headless HTTP 요청 함수\n */\nexport async function executeHeadlessRequestWithRecovery(\n connectionName: string,\n options: HeadlessRequestOptions,\n context: { logger: any; data: any; domain: any }\n): Promise<any> {\n const { logger, data, domain } = context\n const { method, path, headers = {}, body, queryParams, maxRetries = 2, accessor, contentType } = options\n\n // accessor가 있으면 data에서 body 추출\n let requestBody = body\n if (accessor && data) {\n requestBody = access(accessor, data)\n }\n\n const connection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const { endpoint, params: connectionParams, acquireSessionPage, releasePage, reAuthenticateSession, validateSession } = connection\n const loginPagePath = connectionParams?.loginPagePath || '/login'\n\n let page = null\n let pageResource = null // 리소스 추적 객체\n let lastError = null\n\n // 재시도 로직\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // 페이지 획득\n const sessionResult = await acquireSessionPage()\n \n // reAuthenticateSession의 반환 형태 확인\n if (sessionResult && typeof sessionResult === 'object' && sessionResult.page) {\n pageResource = sessionResult // {page, browser, requiresManualRelease}\n page = sessionResult.page\n } else {\n page = sessionResult\n pageResource = { page, requiresManualRelease: false }\n }\n \n // 페이지가 올바른 도메인에 있는지 확인\n const currentUrl = page.url()\n const targetDomain = new URL(endpoint).origin\n if (!currentUrl.startsWith(targetDomain)) {\n logger.info(`Navigating to target domain: ${targetDomain}`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\n \n // 세션 검증은 2번째 시도부터만 수행 (첫 번째는 새 페이지이므로 불필요)\n // 또는 이전 시도에서 세션 관련 에러가 발생한 경우에만 수행\n if (attempt > 0) {\n logger.debug(`Validating session for connection '${connectionName}' (attempt: ${attempt + 1})`)\n const isSessionValid = await validateSession(page)\n if (!isSessionValid) {\n logger.warn(`Session invalid for connection '${connectionName}', attempting re-authentication (attempt: ${attempt + 1})`)\n await safeReleasePageResource(pageResource, releasePage, logger)\n \n try {\n const reauthResult = await reAuthenticateSession()\n if (reauthResult && typeof reauthResult === 'object' && reauthResult.page) {\n pageResource = reauthResult\n page = reauthResult.page\n } else {\n page = reauthResult\n pageResource = { page, requiresManualRelease: false }\n }\n logger.info(`Re-authentication successful for connection '${connectionName}'`)\n } catch (reauthError) {\n logger.error(`Re-authentication failed for connection '${connectionName}':`, reauthError)\n // 재인증 실패 시 이번 시도는 실패로 처리하고 다음 시도로 넘어감\n if (attempt === maxRetries) {\n throw new Error(`Re-authentication failed after ${maxRetries + 1} attempts: ${reauthError.message}`)\n }\n continue\n }\n }\n }\n\n // URL 구성\n const url = new URL(path, endpoint)\n if (queryParams && typeof queryParams === 'object') {\n Object.keys(queryParams).forEach(key => {\n if (queryParams[key] !== null && queryParams[key] !== undefined) {\n url.searchParams.append(key, String(queryParams[key]))\n }\n })\n }\n\n // 요청 옵션 구성\n const requestOptions: any = {\n method,\n headers: {\n ...headers\n },\n credentials: 'include'\n }\n\n // Content-Type과 body 처리\n if (requestBody && ['POST', 'PUT', 'PATCH'].includes(method)) {\n if (contentType) {\n requestOptions.headers['content-type'] = contentType\n switch (contentType) {\n case 'text/plain':\n requestOptions.body = JSON.stringify(requestBody)\n break\n case 'application/json':\n default:\n requestOptions.body = typeof requestBody === 'string' ? requestBody : JSON.stringify(requestBody)\n break\n }\n } else {\n requestOptions.headers['Content-Type'] = 'application/json'\n requestOptions.body = typeof requestBody === 'string' ? requestBody : JSON.stringify(requestBody)\n }\n }\n\n // fetch 요청 실행 - try-catch로 네트워크 에러 처리 \n const response = await page.evaluate(\n async (urlString, opts, loginPagePath) => {\n try {\n const response = await fetch(urlString, { ...opts, redirect: 'manual' })\n \n const result = {\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries())\n }\n\n // 302 리디렉션 감지 - 로그인 페이지로의 리디렉션 체크\n if ([301, 302, 307, 308].includes(response.status)) {\n const location = response.headers.get('location') || ''\n // connection의 loginPagePath와 일반적인 로그인 경로들을 체크\n if (location.includes(loginPagePath) || \n location.includes('/login') || \n location.includes('/signin') || \n location.includes('/auth')) {\n return {\n ...result,\n error: `Redirected to login page: ${location}`,\n redirectedToLogin: true,\n location,\n data: null\n }\n }\n // 다른 리디렉션은 follow\n const redirectResponse = await fetch(urlString, opts)\n return {\n ok: redirectResponse.ok,\n status: redirectResponse.status,\n statusText: redirectResponse.statusText,\n headers: Object.fromEntries(redirectResponse.headers.entries()),\n data: redirectResponse.ok ? await redirectResponse.json().catch(() => redirectResponse.text()).catch(() => null) : null,\n error: redirectResponse.ok ? null : `HTTP ${redirectResponse.status}: ${redirectResponse.statusText}`\n }\n }\n\n if (!response.ok) {\n return {\n ...result,\n error: `HTTP ${response.status}: ${response.statusText}`,\n data: null\n }\n }\n\n const contentType = response.headers.get('content-type') || ''\n \n if (contentType.includes('application/json')) {\n result['data'] = await response.json()\n } else if (contentType.includes('text/')) {\n result['data'] = await response.text()\n } else {\n // 응답이 없는 경우 (204 No Content 등)\n result['data'] = null\n }\n\n return result\n } catch (fetchError) {\n // 네트워크 레벨 에러 처리\n return {\n ok: false,\n status: 0,\n statusText: 'Network Error',\n headers: {},\n error: fetchError.message || 'Failed to fetch',\n data: null,\n networkError: true\n }\n }\n },\n url.toString(),\n requestOptions,\n loginPagePath\n )\n\n // 네트워크 에러 체크 (fetch 자체가 실패한 경우)\n if (response.networkError) {\n if (attempt < maxRetries) {\n logger.warn(`Network error detected: ${response.error}, retrying... (${attempt + 1}/${maxRetries + 1})`)\n await safeReleasePageResource(pageResource, releasePage, logger)\n page = null\n pageResource = null\n continue\n } else {\n await safeReleasePageResource(pageResource, releasePage, logger)\n throw new Error(`Network error after ${maxRetries + 1} attempts: ${response.error}`)\n }\n }\n\n // 로그인 리디렉션 감지 처리\n if (response.redirectedToLogin) {\n if (attempt < maxRetries) {\n logger.warn(`Login redirect detected: ${response.location}, performing re-authentication... (${attempt + 1}/${maxRetries + 1})`)\n await safeReleasePageResource(pageResource, releasePage, logger)\n \n // CRITICAL: 실제 재인증 수행\n try {\n const reauthResult = await reAuthenticateSession()\n if (reauthResult && typeof reauthResult === 'object' && reauthResult.page) {\n pageResource = reauthResult\n page = reauthResult.page\n } else {\n page = reauthResult\n pageResource = { page, requiresManualRelease: false }\n }\n logger.info(`Re-authentication successful after login redirect for connection '${connectionName}'`)\n } catch (reauthError) {\n logger.error(`Re-authentication failed after login redirect for connection '${connectionName}':`, reauthError)\n if (attempt === maxRetries) {\n throw new Error(`Re-authentication failed after login redirect: ${reauthError.message}`)\n }\n continue\n }\n continue\n } else {\n await safeReleasePageResource(pageResource, releasePage, logger)\n throw new Error(`Login redirect after ${maxRetries + 1} attempts: ${response.error}`)\n }\n }\n\n // 세션 타임아웃 관련 에러 체크\n if (!response.ok && isSessionTimeoutError(response.status)) {\n if (attempt < maxRetries) {\n logger.warn(`Session timeout detected (${response.status}), performing re-authentication... (${attempt + 1}/${maxRetries + 1})`)\n await safeReleasePageResource(pageResource, releasePage, logger)\n \n // CRITICAL: 실제 재인증 수행\n try {\n const reauthResult = await reAuthenticateSession()\n if (reauthResult && typeof reauthResult === 'object' && reauthResult.page) {\n pageResource = reauthResult\n page = reauthResult.page\n } else {\n page = reauthResult\n pageResource = { page, requiresManualRelease: false }\n }\n logger.info(`Re-authentication successful after session timeout for connection '${connectionName}'`)\n } catch (reauthError) {\n logger.error(`Re-authentication failed after session timeout for connection '${connectionName}':`, reauthError)\n if (attempt === maxRetries) {\n throw new Error(`Re-authentication failed after session timeout: ${reauthError.message}`)\n }\n continue\n }\n continue\n } else {\n await safeReleasePageResource(pageResource, releasePage, logger)\n throw new Error(`Session timeout after ${maxRetries + 1} attempts: ${response.error}`)\n }\n }\n\n // 기타 HTTP 에러\n if (!response.ok) {\n throw new Error(response.error)\n }\n\n // 성공 시 페이지 릴리즈 후 결과 반환\n const result = {\n data: response.data,\n status: response.status,\n headers: response.headers\n }\n \n await safeReleasePageResource(pageResource, releasePage, logger)\n \n return result\n\n } catch (error) {\n lastError = error\n logger.error(`Headless request attempt ${attempt + 1} failed:`, error)\n\n await safeReleasePageResource(pageResource, releasePage, logger)\n page = null\n pageResource = null\n\n // 세션 관련 에러가 아니거나 마지막 재시도면 에러 발생\n if (!isRecoverableError(error) || attempt === maxRetries) {\n throw error\n }\n\n logger.info(`Retrying request... (${attempt + 2}/${maxRetries + 1})`)\n }\n }\n\n // 모든 재시도가 실패한 경우 - 혹시 남은 리소스가 있으면 정리\n if (pageResource) {\n await safeReleasePageResource(pageResource, releasePage, logger)\n }\n throw lastError || new Error('Request failed after all retry attempts')\n}\n\n/**\n * 세션 타임아웃 관련 HTTP 상태 코드인지 확인\n */\nfunction isSessionTimeoutError(statusCode: number): boolean {\n return [401, 403].includes(statusCode)\n}\n\n/**\n * 복구 가능한 에러인지 확인\n */\nfunction isRecoverableError(error: any): boolean {\n const errorMessage = error.message?.toLowerCase() || ''\n \n // 복구 가능한 에러 키워드 (네트워크, 세션/인증 관련)\n const recoverableErrorKeywords = [\n 'failed to fetch',\n 'network error',\n 'connection failed',\n 'connection reset',\n 'timeout',\n 'timed out',\n 'unauthorized',\n 'forbidden', \n 'session',\n 'authentication',\n 'login',\n 'expired',\n 'redirected to login' // 로그인 리디렉션 추가\n ]\n\n return recoverableErrorKeywords.some(keyword => errorMessage.includes(keyword))\n}\n\n/**\n * Safely release page resource with comprehensive error handling\n */\nexport async function safeReleasePageResource(pageResource: any, releasePage: Function, logger: any): Promise<void> {\n if (!pageResource) {\n return\n }\n\n try {\n // Handle different pageResource formats\n if (pageResource.page) {\n // This is a complex resource object {page, browser, requiresManualRelease}\n const { page, browser, requiresManualRelease } = pageResource\n \n if (requiresManualRelease && browser) {\n // Manual release required - close page first, then release browser\n try {\n if (page && !page.isClosed()) {\n await page.close()\n logger.info('Page closed during manual resource release')\n }\n } catch (closeError) {\n logger.error('Failed to close page during manual release:', closeError)\n }\n\n // Release browser back to pool\n try {\n const { getHeadlessPool } = require('../../resource-pool/headless-pool')\n const pool = getHeadlessPool()\n await pool.release(browser)\n logger.info('Browser manually released to pool')\n } catch (releaseError) {\n logger.error('Failed to manually release browser to pool:', releaseError)\n }\n } else {\n // Standard release through releasePage\n await releasePage(page)\n logger.info('Page released through standard releasePage method')\n }\n } else {\n // Simple page object - use standard release\n await releasePage(pageResource)\n logger.info('Simple page resource released')\n }\n } catch (error) {\n logger.error('Critical error during page resource release:', error)\n \n // Last resort: try to force close the page if it exists\n try {\n const page = pageResource.page || pageResource\n if (page && !page.isClosed()) {\n await page.close()\n logger.warn('Force closed page as last resort')\n }\n } catch (forceCloseError) {\n logger.error('Failed to force close page:', forceCloseError)\n }\n }\n}"]}
|
|
1
|
+
{"version":3,"file":"headless-request-with-recovery.js","sourceRoot":"","sources":["../../../../server/engine/task/utils/headless-request-with-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["import { access } from '@things-factory/utils'\nimport { ConnectionManager } from '../../connection-manager'\n\nexport interface HeadlessRequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n path: string\n headers?: Record<string, string>\n body?: any\n queryParams?: Record<string, any>\n maxRetries?: number\n accessor?: string // POST 요청에서 data 접근용\n contentType?: string // POST 요청에서 content-type 지정용\n}\n\n/**\n * 세션 회복 기능을 포함한 headless HTTP 요청 함수\n */\nexport async function executeHeadlessRequestWithRecovery(\n connectionName: string,\n options: HeadlessRequestOptions,\n context: { logger: any; data: any; domain: any }\n): Promise<any> {\n const { logger, data, domain } = context\n const { method, path, headers = {}, body, queryParams, maxRetries = 2, accessor, contentType } = options\n\n // accessor가 있으면 data에서 body 추출\n let requestBody = body\n if (accessor && data) {\n requestBody = access(accessor, data)\n }\n\n const connection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const {\n endpoint,\n params: connectionParams,\n acquireSessionPage,\n releasePage,\n reAuthenticateSession,\n validateSession\n } = connection\n const loginPagePath = connectionParams?.loginPagePath || '/login'\n\n let page = null\n let pageResource = null // 리소스 추적 객체\n let lastError = null\n\n // 재시도 로직\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // 페이지 획득\n const sessionResult = await acquireSessionPage()\n\n // reAuthenticateSession의 반환 형태 확인\n if (sessionResult && typeof sessionResult === 'object' && sessionResult.page) {\n pageResource = sessionResult // {page, browser, requiresManualRelease}\n page = sessionResult.page\n } else {\n page = sessionResult\n pageResource = { page, requiresManualRelease: false }\n }\n\n // 페이지가 올바른 도메인에 있는지 확인\n const currentUrl = page.url()\n const targetDomain = new URL(endpoint).origin\n if (!currentUrl.startsWith(targetDomain)) {\n logger.info(`Navigating to target domain: ${targetDomain}`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\n\n // 세션 검증은 2번째 시도부터만 수행 (첫 번째는 새 페이지이므로 불필요)\n // 또는 이전 시도에서 세션 관련 에러가 발생한 경우에만 수행\n if (attempt > 0) {\n logger.debug(`Validating session for connection '${connectionName}' (attempt: ${attempt + 1})`)\n const isSessionValid = await validateSession(page)\n if (!isSessionValid) {\n logger.warn(\n `Session invalid for connection '${connectionName}', attempting full connection reset (attempt: ${attempt + 1})`\n )\n\n try {\n const resetResult = await performFullConnectionReset(\n domain,\n connectionName,\n logger,\n pageResource,\n releasePage\n )\n pageResource = resetResult.pageResource\n page = resetResult.page\n logger.info(`Full connection reset successful for connection '${connectionName}'`)\n } catch (resetError) {\n logger.error(`Connection reset failed for connection '${connectionName}':`, resetError)\n // 재설정 실패 시 이번 시도는 실패로 처리하고 다음 시도로 넘어감\n if (attempt === maxRetries) {\n throw new Error(`Connection reset failed after ${maxRetries + 1} attempts: ${resetError.message}`)\n }\n continue\n }\n }\n }\n\n // URL 구성\n const url = new URL(path, endpoint)\n if (queryParams && typeof queryParams === 'object') {\n Object.keys(queryParams).forEach(key => {\n if (queryParams[key] !== null && queryParams[key] !== undefined) {\n url.searchParams.append(key, String(queryParams[key]))\n }\n })\n }\n\n // 요청 옵션 구성\n const requestOptions: any = {\n method,\n headers: {\n ...headers\n },\n credentials: 'include'\n }\n\n // Content-Type과 body 처리\n if (requestBody && ['POST', 'PUT', 'PATCH'].includes(method)) {\n if (contentType) {\n requestOptions.headers['content-type'] = contentType\n switch (contentType) {\n case 'text/plain':\n requestOptions.body = JSON.stringify(requestBody)\n break\n case 'application/json':\n default:\n requestOptions.body = typeof requestBody === 'string' ? requestBody : JSON.stringify(requestBody)\n break\n }\n } else {\n requestOptions.headers['Content-Type'] = 'application/json'\n requestOptions.body = typeof requestBody === 'string' ? requestBody : JSON.stringify(requestBody)\n }\n }\n\n // fetch 요청 실행 - try-catch로 네트워크 에러 처리\n const response = await page.evaluate(\n async (urlString, opts, loginPagePath) => {\n try {\n const response = await fetch(urlString, { ...opts, redirect: 'manual' })\n\n const result = {\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries())\n }\n\n // 302 리디렉션 감지 - 로그인 페이지로의 리디렉션 체크\n if ([301, 302, 307, 308].includes(response.status)) {\n const location = response.headers.get('location') || ''\n // connection의 loginPagePath와 일반적인 로그인 경로들을 체크\n if (\n location.includes(loginPagePath) ||\n location.includes('/login') ||\n location.includes('/signin') ||\n location.includes('/auth')\n ) {\n return {\n ...result,\n error: `Redirected to login page: ${location}`,\n redirectedToLogin: true,\n location,\n data: null\n }\n }\n // 다른 리디렉션은 follow\n const redirectResponse = await fetch(urlString, opts)\n return {\n ok: redirectResponse.ok,\n status: redirectResponse.status,\n statusText: redirectResponse.statusText,\n headers: Object.fromEntries(redirectResponse.headers.entries()),\n data: redirectResponse.ok\n ? await redirectResponse\n .json()\n .catch(() => redirectResponse.text())\n .catch(() => null)\n : null,\n error: redirectResponse.ok ? null : `HTTP ${redirectResponse.status}: ${redirectResponse.statusText}`\n }\n }\n\n // status가 0인 경우는 네트워크 레벨 에러 (CORS, 연결 실패 등)\n if (response.status === 0) {\n logger.info(`[status0 ERROR] response: ${JSON.stringify(response)}`)\n return {\n ok: false,\n status: 0,\n statusText: response.statusText || 'Network Error',\n headers: {},\n error: 'Network connection failed - possible CORS, SSL, or connectivity issue',\n data: null,\n networkError: true\n }\n }\n\n if (!response.ok) {\n return {\n ...result,\n error: `HTTP ${response.status}: ${response.statusText}`,\n data: null\n }\n }\n\n const contentType = response.headers.get('content-type') || ''\n\n if (contentType.includes('application/json')) {\n result['data'] = await response.json()\n } else if (contentType.includes('text/')) {\n result['data'] = await response.text()\n } else {\n // 응답이 없는 경우 (204 No Content 등)\n result['data'] = null\n }\n\n return result\n } catch (fetchError) {\n // 네트워크 레벨 에러 처리\n return {\n ok: false,\n status: 0,\n statusText: 'Network Error',\n headers: {},\n error: fetchError.message || 'Failed to fetch',\n data: null,\n networkError: true\n }\n }\n },\n url.toString(),\n requestOptions,\n loginPagePath\n )\n\n // 네트워크 에러 체크 (fetch 자체가 실패한 경우)\n if (response.networkError) {\n logger.info(`[networkError ERROR] response: ${JSON.stringify(response)}`)\n\n if (attempt < maxRetries) {\n logger.warn(`Network error detected: ${response.error}, retrying... (${attempt + 1}/${maxRetries + 1})`)\n await safeReleasePageResource(pageResource, releasePage, logger)\n page = null\n pageResource = null\n continue\n } else {\n await safeReleasePageResource(pageResource, releasePage, logger)\n throw new Error(`Network error after ${maxRetries + 1} attempts: ${response.error}`)\n }\n }\n\n // 로그인 리디렉션 감지 처리\n if (response.redirectedToLogin) {\n if (attempt < maxRetries) {\n logger.warn(\n `Login redirect detected: ${response.location}, performing full connection reset... (${attempt + 1}/${maxRetries + 1})`\n )\n\n // CRITICAL: 커넥션 재연결 방식으로 완전한 초기화\n try {\n const resetResult = await performFullConnectionReset(\n domain,\n connectionName,\n logger,\n pageResource,\n releasePage\n )\n pageResource = resetResult.pageResource\n page = resetResult.page\n logger.info(`Full connection reset successful after login redirect for connection '${connectionName}'`)\n } catch (resetError) {\n logger.error(`Connection reset failed after login redirect for connection '${connectionName}':`, resetError)\n if (attempt === maxRetries) {\n throw new Error(`Connection reset failed after login redirect: ${resetError.message}`)\n }\n continue\n }\n continue\n } else {\n await safeReleasePageResource(pageResource, releasePage, logger)\n throw new Error(`Login redirect after ${maxRetries + 1} attempts: ${response.error}`)\n }\n }\n\n // 세션 타임아웃 관련 에러 체크\n if (!response.ok && isSessionTimeoutError(response.status)) {\n if (attempt < maxRetries) {\n logger.warn(\n `Session timeout detected (${response.status}), performing full connection reset... (${attempt + 1}/${maxRetries + 1})`\n )\n\n // CRITICAL: 커넥션 재연결 방식으로 완전한 초기화\n try {\n const resetResult = await performFullConnectionReset(\n domain,\n connectionName,\n logger,\n pageResource,\n releasePage\n )\n pageResource = resetResult.pageResource\n page = resetResult.page\n logger.info(`Full connection reset successful after session timeout for connection '${connectionName}'`)\n } catch (resetError) {\n logger.error(\n `Connection reset failed after session timeout for connection '${connectionName}':`,\n resetError\n )\n if (attempt === maxRetries) {\n throw new Error(`Connection reset failed after session timeout: ${resetError.message}`)\n }\n continue\n }\n continue\n } else {\n await safeReleasePageResource(pageResource, releasePage, logger)\n throw new Error(`Session timeout after ${maxRetries + 1} attempts: ${response.error}`)\n }\n }\n\n // 기타 HTTP 에러\n if (!response.ok) {\n throw new Error(response.error)\n }\n\n // 성공 시 페이지 릴리즈 후 결과 반환\n const result = {\n data: response.data,\n status: response.status,\n headers: response.headers\n }\n\n await safeReleasePageResource(pageResource, releasePage, logger)\n\n return result\n } catch (error) {\n lastError = error\n logger.error(`Headless request attempt ${attempt + 1} failed:`, error)\n\n await safeReleasePageResource(pageResource, releasePage, logger)\n page = null\n pageResource = null\n\n // 세션 관련 에러가 아니거나 마지막 재시도면 에러 발생\n if (!isRecoverableError(error) || attempt === maxRetries) {\n throw error\n }\n\n logger.info(`Retrying request... (${attempt + 2}/${maxRetries + 1})`)\n }\n }\n\n // 모든 재시도가 실패한 경우 - 혹시 남은 리소스가 있으면 정리\n if (pageResource) {\n await safeReleasePageResource(pageResource, releasePage, logger)\n }\n throw lastError || new Error('Request failed after all retry attempts')\n}\n\n/**\n * 세션 타임아웃 관련 HTTP 상태 코드인지 확인\n */\nfunction isSessionTimeoutError(statusCode: number): boolean {\n return [401, 403].includes(statusCode)\n}\n\n/**\n * 복구 가능한 에러인지 확인\n */\nfunction isRecoverableError(error: any): boolean {\n const errorMessage = error.message?.toLowerCase() || ''\n\n // 복구 가능한 에러 키워드 (네트워크, 세션/인증 관련)\n const recoverableErrorKeywords = [\n 'failed to fetch',\n 'network error',\n 'connection failed',\n 'connection reset',\n 'timeout',\n 'timed out',\n 'unauthorized',\n 'forbidden',\n 'session',\n 'authentication',\n 'login',\n 'expired',\n 'redirected to login' // 로그인 리디렉션 추가\n ]\n\n return recoverableErrorKeywords.some(keyword => errorMessage.includes(keyword))\n}\n\n/**\n * Safely release page resource with comprehensive error handling\n */\nexport async function safeReleasePageResource(pageResource: any, releasePage: Function, logger: any): Promise<void> {\n if (!pageResource) {\n return\n }\n\n try {\n // Handle different pageResource formats\n if (pageResource.page) {\n // This is a complex resource object {page, browser, requiresManualRelease}\n const { page, browser, requiresManualRelease } = pageResource\n\n if (requiresManualRelease && browser) {\n // Manual release required - close page first, then release browser\n try {\n if (page && !page.isClosed()) {\n await page.close()\n logger.info('Page closed during manual resource release')\n }\n } catch (closeError) {\n logger.error('Failed to close page during manual release:', closeError)\n }\n\n // Release browser back to pool\n try {\n const { getHeadlessPool } = require('../../resource-pool/headless-pool')\n const pool = getHeadlessPool()\n await pool.release(browser)\n logger.info('Browser manually released to pool')\n } catch (releaseError) {\n logger.error('Failed to manually release browser to pool:', releaseError)\n }\n } else {\n // Standard release through releasePage\n await releasePage(page)\n logger.info('Page released through standard releasePage method')\n }\n } else {\n // Simple page object - use standard release\n await releasePage(pageResource)\n logger.info('Simple page resource released')\n }\n } catch (error) {\n logger.error('Critical error during page resource release:', error)\n\n // Last resort: try to force close the page if it exists\n try {\n const page = pageResource.page || pageResource\n if (page && !page.isClosed()) {\n await page.close()\n logger.warn('Force closed page as last resort')\n }\n } catch (forceCloseError) {\n logger.error('Failed to force close page:', forceCloseError)\n }\n }\n}\n\n/**\n * 커넥션을 완전 재초기화하는 함수\n * 세션 만료 시 커넥션 재연결과 동일한 완전한 초기화를 수행\n */\nasync function performFullConnectionReset(\n domain: any,\n connectionName: string,\n logger: any,\n pageResource: any,\n releasePage: Function\n): Promise<{ pageResource: any; page: any }> {\n // 기존 페이지 리소스 해제\n await safeReleasePageResource(pageResource, releasePage, logger)\n\n // 커넥션 재연결 방식으로 완전한 초기화\n const connectionEntity = await ConnectionManager.getConnectionEntityByName(domain, connectionName)\n if (!connectionEntity) {\n throw new Error('Connection entity not found for reset')\n }\n\n // 기존 연결 완전 해제\n await connectionEntity.disconnect()\n // 새로운 연결 생성 (완전한 초기화)\n await connectionEntity.connect()\n\n // 새로운 connection 인스턴스 가져오기\n const newConnection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n if (!newConnection || !newConnection.acquireSessionPage) {\n throw new Error('Failed to acquire session page after connection reset')\n }\n\n const newSessionResult = await newConnection.acquireSessionPage()\n let newPageResource: any\n let newPage: any\n\n if (newSessionResult && typeof newSessionResult === 'object' && newSessionResult.page) {\n newPageResource = newSessionResult\n newPage = newSessionResult.page\n } else {\n newPage = newSessionResult\n newPageResource = { page: newPage, requiresManualRelease: false }\n }\n\n return { pageResource: newPageResource, page: newPage }\n}\n"]}
|
|
@@ -12,7 +12,7 @@ export * from './payload-log/payload-log';
|
|
|
12
12
|
export * from './state-register/state-register';
|
|
13
13
|
export declare const entities: any[];
|
|
14
14
|
export declare const schema: {
|
|
15
|
-
resolverClasses: (typeof import("./connection/connection-query").ConnectionQuery | typeof import("./connection/connection-mutation").ConnectionMutation | typeof import("./connection/connection-subscription").ConnectionSubscription | typeof import("./connector/connector-query").ConnectorQuery | typeof import("./scenario/scenario-query").ScenarioQuery | typeof import("./scenario/scenario-mutation").ScenarioMutation | typeof import("./scenario-instance/scenario-instance-query").ScenarioInstanceQuery | typeof import("./scenario-instance/scenario-instance-mutation").ScenarioInstanceMutation | typeof import("./scenario-instance/scenario-instance-subscription").ScenarioInstanceSubscription | typeof import("./scenario-queue/scenario-queue-subscription").ScenarioQueueSubscription | typeof import("./step/step-query").StepQuery | typeof import("./step/step-mutation").StepMutation | typeof import("./task-type/task-type-query").TaskTypeQuery | typeof import("./
|
|
15
|
+
resolverClasses: (typeof import("./connection/connection-query").ConnectionQuery | typeof import("./connection/connection-mutation").ConnectionMutation | typeof import("./connection/connection-subscription").ConnectionSubscription | typeof import("./connector/connector-query").ConnectorQuery | typeof import("./scenario/scenario-query").ScenarioQuery | typeof import("./scenario/scenario-mutation").ScenarioMutation | typeof import("./scenario-instance/scenario-instance-query").ScenarioInstanceQuery | typeof import("./scenario-instance/scenario-instance-mutation").ScenarioInstanceMutation | typeof import("./scenario-instance/scenario-instance-subscription").ScenarioInstanceSubscription | typeof import("./scenario-queue/scenario-queue-subscription").ScenarioQueueSubscription | typeof import("./step/step-query").StepQuery | typeof import("./step/step-mutation").StepMutation | typeof import("./task-type/task-type-query").TaskTypeQuery | typeof import("./payload-log/payload-log-query").PayloadLogQuery | typeof import("./payload-log/payload-log-mutation").PayloadLogMutation | typeof import("./state-register/state-register-query").StateRegisterQuery | typeof import("./state-register/state-register-mutation").StateRegisterMutation | typeof import("./state-register/data-resolver").DataResolver | typeof import("./analysis/analysis-query").IntegrationAnalysisQuery)[];
|
|
16
16
|
};
|
|
17
17
|
export { PayloadType } from './payload-log/payload-log';
|
|
18
18
|
export { createPayloadLog } from './payload-log/payload-log-mutation';
|