@things-factory/integration-base 9.2.20 → 9.2.22

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.
@@ -9,6 +9,7 @@ import './mssql-connector';
9
9
  import './oracle-connector';
10
10
  import './mysql-connector';
11
11
  import './socket-server';
12
+ import './stream-socket-server';
12
13
  import './operato-connector';
13
14
  import './headless-connector';
14
15
  import './pyrun-connector';
@@ -11,6 +11,7 @@ require("./mssql-connector");
11
11
  require("./oracle-connector");
12
12
  require("./mysql-connector");
13
13
  require("./socket-server");
14
+ require("./stream-socket-server");
14
15
  require("./operato-connector");
15
16
  require("./headless-connector");
16
17
  require("./pyrun-connector");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/engine/connector/index.ts"],"names":[],"mappings":";;AAAA,8BAA2B;AAC3B,iCAA8B;AAC9B,4BAAyB;AACzB,+BAA4B;AAC5B,8BAA2B;AAC3B,kCAA+B;AAC/B,4BAAyB;AACzB,6BAA0B;AAC1B,8BAA2B;AAC3B,6BAA0B;AAC1B,2BAAwB;AACxB,+BAA4B;AAC5B,gCAA6B;AAC7B,6BAA0B","sourcesContent":["import './echo-back-server'\nimport './echo-back-connector'\nimport './http-connector'\nimport './graphql-connector'\nimport './sqlite-connector'\nimport './postgresql-connector'\nimport './mqtt-connector'\nimport './mssql-connector'\nimport './oracle-connector'\nimport './mysql-connector'\nimport './socket-server'\nimport './operato-connector'\nimport './headless-connector'\nimport './pyrun-connector'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/engine/connector/index.ts"],"names":[],"mappings":";;AAAA,8BAA2B;AAC3B,iCAA8B;AAC9B,4BAAyB;AACzB,+BAA4B;AAC5B,8BAA2B;AAC3B,kCAA+B;AAC/B,4BAAyB;AACzB,6BAA0B;AAC1B,8BAA2B;AAC3B,6BAA0B;AAC1B,2BAAwB;AACxB,kCAA+B;AAC/B,+BAA4B;AAC5B,gCAA6B;AAC7B,6BAA0B","sourcesContent":["import './echo-back-server'\nimport './echo-back-connector'\nimport './http-connector'\nimport './graphql-connector'\nimport './sqlite-connector'\nimport './postgresql-connector'\nimport './mqtt-connector'\nimport './mssql-connector'\nimport './oracle-connector'\nimport './mysql-connector'\nimport './socket-server'\nimport './stream-socket-server'\nimport './operato-connector'\nimport './headless-connector'\nimport './pyrun-connector'\n"]}
@@ -0,0 +1,14 @@
1
+ import { Connector } from '../types';
2
+ import { InputConnection } from '../../service/connection/connection-type';
3
+ /**
4
+ * \r\n 구분자 기반 스트림 버퍼링 소켓 서버 커넥터.
5
+ * TCP 스트림 단편화(fragmentation)로 인해 메시지가 여러 청크로 나뉘어 수신될 때,
6
+ * \r\n이 도착할 때까지 버퍼에 누적한 뒤 완전한 메시지만 socket-message-arrive 이벤트로 emit한다.
7
+ */
8
+ export declare class StreamSocketServer implements Connector {
9
+ ready(connectionConfigs: InputConnection[]): Promise<void>;
10
+ connect(config: InputConnection): Promise<void>;
11
+ disconnect(connection: InputConnection): Promise<void>;
12
+ get parameterSpec(): any[];
13
+ get taskPrefixes(): string[];
14
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamSocketServer = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const net_1 = tslib_1.__importDefault(require("net"));
6
+ const events_1 = require("events");
7
+ const connection_manager_1 = require("../connection-manager");
8
+ /**
9
+ * \r\n 구분자 기반 스트림 버퍼링 소켓 서버 커넥터.
10
+ * TCP 스트림 단편화(fragmentation)로 인해 메시지가 여러 청크로 나뉘어 수신될 때,
11
+ * \r\n이 도착할 때까지 버퍼에 누적한 뒤 완전한 메시지만 socket-message-arrive 이벤트로 emit한다.
12
+ */
13
+ class StreamSocketServer {
14
+ async ready(connectionConfigs) {
15
+ await Promise.all(connectionConfigs.map(this.connect));
16
+ connection_manager_1.ConnectionManager.logger.info('stream socket servers are ready');
17
+ }
18
+ async connect(config) {
19
+ var em = new events_1.EventEmitter();
20
+ var [host = '0.0.0.0', port = 8124] = config.endpoint.split(':');
21
+ var clientIPs = new Set();
22
+ return new Promise((resolve, reject) => {
23
+ var server = net_1.default.createServer(tcp => {
24
+ const remoteIP = `${tcp.remoteAddress}:${tcp.remotePort}`;
25
+ clientIPs.add(remoteIP);
26
+ connection_manager_1.ConnectionManager.logger.info('Client connection: ');
27
+ connection_manager_1.ConnectionManager.logger.info(' local = %s:%s', tcp.localAddress, tcp.localPort);
28
+ connection_manager_1.ConnectionManager.logger.info(' remote = %s:%s', tcp.remoteAddress, tcp.remotePort);
29
+ console.log(`[stream-socket-server] ${config.name} clients: ${clientIPs.size}\n${[...clientIPs].sort().map(ip => ` - ${ip}`).join('\n')}`);
30
+ // TCP 스트림은 청크 단위로 수신되므로 \r\n 구분자가 올 때까지 버퍼링한다
31
+ let buffer = '';
32
+ tcp.on('data', (data) => {
33
+ em['__tcp__'] = tcp;
34
+ buffer += data.toString();
35
+ // 완전한 메시지(\r\n 또는 \n 종료)가 수신될 때마다 처리
36
+ let newlineIndex;
37
+ while ((newlineIndex = buffer.search(/\r\n|\n|\r/)) !== -1) {
38
+ const eolLength = buffer[newlineIndex] === '\r' && buffer[newlineIndex + 1] === '\n' ? 2 : 1;
39
+ let message = buffer.substring(0, newlineIndex);
40
+ buffer = buffer.substring(newlineIndex + eolLength);
41
+ message = message.replace('SB#', '');
42
+ if (!message)
43
+ continue;
44
+ // IP 정보를 함께 전달한다
45
+ em.emit('socket-message-arrive', message, tcp.remoteAddress);
46
+ connection_manager_1.ConnectionManager.logger.info(`stream-socket ${message}`);
47
+ tcp.write('ok');
48
+ }
49
+ });
50
+ tcp.on('close', hadError => {
51
+ connection_manager_1.ConnectionManager.logger.info('Client disconnected');
52
+ if (hadError) {
53
+ connection_manager_1.ConnectionManager.logger.info('Client disconnected with error: ' + hadError);
54
+ }
55
+ clientIPs.delete(remoteIP);
56
+ console.log(`[stream-socket-server] ${config.name} clients: ${clientIPs.size}\n${[...clientIPs].sort().map(ip => ` - ${ip}`).join('\n')}`);
57
+ });
58
+ tcp.on('error', ex => {
59
+ connection_manager_1.ConnectionManager.logger.info('Server socket error: ');
60
+ var logger = connection_manager_1.ConnectionManager.logger;
61
+ let message = ex.stack ? ex.stack : ex;
62
+ let msg = typeof message == 'object' ? JSON.stringify(message, null, 2) : message;
63
+ logger.error(msg, ex);
64
+ reject(ex);
65
+ });
66
+ });
67
+ server.listen(port, async () => {
68
+ connection_manager_1.ConnectionManager.logger.info(`stream tcp socket server listening on ${host}:${port}`);
69
+ try {
70
+ em['__server__'] = server;
71
+ connection_manager_1.ConnectionManager.addConnectionInstance(config, em);
72
+ connection_manager_1.ConnectionManager.logger.info(`stream socket server connection(${config.name}:${config.endpoint}) is connected`);
73
+ resolve();
74
+ }
75
+ catch (err) {
76
+ connection_manager_1.ConnectionManager.logger.error(err);
77
+ reject(err);
78
+ }
79
+ });
80
+ });
81
+ }
82
+ async disconnect(connection) {
83
+ let em = connection_manager_1.ConnectionManager.removeConnectionInstance(connection);
84
+ var tcp = em['__tcp__'];
85
+ var server = em['__server__'];
86
+ tcp && (await tcp.destroy());
87
+ server && (await server.close());
88
+ connection_manager_1.ConnectionManager.logger.info(`stream socket server connection(${connection.name}) is disconnected`);
89
+ }
90
+ get parameterSpec() {
91
+ return [];
92
+ }
93
+ get taskPrefixes() {
94
+ return ['socket'];
95
+ }
96
+ }
97
+ exports.StreamSocketServer = StreamSocketServer;
98
+ connection_manager_1.ConnectionManager.registerConnector('stream-socket-server', new StreamSocketServer());
99
+ //# sourceMappingURL=stream-socket-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-socket-server.js","sourceRoot":"","sources":["../../../server/engine/connector/stream-socket-server.ts"],"names":[],"mappings":";;;;AAAA,sDAAqB;AACrB,mCAAqC;AAGrC,8DAAyD;AAGzD;;;;GAIG;AACH,MAAa,kBAAkB;IAC7B,KAAK,CAAC,KAAK,CAAC,iBAAoC;QAC9C,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QACtD,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAA;IAClE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAuB;QACnC,IAAI,EAAE,GAAG,IAAI,qBAAY,EAAE,CAAA;QAC3B,IAAI,CAAC,IAAI,GAAG,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAChE,IAAI,SAAS,GAAG,IAAI,GAAG,EAAU,CAAA;QAEjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,MAAM,GAAG,aAAG,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE;gBAClC,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,UAAU,EAAE,CAAA;gBACzD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;gBACvB,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;gBACpD,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,SAAS,CAAC,CAAA;gBAClF,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,CAAA;gBACrF,OAAO,CAAC,GAAG,CACT,0BAA0B,MAAM,CAAC,IAAI,aAAa,SAAS,CAAC,IAAI,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/H,CAAA;gBAED,8CAA8C;gBAC9C,IAAI,MAAM,GAAG,EAAE,CAAA;gBACf,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAS,EAAE,EAAE;oBAC3B,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,CAAA;oBACnB,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAA;oBAEzB,qCAAqC;oBACrC,IAAI,YAAoB,CAAA;oBACxB,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;wBAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;wBAC5F,IAAI,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,YAAY,CAAC,CAAA;wBAC/C,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,YAAY,GAAG,SAAS,CAAC,CAAA;wBAEnD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;wBACpC,IAAI,CAAC,OAAO;4BAAE,SAAQ;wBAEtB,iBAAiB;wBACjB,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,CAAA;wBAC5D,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAA;wBACzD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBACjB,CAAC;gBACH,CAAC,CAAC,CAAA;gBAEF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE;oBACzB,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;oBACpD,IAAI,QAAQ,EAAE,CAAC;wBACb,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,kCAAkC,GAAG,QAAQ,CAAC,CAAA;oBAC9E,CAAC;oBACD,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;oBAC1B,OAAO,CAAC,GAAG,CACT,0BAA0B,MAAM,CAAC,IAAI,aAAa,SAAS,CAAC,IAAI,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/H,CAAA;gBACH,CAAC,CAAC,CAAA;gBAEF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE;oBACnB,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;oBACtD,IAAI,MAAM,GAAG,sCAAiB,CAAC,MAAM,CAAA;oBACrC,IAAI,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;oBACtC,IAAI,GAAG,GAAG,OAAO,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;oBACjF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;oBACrB,MAAM,CAAC,EAAE,CAAC,CAAA;gBACZ,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;YAEF,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;gBAC7B,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,IAAI,IAAI,IAAI,EAAE,CAAC,CAAA;gBAEtF,IAAI,CAAC;oBACH,EAAE,CAAC,YAAY,CAAC,GAAG,MAAM,CAAA;oBACzB,sCAAiB,CAAC,qBAAqB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;oBACnD,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAC3B,mCAAmC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,gBAAgB,CAClF,CAAA;oBACD,OAAO,EAAE,CAAA;gBACX,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,sCAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;oBACnC,MAAM,CAAC,GAAG,CAAC,CAAA;gBACb,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAA2B;QAC1C,IAAI,EAAE,GAAG,sCAAiB,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAA;QAC/D,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACvB,IAAI,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,CAAA;QAE7B,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;QAC5B,MAAM,IAAI,CAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;QAEhC,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,UAAU,CAAC,IAAI,mBAAmB,CAAC,CAAA;IACtG,CAAC;IAED,IAAI,aAAa;QACf,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,YAAY;QACd,OAAO,CAAC,QAAQ,CAAC,CAAA;IACnB,CAAC;CACF;AAtGD,gDAsGC;AAED,sCAAiB,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,IAAI,kBAAkB,EAAE,CAAC,CAAA","sourcesContent":["import net from 'net'\nimport { EventEmitter } from 'events'\n\nimport { Connector } from '../types'\nimport { ConnectionManager } from '../connection-manager'\nimport { InputConnection } from '../../service/connection/connection-type'\n\n/**\n * \\r\\n 구분자 기반 스트림 버퍼링 소켓 서버 커넥터.\n * TCP 스트림 단편화(fragmentation)로 인해 메시지가 여러 청크로 나뉘어 수신될 때,\n * \\r\\n이 도착할 때까지 버퍼에 누적한 뒤 완전한 메시지만 socket-message-arrive 이벤트로 emit한다.\n */\nexport class StreamSocketServer implements Connector {\n async ready(connectionConfigs: InputConnection[]) {\n await Promise.all(connectionConfigs.map(this.connect))\n ConnectionManager.logger.info('stream socket servers are ready')\n }\n\n async connect(config: InputConnection): Promise<void> {\n var em = new EventEmitter()\n var [host = '0.0.0.0', port = 8124] = config.endpoint.split(':')\n var clientIPs = new Set<string>()\n\n return new Promise((resolve, reject) => {\n var server = net.createServer(tcp => {\n const remoteIP = `${tcp.remoteAddress}:${tcp.remotePort}`\n clientIPs.add(remoteIP)\n ConnectionManager.logger.info('Client connection: ')\n ConnectionManager.logger.info(' local = %s:%s', tcp.localAddress, tcp.localPort)\n ConnectionManager.logger.info(' remote = %s:%s', tcp.remoteAddress, tcp.remotePort)\n console.log(\n `[stream-socket-server] ${config.name} clients: ${clientIPs.size}\\n${[...clientIPs].sort().map(ip => ` - ${ip}`).join('\\n')}`\n )\n\n // TCP 스트림은 청크 단위로 수신되므로 \\r\\n 구분자가 올 때까지 버퍼링한다\n let buffer = ''\n tcp.on('data', (data: any) => {\n em['__tcp__'] = tcp\n buffer += data.toString()\n\n // 완전한 메시지(\\r\\n 또는 \\n 종료)가 수신될 때마다 처리\n let newlineIndex: number\n while ((newlineIndex = buffer.search(/\\r\\n|\\n|\\r/)) !== -1) {\n const eolLength = buffer[newlineIndex] === '\\r' && buffer[newlineIndex + 1] === '\\n' ? 2 : 1\n let message = buffer.substring(0, newlineIndex)\n buffer = buffer.substring(newlineIndex + eolLength)\n\n message = message.replace('SB#', '')\n if (!message) continue\n\n // IP 정보를 함께 전달한다\n em.emit('socket-message-arrive', message, tcp.remoteAddress)\n ConnectionManager.logger.info(`stream-socket ${message}`)\n tcp.write('ok')\n }\n })\n\n tcp.on('close', hadError => {\n ConnectionManager.logger.info('Client disconnected')\n if (hadError) {\n ConnectionManager.logger.info('Client disconnected with error: ' + hadError)\n }\n clientIPs.delete(remoteIP)\n console.log(\n `[stream-socket-server] ${config.name} clients: ${clientIPs.size}\\n${[...clientIPs].sort().map(ip => ` - ${ip}`).join('\\n')}`\n )\n })\n\n tcp.on('error', ex => {\n ConnectionManager.logger.info('Server socket error: ')\n var logger = ConnectionManager.logger\n let message = ex.stack ? ex.stack : ex\n let msg = typeof message == 'object' ? JSON.stringify(message, null, 2) : message\n logger.error(msg, ex)\n reject(ex)\n })\n })\n\n server.listen(port, async () => {\n ConnectionManager.logger.info(`stream tcp socket server listening on ${host}:${port}`)\n\n try {\n em['__server__'] = server\n ConnectionManager.addConnectionInstance(config, em)\n ConnectionManager.logger.info(\n `stream socket server connection(${config.name}:${config.endpoint}) is connected`\n )\n resolve()\n } catch (err) {\n ConnectionManager.logger.error(err)\n reject(err)\n }\n })\n })\n }\n\n async disconnect(connection: InputConnection) {\n let em = ConnectionManager.removeConnectionInstance(connection)\n var tcp = em['__tcp__']\n var server = em['__server__']\n\n tcp && (await tcp.destroy())\n server && (await server.close())\n\n ConnectionManager.logger.info(`stream socket server connection(${connection.name}) is disconnected`)\n }\n\n get parameterSpec() {\n return []\n }\n\n get taskPrefixes() {\n return ['socket']\n }\n}\n\nConnectionManager.registerConnector('stream-socket-server', new StreamSocketServer())\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const task_registry_1 = require("../task-registry");
4
+ const connection_manager_1 = require("../connection-manager");
5
+ async function EchoSendHex(step, { logger, domain }) {
6
+ var { connection: connectionName, params: { message } } = step;
7
+ var connection = await connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
8
+ if (!connection) {
9
+ throw Error(`connection is not found : ${connectionName}`);
10
+ }
11
+ var data = await connection.write(Buffer.from(message, 'hex'));
12
+ return {
13
+ data
14
+ };
15
+ }
16
+ EchoSendHex.parameterSpec = [
17
+ {
18
+ type: 'string',
19
+ name: 'message',
20
+ label: 'message (hex)'
21
+ }
22
+ ];
23
+ task_registry_1.TaskRegistry.registerTaskHandler('echo-send-hex', EchoSendHex);
24
+ //# sourceMappingURL=echo-send-hex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"echo-send-hex.js","sourceRoot":"","sources":["../../../server/engine/task/echo-send-hex.ts"],"names":[],"mappings":";;AAAA,oDAA+C;AAC/C,8DAAyD;AAIzD,KAAK,UAAU,WAAW,CAAC,IAAe,EAAE,EAAE,MAAM,EAAE,MAAM,EAAW;IACrE,IAAI,EACF,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,EAAE,OAAO,EAAE,EACpB,GAAG,IAAI,CAAA;IAER,IAAI,UAAU,GAAG,MAAM,sCAAiB,CAAC,2BAA2B,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAC5F,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC,6BAA6B,cAAc,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,IAAI,IAAI,GAAG,MAAO,UAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;IAEvE,OAAO;QACL,IAAI;KACL,CAAA;AACH,CAAC;AAED,WAAW,CAAC,aAAa,GAAG;IAC1B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,eAAe;KACvB;CACF,CAAA;AAED,4BAAY,CAAC,mBAAmB,CAAC,eAAe,EAAE,WAAW,CAAC,CAAA","sourcesContent":["import { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\nimport { InputStep } from '../../service/step/step-type'\nimport { Context } from '../types'\n\nasync function EchoSendHex(step: InputStep, { logger, domain }: Context) {\n var {\n connection: connectionName,\n params: { message }\n } = step\n\n var connection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n if (!connection) {\n throw Error(`connection is not found : ${connectionName}`)\n }\n\n var data = await (connection as any).write(Buffer.from(message, 'hex'))\n\n return {\n data\n }\n}\n\nEchoSendHex.parameterSpec = [\n {\n type: 'string',\n name: 'message',\n label: 'message (hex)'\n }\n]\n\nTaskRegistry.registerTaskHandler('echo-send-hex', EchoSendHex)\n"]}
@@ -1,4 +1,5 @@
1
1
  import './echo-send';
2
+ import './echo-send-hex';
2
3
  import './echo-receive';
3
4
  import './sleep';
4
5
  import './log';
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  require("./echo-send");
4
+ require("./echo-send-hex");
4
5
  require("./echo-receive");
5
6
  require("./sleep");
6
7
  require("./log");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/engine/task/index.ts"],"names":[],"mappings":";;AAAA,uBAAoB;AACpB,0BAAuB;AACvB,mBAAgB;AAChB,iBAAc;AACd,qBAAkB;AAClB,sBAAmB;AACnB,uBAAoB;AACpB,2BAAwB;AACxB,4BAAyB;AACzB,iCAA8B;AAC9B,kCAA+B;AAC/B,0BAAuB;AACvB,2BAAwB;AACxB,8BAA2B;AAC3B,mCAAgC;AAChC,iCAA8B;AAC9B,yBAAsB;AACtB,kBAAe;AACf,iBAAc;AACd,yBAAsB;AACtB,+BAA4B;AAC5B,6BAA0B;AAC1B,mCAAgC;AAChC,wBAAqB;AACrB,8BAA2B;AAC3B,oBAAiB;AACjB,4BAAyB;AACzB,4BAAyB;AACzB,0BAAuB;AACvB,mBAAgB;AAChB,uBAAoB;AACpB,4BAAyB;AACzB,6BAA0B;AAC1B,oBAAiB;AACjB,0BAAuB;AACvB,yBAAsB;AACtB,0BAAuB;AACvB,2BAAwB;AACxB,0BAAuB;AACvB,4BAAyB;AACzB,6BAA0B;AAC1B,4BAAyB;AACzB,wBAAqB;AACrB,6BAA6B;AAC7B,8BAA2B;AAC3B,8BAA2B;AAC3B,qBAAkB;AAClB,wBAAqB;AACrB,yBAAsB;AACtB,8BAA2B;AAC3B,2BAAwB","sourcesContent":["import './echo-send'\nimport './echo-receive'\nimport './sleep'\nimport './log'\nimport './publish'\nimport './http-get'\nimport './http-post'\nimport './graphql-query'\nimport './graphql-mutate'\nimport './local-graphql-query'\nimport './local-graphql-mutate'\nimport './sub-scenario'\nimport './stop-scenario'\nimport './book-up-scenario'\nimport './pick-pending-scenario'\nimport './reset-pending-queue'\nimport './empty-check'\nimport './goto'\nimport './end'\nimport './switch-goto'\nimport './switch-range-goto'\nimport './switch-scenario'\nimport './switch-range-scenario'\nimport './switch-set'\nimport './switch-range-set'\nimport './script'\nimport './database-query'\nimport './mqtt-subscribe'\nimport './mqtt-publish'\nimport './throw'\nimport './variables'\nimport './floating-point'\nimport './socket-listener'\nimport './random'\nimport './csv-readline'\nimport './data-mapper'\nimport './headless-get'\nimport './headless-post'\nimport './headless-put'\nimport './headless-patch'\nimport './headless-delete'\nimport './headless-scrap'\nimport './set-domain'\n// import './mssql-procedure'\nimport './mssql-exec-query'\nimport './oracle-procedure'\nimport './jsonata'\nimport './state-read'\nimport './state-write'\nimport './state-group-read'\nimport './pyrun-execute'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/engine/task/index.ts"],"names":[],"mappings":";;AAAA,uBAAoB;AACpB,2BAAwB;AACxB,0BAAuB;AACvB,mBAAgB;AAChB,iBAAc;AACd,qBAAkB;AAClB,sBAAmB;AACnB,uBAAoB;AACpB,2BAAwB;AACxB,4BAAyB;AACzB,iCAA8B;AAC9B,kCAA+B;AAC/B,0BAAuB;AACvB,2BAAwB;AACxB,8BAA2B;AAC3B,mCAAgC;AAChC,iCAA8B;AAC9B,yBAAsB;AACtB,kBAAe;AACf,iBAAc;AACd,yBAAsB;AACtB,+BAA4B;AAC5B,6BAA0B;AAC1B,mCAAgC;AAChC,wBAAqB;AACrB,8BAA2B;AAC3B,oBAAiB;AACjB,4BAAyB;AACzB,4BAAyB;AACzB,0BAAuB;AACvB,mBAAgB;AAChB,uBAAoB;AACpB,4BAAyB;AACzB,6BAA0B;AAC1B,oBAAiB;AACjB,0BAAuB;AACvB,yBAAsB;AACtB,0BAAuB;AACvB,2BAAwB;AACxB,0BAAuB;AACvB,4BAAyB;AACzB,6BAA0B;AAC1B,4BAAyB;AACzB,wBAAqB;AACrB,6BAA6B;AAC7B,8BAA2B;AAC3B,8BAA2B;AAC3B,qBAAkB;AAClB,wBAAqB;AACrB,yBAAsB;AACtB,8BAA2B;AAC3B,2BAAwB","sourcesContent":["import './echo-send'\nimport './echo-send-hex'\nimport './echo-receive'\nimport './sleep'\nimport './log'\nimport './publish'\nimport './http-get'\nimport './http-post'\nimport './graphql-query'\nimport './graphql-mutate'\nimport './local-graphql-query'\nimport './local-graphql-mutate'\nimport './sub-scenario'\nimport './stop-scenario'\nimport './book-up-scenario'\nimport './pick-pending-scenario'\nimport './reset-pending-queue'\nimport './empty-check'\nimport './goto'\nimport './end'\nimport './switch-goto'\nimport './switch-range-goto'\nimport './switch-scenario'\nimport './switch-range-scenario'\nimport './switch-set'\nimport './switch-range-set'\nimport './script'\nimport './database-query'\nimport './mqtt-subscribe'\nimport './mqtt-publish'\nimport './throw'\nimport './variables'\nimport './floating-point'\nimport './socket-listener'\nimport './random'\nimport './csv-readline'\nimport './data-mapper'\nimport './headless-get'\nimport './headless-post'\nimport './headless-put'\nimport './headless-patch'\nimport './headless-delete'\nimport './headless-scrap'\nimport './set-domain'\n// import './mssql-procedure'\nimport './mssql-exec-query'\nimport './oracle-procedure'\nimport './jsonata'\nimport './state-read'\nimport './state-write'\nimport './state-group-read'\nimport './pyrun-execute'\n"]}
@@ -61,7 +61,12 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
61
61
  }
62
62
  // 세션 검증은 2번째 시도부터만 수행 (첫 번째는 새 페이지이므로 불필요)
63
63
  if (attempt > 0) {
64
+ // [디버그] validateSession 실행 전 상태 로깅
65
+ const preValidateUrl = page.url();
66
+ const preValidateCookies = await page.cookies();
67
+ logger.info(`[Debug] Pre-validateSession state — URL: ${preValidateUrl}, cookies: [${preValidateCookies.map((c) => `${c.name}(${c.domain})`).join(', ')}]`);
64
68
  const isSessionValid = await validateSession(page);
69
+ logger.info(`[Debug] validateSession result: ${isSessionValid}`);
65
70
  if (!isSessionValid) {
66
71
  logger.warn(`Session invalid for connection '${connectionName}', attempting full connection reset (attempt: ${attempt + 1})`);
67
72
  try {
@@ -232,7 +237,10 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
232
237
  // 로그인 리디렉션 감지 처리
233
238
  if (response.redirectedToLogin) {
234
239
  if (attempt < maxRetries) {
240
+ // [디버그] redirect 발생 시 쿠키 상태 확인
241
+ const redirectCookies = await page.cookies();
235
242
  logger.warn(`Login redirect detected: ${response.location}, performing full connection reset... (${attempt + 1}/${maxRetries + 1})`);
243
+ logger.info(`[Debug] Cookies at redirect: [${redirectCookies.map((c) => `${c.name}(${c.domain})`).join(', ')}]`);
236
244
  // CRITICAL: 커넥션 재연결 방식으로 완전한 초기화
237
245
  try {
238
246
  const resetResult = await performFullConnectionReset(domain, connectionName, logger, pageResource, releasePage);
@@ -247,7 +255,7 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
247
255
  // 페이지 상태 확인
248
256
  const resetUrl = page.url();
249
257
  const cookies = await page.cookies();
250
- logger.info(`[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length}`);
258
+ logger.info(`[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length} [${cookies.map((c) => `${c.name}(${c.domain})`).join(', ')}]`);
251
259
  // about:blank 체크
252
260
  if (resetUrl === 'about:blank') {
253
261
  throw new Error('Page stuck on about:blank after reset');
@@ -290,7 +298,7 @@ async function executeHeadlessRequestWithRecovery(connectionName, options, conte
290
298
  // 페이지 상태 확인
291
299
  const resetUrl = page.url();
292
300
  const cookies = await page.cookies();
293
- logger.info(`[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length}`);
301
+ logger.info(`[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length} [${cookies.map((c) => `${c.name}(${c.domain})`).join(', ')}]`);
294
302
  // about:blank 체크
295
303
  if (resetUrl === 'about:blank') {
296
304
  throw new Error('Page stuck on about:blank after reset');
@@ -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\n// 커넥션별 reset 진행 여부를 관리하는 Map (동시 호출 시 reset 중복 방지)\nconst resetLocks: Map<string, Promise<void>> = new Map()\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 // 요청 컨텍스트 로깅 (발전소 식별을 위한 데이터 포함)\n const contextInfo = [`Connection: ${connectionName}`, `Path: ${path}`]\n\n // 발전소 식별자 추출 (디버깅용)\n if (data) {\n const identifiers = ['ICUS', 'SEL_METER_ID', 'plantId', 'siteId', 'powerPlantId']\n identifiers.forEach(key => {\n if (data[key]) {\n contextInfo.push(`${key}: ${data[key]}`)\n }\n })\n }\n\n logger.info(`[Context] ${contextInfo.join(', ')}`)\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 if (attempt > 0) {\n const isSessionValid = await validateSession(page)\n\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\n // 재연결 후 페이지 완전 준비 대기\n await page\n .waitForFunction(() => document.readyState === 'complete', { timeout: 10000 })\n .catch(() => logger.warn('Page readyState timeout'))\n\n // JavaScript 초기화를 위한 추가 대기 (핵심 해결책)\n await new Promise(resolve => setTimeout(resolve, 2000))\n\n // 재연결 후 페이지가 올바른 도메인에 있는지 확인\n const resetPageUrl = page.url()\n if (!resetPageUrl.startsWith(targetDomain)) {\n logger.info(`Navigating reset page to target domain: ${targetDomain} (current: ${resetPageUrl})`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\n\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 logger.info(`[Fetch] Target: ${url.toString()}, Page: ${page.url()}`)\n\n const response = await page.evaluate(\n async (urlString, opts, loginPagePath) => {\n try {\n // 페이지 상태 검증 (about:blank 또는 로딩 중인 페이지에서 fetch 방지)\n const pageLocation = window.location.href\n const pageReadyState = document.readyState\n\n if (pageLocation === 'about:blank' || pageReadyState !== 'complete') {\n return {\n ok: false,\n status: 0,\n statusText: 'Page Not Ready',\n headers: {},\n error: `Page state invalid - location: ${pageLocation}, readyState: ${pageReadyState}`,\n data: null,\n networkError: true\n }\n }\n\n const response = await fetch(urlString, opts)\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 // 리디렉션 감지 - 로그인 페이지로의 리디렉션 체크\n // 브라우저 Fetch API에서 redirect: 'manual'은 opaque response(status=0)를 반환하므로\n // redirect: 'follow'(기본값)를 사용하고 response.redirected로 감지\n if (response.redirected) {\n const finalUrl = response.url || ''\n if (\n finalUrl.includes(loginPagePath) ||\n finalUrl.includes('/login') ||\n finalUrl.includes('/signin') ||\n finalUrl.includes('/auth')\n ) {\n return {\n ...result,\n error: `Redirected to login page: ${finalUrl}`,\n redirectedToLogin: true,\n location: finalUrl,\n data: null\n }\n }\n }\n\n // status가 0인 경우는 네트워크 레벨 에러 (CORS, 연결 실패 등)\n if (response.status === 0) {\n return {\n ok: false,\n status: 0,\n statusText: response.statusText || 'Network Error',\n headers: {},\n error: 'HTTP 0 - 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 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\n // 재연결 후 페이지 완전 준비 대기\n await page\n .waitForFunction(() => document.readyState === 'complete', { timeout: 10000 })\n .catch(() => logger.warn('Page readyState timeout'))\n\n // JavaScript 초기화를 위한 추가 대기\n await new Promise(resolve => setTimeout(resolve, 2000))\n\n // 페이지 상태 확인\n const resetUrl = page.url()\n const cookies = await page.cookies()\n\n logger.info(`[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length}`)\n\n // about:blank 체크\n if (resetUrl === 'about:blank') {\n throw new Error('Page stuck on about:blank after reset')\n }\n\n // 재연결 후 페이지가 올바른 도메인에 있는지 확인\n if (!resetUrl.startsWith(targetDomain)) {\n logger.info(`Navigating reset page to target domain: ${targetDomain} (current: ${resetUrl})`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\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\n // 재연결 후 페이지 완전 준비 대기\n await page\n .waitForFunction(() => document.readyState === 'complete', { timeout: 10000 })\n .catch(() => logger.warn('Page readyState timeout'))\n\n // JavaScript 초기화를 위한 추가 대기\n await new Promise(resolve => setTimeout(resolve, 2000))\n\n // 페이지 상태 확인\n const resetUrl = page.url()\n const cookies = await page.cookies()\n\n logger.info(`[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length}`)\n\n // about:blank 체크\n if (resetUrl === 'about:blank') {\n throw new Error('Page stuck on about:blank after reset')\n }\n\n // 재연결 후 페이지가 올바른 도메인에 있는지 확인\n if (!resetUrl.startsWith(targetDomain)) {\n logger.info(`Navigating reset page to target domain: ${targetDomain} (current: ${resetUrl})`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\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 // 풀이 drain 중인 경우 재연결 완료까지 대기\n if (error.message?.toLowerCase().includes('draining')) {\n logger.info('Pool is draining, waiting for reconnection...')\n await new Promise(resolve => setTimeout(resolve, 5000))\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 'draining', // 브라우저 풀 drain 중 (커넥션 재연결 과도기)\n 'cannot accept work' // generic-pool drain 상태\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 * 동시 호출 시 먼저 시작된 reset이 완료될 때까지 대기 (pool draining 충돌 방지)\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 // 이미 같은 커넥션에 대한 reset이 진행 중이면 완료될 때까지 대기\n const existingLock = resetLocks.get(connectionName)\n if (existingLock) {\n logger.info(`Connection '${connectionName}' reset already in progress, waiting...`)\n await existingLock.catch(() => {}) // 선행 reset 실패해도 대기는 완료\n }\n\n let resolveLock!: () => void\n const lock = new Promise<void>(resolve => {\n resolveLock = resolve\n })\n resetLocks.set(connectionName, lock)\n\n try {\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 // Connector를 직접 사용하여 연결 재설정\n const { type, edge } = connectionEntity\n const ProxyConnector = require('../../connector/proxy-connector').ProxyConnector\n const connector = edge ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n\n if (!connector) {\n throw new Error(`Connector not found for type: ${type}`)\n }\n\n // 기존 연결 완전 해제\n await connector.disconnect(connectionEntity)\n logger.info(`Connection '${connectionName}' disconnected for reset`)\n\n // 브라우저 완전 종료 및 리소스 정리 대기 (중요!)\n // 수동 재연결 시에는 자연스럽게 시간이 지나가지만,\n // 코드에서는 즉시 연결을 시도하므로 명시적 대기 필요\n await new Promise(resolve => setTimeout(resolve, 3000))\n\n // 새로운 연결 생성 (완전한 초기화)\n await connector.connect(connectionEntity)\n logger.info(`Connection '${connectionName}' reconnected after reset`)\n\n // 새 브라우저 인스턴스 초기화 대기\n await new Promise(resolve => setTimeout(resolve, 2000))\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 // 새 페이지 안정화 대기 (핵심!)\n await new Promise(resolve => setTimeout(resolve, 1000))\n\n return { pageResource: newPageResource, page: newPage }\n } finally {\n // reset 완료 시 락 해제 (성공/실패 무관하게 항상 해제)\n resolveLock()\n resetLocks.delete(connectionName)\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\n// 커넥션별 reset 진행 여부를 관리하는 Map (동시 호출 시 reset 중복 방지)\nconst resetLocks: Map<string, Promise<void>> = new Map()\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 // 요청 컨텍스트 로깅 (발전소 식별을 위한 데이터 포함)\n const contextInfo = [`Connection: ${connectionName}`, `Path: ${path}`]\n\n // 발전소 식별자 추출 (디버깅용)\n if (data) {\n const identifiers = ['ICUS', 'SEL_METER_ID', 'plantId', 'siteId', 'powerPlantId']\n identifiers.forEach(key => {\n if (data[key]) {\n contextInfo.push(`${key}: ${data[key]}`)\n }\n })\n }\n\n logger.info(`[Context] ${contextInfo.join(', ')}`)\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 if (attempt > 0) {\n // [디버그] validateSession 실행 전 상태 로깅\n const preValidateUrl = page.url()\n const preValidateCookies = await page.cookies()\n logger.info(\n `[Debug] Pre-validateSession state — URL: ${preValidateUrl}, cookies: [${preValidateCookies.map((c: { name: string; domain: string }) => `${c.name}(${c.domain})`).join(', ')}]`\n )\n\n const isSessionValid = await validateSession(page)\n logger.info(`[Debug] validateSession result: ${isSessionValid}`)\n\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\n // 재연결 후 페이지 완전 준비 대기\n await page\n .waitForFunction(() => document.readyState === 'complete', { timeout: 10000 })\n .catch(() => logger.warn('Page readyState timeout'))\n\n // JavaScript 초기화를 위한 추가 대기 (핵심 해결책)\n await new Promise(resolve => setTimeout(resolve, 2000))\n\n // 재연결 후 페이지가 올바른 도메인에 있는지 확인\n const resetPageUrl = page.url()\n if (!resetPageUrl.startsWith(targetDomain)) {\n logger.info(`Navigating reset page to target domain: ${targetDomain} (current: ${resetPageUrl})`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\n\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 logger.info(`[Fetch] Target: ${url.toString()}, Page: ${page.url()}`)\n\n const response = await page.evaluate(\n async (urlString, opts, loginPagePath) => {\n try {\n // 페이지 상태 검증 (about:blank 또는 로딩 중인 페이지에서 fetch 방지)\n const pageLocation = window.location.href\n const pageReadyState = document.readyState\n\n if (pageLocation === 'about:blank' || pageReadyState !== 'complete') {\n return {\n ok: false,\n status: 0,\n statusText: 'Page Not Ready',\n headers: {},\n error: `Page state invalid - location: ${pageLocation}, readyState: ${pageReadyState}`,\n data: null,\n networkError: true\n }\n }\n\n const response = await fetch(urlString, opts)\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 // 리디렉션 감지 - 로그인 페이지로의 리디렉션 체크\n // 브라우저 Fetch API에서 redirect: 'manual'은 opaque response(status=0)를 반환하므로\n // redirect: 'follow'(기본값)를 사용하고 response.redirected로 감지\n if (response.redirected) {\n const finalUrl = response.url || ''\n if (\n finalUrl.includes(loginPagePath) ||\n finalUrl.includes('/login') ||\n finalUrl.includes('/signin') ||\n finalUrl.includes('/auth')\n ) {\n return {\n ...result,\n error: `Redirected to login page: ${finalUrl}`,\n redirectedToLogin: true,\n location: finalUrl,\n data: null\n }\n }\n }\n\n // status가 0인 경우는 네트워크 레벨 에러 (CORS, 연결 실패 등)\n if (response.status === 0) {\n return {\n ok: false,\n status: 0,\n statusText: response.statusText || 'Network Error',\n headers: {},\n error: 'HTTP 0 - 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 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 // [디버그] redirect 발생 시 쿠키 상태 확인\n const redirectCookies = await page.cookies()\n logger.warn(\n `Login redirect detected: ${response.location}, performing full connection reset... (${attempt + 1}/${maxRetries + 1})`\n )\n logger.info(\n `[Debug] Cookies at redirect: [${redirectCookies.map((c: { name: string; domain: string }) => `${c.name}(${c.domain})`).join(', ')}]`\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\n // 재연결 후 페이지 완전 준비 대기\n await page\n .waitForFunction(() => document.readyState === 'complete', { timeout: 10000 })\n .catch(() => logger.warn('Page readyState timeout'))\n\n // JavaScript 초기화를 위한 추가 대기\n await new Promise(resolve => setTimeout(resolve, 2000))\n\n // 페이지 상태 확인\n const resetUrl = page.url()\n const cookies = await page.cookies()\n\n logger.info(\n `[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length} [${cookies.map((c: { name: string; domain: string }) => `${c.name}(${c.domain})`).join(', ')}]`\n )\n\n // about:blank 체크\n if (resetUrl === 'about:blank') {\n throw new Error('Page stuck on about:blank after reset')\n }\n\n // 재연결 후 페이지가 올바른 도메인에 있는지 확인\n if (!resetUrl.startsWith(targetDomain)) {\n logger.info(`Navigating reset page to target domain: ${targetDomain} (current: ${resetUrl})`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\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\n // 재연결 후 페이지 완전 준비 대기\n await page\n .waitForFunction(() => document.readyState === 'complete', { timeout: 10000 })\n .catch(() => logger.warn('Page readyState timeout'))\n\n // JavaScript 초기화를 위한 추가 대기\n await new Promise(resolve => setTimeout(resolve, 2000))\n\n // 페이지 상태 확인\n const resetUrl = page.url()\n const cookies = await page.cookies()\n\n logger.info(\n `[Reset Success] URL: ${resetUrl}, Cookies: ${cookies.length} [${cookies.map((c: { name: string; domain: string }) => `${c.name}(${c.domain})`).join(', ')}]`\n )\n\n // about:blank 체크\n if (resetUrl === 'about:blank') {\n throw new Error('Page stuck on about:blank after reset')\n }\n\n // 재연결 후 페이지가 올바른 도메인에 있는지 확인\n if (!resetUrl.startsWith(targetDomain)) {\n logger.info(`Navigating reset page to target domain: ${targetDomain} (current: ${resetUrl})`)\n await page.goto(targetDomain, { waitUntil: 'networkidle2' })\n }\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 // 풀이 drain 중인 경우 재연결 완료까지 대기\n if (error.message?.toLowerCase().includes('draining')) {\n logger.info('Pool is draining, waiting for reconnection...')\n await new Promise(resolve => setTimeout(resolve, 5000))\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 'draining', // 브라우저 풀 drain 중 (커넥션 재연결 과도기)\n 'cannot accept work' // generic-pool drain 상태\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 * 동시 호출 시 먼저 시작된 reset이 완료될 때까지 대기 (pool draining 충돌 방지)\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 // 이미 같은 커넥션에 대한 reset이 진행 중이면 완료될 때까지 대기\n const existingLock = resetLocks.get(connectionName)\n if (existingLock) {\n logger.info(`Connection '${connectionName}' reset already in progress, waiting...`)\n await existingLock.catch(() => {}) // 선행 reset 실패해도 대기는 완료\n }\n\n let resolveLock!: () => void\n const lock = new Promise<void>(resolve => {\n resolveLock = resolve\n })\n resetLocks.set(connectionName, lock)\n\n try {\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 // Connector를 직접 사용하여 연결 재설정\n const { type, edge } = connectionEntity\n const ProxyConnector = require('../../connector/proxy-connector').ProxyConnector\n const connector = edge ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n\n if (!connector) {\n throw new Error(`Connector not found for type: ${type}`)\n }\n\n // 기존 연결 완전 해제\n await connector.disconnect(connectionEntity)\n logger.info(`Connection '${connectionName}' disconnected for reset`)\n\n // 브라우저 완전 종료 및 리소스 정리 대기 (중요!)\n // 수동 재연결 시에는 자연스럽게 시간이 지나가지만,\n // 코드에서는 즉시 연결을 시도하므로 명시적 대기 필요\n await new Promise(resolve => setTimeout(resolve, 3000))\n\n // 새로운 연결 생성 (완전한 초기화)\n await connector.connect(connectionEntity)\n logger.info(`Connection '${connectionName}' reconnected after reset`)\n\n // 새 브라우저 인스턴스 초기화 대기\n await new Promise(resolve => setTimeout(resolve, 2000))\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 // 새 페이지 안정화 대기 (핵심!)\n await new Promise(resolve => setTimeout(resolve, 1000))\n\n return { pageResource: newPageResource, page: newPage }\n } finally {\n // reset 완료 시 락 해제 (성공/실패 무관하게 항상 해제)\n resolveLock()\n resetLocks.delete(connectionName)\n }\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("./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)[];
15
+ resolverClasses: (typeof import("./task-type/task-type-query").TaskTypeQuery | typeof import("./connector/connector-query").ConnectorQuery | typeof import("./connection/connection-query").ConnectionQuery | typeof import("./connection/connection-mutation").ConnectionMutation | typeof import("./connection/connection-subscription").ConnectionSubscription | 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("./payload-log/payload-log-query").PayloadLogQuery | typeof import("./payload-log/payload-log-mutation").PayloadLogMutation | typeof import("./analysis/analysis-query").IntegrationAnalysisQuery | typeof import("./state-register/state-register-query").StateRegisterQuery | typeof import("./state-register/state-register-mutation").StateRegisterMutation | typeof import("./state-register/data-resolver").DataResolver)[];
16
16
  };
17
17
  export { PayloadType } from './payload-log/payload-log';
18
18
  export { createPayloadLog } from './payload-log/payload-log-mutation';