@things-factory/integration-headless 8.0.5 → 9.0.0-beta.12

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.
Files changed (35) hide show
  1. package/dist-server/engine/index.d.ts +0 -1
  2. package/dist-server/engine/index.js +0 -1
  3. package/dist-server/engine/index.js.map +1 -1
  4. package/dist-server/engine/task/pdf-capture-util.d.ts +1 -1
  5. package/dist-server/engine/task/pdf-capture-util.js +3 -3
  6. package/dist-server/engine/task/pdf-capture-util.js.map +1 -1
  7. package/dist-server/tsconfig.tsbuildinfo +1 -1
  8. package/helps/integration/connector/headless-connector.ja.md +183 -31
  9. package/helps/integration/connector/headless-connector.ko.md +177 -32
  10. package/helps/integration/connector/headless-connector.md +178 -31
  11. package/helps/integration/connector/headless-connector.ms.md +180 -32
  12. package/helps/integration/connector/headless-connector.zh.md +178 -31
  13. package/package.json +6 -6
  14. package/dist-server/engine/connector/headless-connector.d.ts +0 -14
  15. package/dist-server/engine/connector/headless-connector.js +0 -54
  16. package/dist-server/engine/connector/headless-connector.js.map +0 -1
  17. package/dist-server/engine/connector/headless-pool.d.ts +0 -3
  18. package/dist-server/engine/connector/headless-pool.js +0 -63
  19. package/dist-server/engine/connector/headless-pool.js.map +0 -1
  20. package/dist-server/engine/connector/index.d.ts +0 -1
  21. package/dist-server/engine/connector/index.js +0 -4
  22. package/dist-server/engine/connector/index.js.map +0 -1
  23. package/server/engine/connector/headless-connector.ts +0 -68
  24. package/server/engine/connector/headless-pool.ts +0 -69
  25. package/server/engine/connector/index.ts +0 -1
  26. package/server/engine/index.ts +0 -2
  27. package/server/engine/task/headless-pdf-capture-board.ts +0 -182
  28. package/server/engine/task/headless-pdf-capture-markdown.ts +0 -47
  29. package/server/engine/task/headless-pdf-capture.ts +0 -39
  30. package/server/engine/task/headless-pdf-open.ts +0 -98
  31. package/server/engine/task/headless-pdf-save.ts +0 -88
  32. package/server/engine/task/index.ts +0 -9
  33. package/server/engine/task/pdf-capture-util.ts +0 -331
  34. package/server/index.ts +0 -3
  35. package/server/tsconfig.json +0 -10
@@ -1,57 +1,168 @@
1
- ### 无头浏览器池连接器 (Headless Pool Connector)
1
+ ### 🏆 无头连接器 (Headless Connector)
2
2
 
3
- **无头浏览器池连接器**旨在管理无头浏览器实例的池。该连接器在执行需要多个无头浏览器会话同时运行的任务时非常有用,例如生成PDF或进行网页抓取。它能够有效地管理这些无头浏览器实例的创建、使用和销毁。
3
+ **无头连接器** 利用无头浏览器执行 **自动登录**、**网页爬取**、**PDF 生成**、**表单提交**等任务。
4
+ 它可以管理多个浏览器会话,支持会话保持、Cookie 处理、基于 API 的登录等功能。
4
5
 
5
- #### 端点
6
+ ---
6
7
 
7
- `无头浏览器池连接器`不要求特定的端点,但可以作为连接设置的一部分提供URI。此URI通常作为占位符使用,池在内部处理实际连接。也就是说,端点可以输入任意字符串。
8
+ ## 🚀 1. 端点(Endpoint)
8
9
 
9
- #### 参数
10
+ 无头连接器的端点需要填写 **目标网页的 URL**。
11
+ 例如,可以输入 `https://example.com` 以抓取该网站的内容,或者使用 `localhost` 生成 PDF。
10
12
 
11
- - **min** (数字):
12
- - 指定池中维持的最少无头浏览器实例数量。
13
- - 默认值: `2`
14
- - **max** (数字):
15
- - 指定池中可以处理的最大无头浏览器实例数量。
16
- - 默认值: `10`
13
+ ---
17
14
 
18
- 这些参数允许根据预期的工作负载调整池的大小。例如,如果应用程序经常同时需要多个浏览器实例,则可以增加`max`值。
15
+ ## 🔧 2. 参数(Parameters)
19
16
 
20
- #### 设置示例
17
+ ### ✅ 登录相关配置
18
+
19
+ | 参数 | 类型 | 描述 | 默认值 |
20
+ | -------------------- | -------- | -------------------------------------------- | ------------- |
21
+ | `username` | `string` | 登录用户名(账户名) | `""` |
22
+ | `password` | `string` | 登录密码 | `""` |
23
+ | `loginPagePath` | `string` | 登录页面路径(如 `/login`) | `"/login"` |
24
+ | `loginApiUrl` | `string` | 登录 API 端点(如果没有则 `null`) | `null` |
25
+ | `usernameSelector` | `string` | 登录表单的用户名输入框 CSS 选择器 | `"#username"` |
26
+ | `passwordSelector` | `string` | 登录表单的密码输入框 CSS 选择器 | `"#password"` |
27
+ | `submitSelector` | `string` | 登录按钮的 CSS 选择器 | `"#submit"` |
28
+ | `successSelector` | `string` | 用于判断登录成功的元素 CSS 选择器 | `null` |
29
+ | `shadowDomSelectors` | `string` | 处理 Shadow DOM 内部元素的选择器(逗号分隔) | `""` |
30
+ | `timeout` | `number` | 登录及页面加载的超时时间(毫秒) | `15000` |
31
+ | `retries` | `number` | 登录重试次数 | `3` |
32
+
33
+ ---
34
+
35
+ ## 🔍 3. `loginApiUrl` 参数的详细说明
36
+
37
+ ### 🔹 `loginApiUrl` 是什么?
38
+
39
+ `loginApiUrl` 是指用于网站 **API 登录** 的接口地址。如果网站不使用 API 方式登录,则不需要设置此参数。
40
+
41
+ ### ✅ `loginApiUrl` 为空 (`null`)
42
+
43
+ 此时 **使用 HTML 表单提交的方式进行登录**。
44
+ 无头浏览器会直接模拟用户操作,如填写用户名和密码,并点击提交按钮。
45
+
46
+ #### 📌 示例(HTML 表单登录)
21
47
 
22
48
  ```json
23
49
  {
24
- "name": "headless-browser-pool",
50
+ "endpoint": "https://example.com",
51
+ "params": {
52
+ "username": "user123",
53
+ "password": "securepassword",
54
+ "loginPagePath": "/login",
55
+ "usernameSelector": "#username",
56
+ "passwordSelector": "#password",
57
+ "submitSelector": "#submit-button",
58
+ "successSelector": ".dashboard"
59
+ }
60
+ }
61
+ ```
62
+
63
+ #### 🛠 处理过程:
64
+
65
+ 1. 访问 `https://example.com/login`
66
+ 2. 在 `#username` 和 `#password` 输入框中填入账户信息
67
+ 3. 点击 `#submit-button` 提交表单
68
+ 4. 通过 `.dashboard` 元素是否存在来判断是否登录成功
69
+
70
+ ---
71
+
72
+ ### ✅ `loginApiUrl` 设定时
73
+
74
+ 如果目标网站使用 **API 方式登录**,则 `loginApiUrl` 需要设置为该 API 的地址。
75
+ 例如,如果 API 登录地址是 `/api/auth/login`,则需要手动设置该参数。
76
+
77
+ #### 📌 示例(API 登录方式)
78
+
79
+ ```json
80
+ {
81
+ "endpoint": "https://example.com",
82
+ "params": {
83
+ "username": "user123",
84
+ "password": "securepassword",
85
+ "loginPagePath": "/login",
86
+ "loginApiUrl": "/api/auth/login",
87
+ "usernameSelector": "#username",
88
+ "passwordSelector": "#password",
89
+ "submitSelector": "#submit-button",
90
+ "successSelector": ".dashboard"
91
+ }
92
+ }
93
+ ```
94
+
95
+ #### 🛠 处理过程:
96
+
97
+ 1. 发送 **HTTP POST 请求** 到 `https://example.com/api/auth/login`
98
+ 2. 服务器返回成功响应,浏览器存储会话 Cookie
99
+ 3. 访问主页面并检查 `.dashboard` 是否存在,以确认登录成功
100
+
101
+ ---
102
+
103
+ ## 🏗 4. 配置示例
104
+
105
+ ```json
106
+ {
107
+ "name": "headless-browser-connector",
25
108
  "connector": "headless-connector",
26
- "endpoint": "1",
109
+ "endpoint": "https://example.com",
27
110
  "params": {
28
- "min": 5,
29
- "max": 20
111
+ "username": "user123",
112
+ "password": "securepassword",
113
+ "loginPagePath": "/login",
114
+ "loginApiUrl": "/api/auth/login",
115
+ "usernameSelector": "#username",
116
+ "passwordSelector": "#password",
117
+ "submitSelector": "#submit",
118
+ "successSelector": ".dashboard",
119
+ "timeout": 20000,
120
+ "retries": 5
30
121
  }
31
122
  }
32
123
  ```
33
124
 
34
- 在上述示例中,池至少保持5个无头浏览器实例,并且在需要时可以扩展到最多20个实例。
125
+ ---
126
+
127
+ ## 🔄 5. 连接生命周期(Connection Lifecycle)
128
+
129
+ - **connect**
35
130
 
36
- #### 连接生命周期
131
+ - 根据参数执行登录流程,并保持会话。
132
+ - 存储 Cookie,以便后续请求使用。
37
133
 
38
- - **connect**:
134
+ - **disconnect**
135
+ - 关闭浏览器会话,并清除 Cookie。
39
136
 
40
- - 初始化连接,根据设定的参数(`min` 和 `max`)创建无头浏览器实例池。
41
- - 池会自动管理这些实例的生命周期,必要时创建新的实例并重用现有的实例。
137
+ ---
42
138
 
43
- - **disconnect**:
44
- - 销毁无头浏览器池,确保所有实例都被适当关闭并释放资源。
139
+ ## ⚡ 6. 支持的任务(Supported Tasks)
45
140
 
46
- #### 使用场景
141
+ | 任务类型 | 描述 |
142
+ | ---------------------- | ---------------------- |
143
+ | `headless-pdf-capture` | 将 HTML 转换为 PDF |
144
+ | `headless-scrap` | 网页爬取,提取特定数据 |
145
+ | `headless-post` | 自动填写表单并提交 |
47
146
 
48
- `无头浏览器池连接器`主要用于需要在无头浏览器环境中执行任务的场景。例如,生成HTML内容的PDF、捕获网页截图或从网站抓取数据。
147
+ #### 📌 任务示例
49
148
 
50
- #### 支持的任务
149
+ ### 1️⃣ 网页爬取(`headless-scrap`)
51
150
 
52
- `无头浏览器池连接器`支持以 `headless-pdf` 为前缀的任务。这些任务将使用由此连接器管理的无头浏览器实例。
151
+ ```json
152
+ {
153
+ "name": "scrape-data",
154
+ "taskType": "headless-scrap",
155
+ "params": {
156
+ "path": "/products",
157
+ "selectors": [
158
+ { "text": "商品名称", "value": ".product-title" },
159
+ { "text": "价格", "value": ".product-price" }
160
+ ]
161
+ }
162
+ }
163
+ ```
53
164
 
54
- #### 任务示例使用
165
+ ### 2️⃣ PDF 生成(`headless-pdf-capture`)
55
166
 
56
167
  ```json
57
168
  {
@@ -66,6 +177,42 @@
66
177
  }
67
178
  ```
68
179
 
69
- #### 说明
180
+ ### 3️⃣ 表单提交(`headless-post`)
181
+
182
+ ```json
183
+ {
184
+ "name": "submit-form",
185
+ "taskType": "headless-post",
186
+ "params": {
187
+ "formPath": "/contact",
188
+ "fields": {
189
+ "#name": "张三",
190
+ "#email": "zhangsan@example.com",
191
+ "#message": "你好,我想咨询一下!"
192
+ },
193
+ "submitSelector": "#submit-button"
194
+ }
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## 📢 7. 说明(Description)
201
+
202
+ **无头连接器** 可用于 **自动化网页爬取、PDF 生成、表单提交** 等任务。
203
+ 它支持 **HTML 登录** 和 **API 登录**,适用于各种不同的网页登录方式。
204
+
205
+ ---
206
+
207
+ ### 📌 功能总结
208
+
209
+ | 功能 | 说明 |
210
+ | ----------------- | ------------------------------------ |
211
+ | **HTML 表单登录** | 直接模拟用户操作(适用于大部分网页) |
212
+ | **API 登录** | 通过 API 发送登录请求(更快) |
213
+ | **会话保持** | 存储 Cookie 并维持登录状态 |
214
+ | **网页爬取** | 提取页面中的特定数据 |
215
+ | **PDF 生成** | 将 HTML 页面转换为 PDF |
216
+ | **表单提交** | 自动填充并提交表单 |
70
217
 
71
- **无头浏览器池连接器**通过高效管理无头浏览器实例池,确保在高需求场景下执行PDF生成和网页抓取等任务时,资源利用的优化和可扩展性。
218
+ 📌 **通过无头连接器,你可以轻松地在不同网站上实现自动化操作!** 🚀
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/integration-headless",
3
- "version": "8.0.5",
3
+ "version": "9.0.0-beta.12",
4
4
  "main": "dist-server/index.js",
5
5
  "things-factory": true,
6
6
  "author": "heartyoh <heartyoh@hatiolab.com>",
@@ -22,12 +22,12 @@
22
22
  "clean": "npm run clean:server"
23
23
  },
24
24
  "dependencies": {
25
- "@things-factory/attachment-base": "^8.0.5",
26
- "@things-factory/board-service": "^8.0.5",
27
- "@things-factory/integration-base": "^8.0.5",
28
- "@things-factory/shell": "^8.0.2",
25
+ "@things-factory/attachment-base": "^9.0.0-beta.12",
26
+ "@things-factory/board-service": "^9.0.0-beta.12",
27
+ "@things-factory/integration-base": "^9.0.0-beta.12",
28
+ "@things-factory/shell": "^9.0.0-beta.12",
29
29
  "ejs": "^3.1.10",
30
30
  "pdf-lib": "^1.17.1"
31
31
  },
32
- "gitHead": "9ab6fca18eeb58b9d2f3411661508a39d0ab6aee"
32
+ "gitHead": "5e9ade1c2d4b4c96b89396e36c3afa1caaf18ef0"
33
33
  }
@@ -1,14 +0,0 @@
1
- import { Connector } from '@things-factory/integration-base';
2
- export declare class HeadlessConnector implements Connector {
3
- ready(connectionConfigs: any): Promise<void>;
4
- connect(connection: any): Promise<void>;
5
- disconnect(connection: any): Promise<void>;
6
- get parameterSpec(): {
7
- type: string;
8
- name: string;
9
- label: string;
10
- }[];
11
- get taskPrefixes(): string[];
12
- get description(): string;
13
- get help(): string;
14
- }
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HeadlessConnector = void 0;
4
- const integration_base_1 = require("@things-factory/integration-base");
5
- const headless_pool_1 = require("./headless-pool");
6
- class HeadlessConnector {
7
- async ready(connectionConfigs) {
8
- await Promise.all(connectionConfigs.map(this.connect.bind(this)));
9
- integration_base_1.ConnectionManager.logger.info('headless-connector connections are ready');
10
- }
11
- async connect(connection) {
12
- var { endpoint: uri = '1', params: { min = 2, max = 10 } } = connection;
13
- const headlessPool = (0, headless_pool_1.createHeadlessPool)({
14
- min,
15
- max,
16
- testOnBorrow: true,
17
- acquireTimeoutMillis: 15000
18
- });
19
- integration_base_1.ConnectionManager.addConnectionInstance(connection, headlessPool);
20
- integration_base_1.ConnectionManager.logger.info(`headless-connector connection(${connection.name}:${connection.endpoint}) is connected`);
21
- }
22
- async disconnect(connection) {
23
- const headlessPool = integration_base_1.ConnectionManager.getConnectionInstance(connection);
24
- (0, headless_pool_1.destroyHeadlessPool)(headlessPool);
25
- integration_base_1.ConnectionManager.removeConnectionInstance(connection);
26
- integration_base_1.ConnectionManager.logger.info(`headless-connector connection(${connection.name}) is disconnected`);
27
- }
28
- get parameterSpec() {
29
- return [
30
- {
31
- type: 'number',
32
- name: 'min',
33
- label: 'pool-size-min'
34
- },
35
- {
36
- type: 'number',
37
- name: 'max',
38
- label: 'pool-size-max'
39
- }
40
- ];
41
- }
42
- get taskPrefixes() {
43
- return ['headless-pdf'];
44
- }
45
- get description() {
46
- return 'Headless Pool Connector';
47
- }
48
- get help() {
49
- return 'integration/connector/headless-connector';
50
- }
51
- }
52
- exports.HeadlessConnector = HeadlessConnector;
53
- integration_base_1.ConnectionManager.registerConnector('headless-connector', new HeadlessConnector());
54
- //# sourceMappingURL=headless-connector.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"headless-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/headless-connector.ts"],"names":[],"mappings":";;;AAAA,uEAA+E;AAC/E,mDAAyE;AAEzE,MAAa,iBAAiB;IAC5B,KAAK,CAAC,KAAK,CAAC,iBAAiB;QAC3B,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEjE,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;IAC3E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAU;QACtB,IAAI,EACF,QAAQ,EAAE,GAAG,GAAG,GAAG,EACnB,MAAM,EAAE,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,EAC9B,GAAG,UAAU,CAAA;QAEd,MAAM,YAAY,GAAG,IAAA,kCAAkB,EAAC;YACtC,GAAG;YACH,GAAG;YACH,YAAY,EAAE,IAAI;YAClB,oBAAoB,EAAE,KAAK;SAC5B,CAAC,CAAA;QAEF,oCAAiB,CAAC,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;QAEjE,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAC3B,iCAAiC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,gBAAgB,CACxF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAU;QACzB,MAAM,YAAY,GAAG,oCAAiB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAA;QACxE,IAAA,mCAAmB,EAAC,YAAY,CAAC,CAAA;QAEjC,oCAAiB,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAA;QAEtD,oCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,UAAU,CAAC,IAAI,mBAAmB,CAAC,CAAA;IACpG,CAAC;IAED,IAAI,aAAa;QACf,OAAO;YACL;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,eAAe;aACvB;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,eAAe;aACvB;SACF,CAAA;IACH,CAAC;IAED,IAAI,YAAY;QACd,OAAO,CAAC,cAAc,CAAC,CAAA;IACzB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,yBAAyB,CAAA;IAClC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,0CAA0C,CAAA;IACnD,CAAC;CACF;AA9DD,8CA8DC;AAED,oCAAiB,CAAC,iBAAiB,CAAC,oBAAoB,EAAE,IAAI,iBAAiB,EAAE,CAAC,CAAA","sourcesContent":["import { ConnectionManager, Connector } from '@things-factory/integration-base'\nimport { createHeadlessPool, destroyHeadlessPool } from './headless-pool'\n\nexport class HeadlessConnector implements Connector {\n async ready(connectionConfigs) {\n await Promise.all(connectionConfigs.map(this.connect.bind(this)))\n\n ConnectionManager.logger.info('headless-connector connections are ready')\n }\n\n async connect(connection) {\n var {\n endpoint: uri = '1',\n params: { min = 2, max = 10 }\n } = connection\n\n const headlessPool = createHeadlessPool({\n min,\n max,\n testOnBorrow: true,\n acquireTimeoutMillis: 15000\n })\n\n ConnectionManager.addConnectionInstance(connection, headlessPool)\n\n ConnectionManager.logger.info(\n `headless-connector connection(${connection.name}:${connection.endpoint}) is connected`\n )\n }\n\n async disconnect(connection) {\n const headlessPool = ConnectionManager.getConnectionInstance(connection)\n destroyHeadlessPool(headlessPool)\n\n ConnectionManager.removeConnectionInstance(connection)\n\n ConnectionManager.logger.info(`headless-connector connection(${connection.name}) is disconnected`)\n }\n\n get parameterSpec() {\n return [\n {\n type: 'number',\n name: 'min',\n label: 'pool-size-min'\n },\n {\n type: 'number',\n name: 'max',\n label: 'pool-size-max'\n }\n ]\n }\n\n get taskPrefixes() {\n return ['headless-pdf']\n }\n\n get description() {\n return 'Headless Pool Connector'\n }\n\n get help() {\n return 'integration/connector/headless-connector'\n }\n}\n\nConnectionManager.registerConnector('headless-connector', new HeadlessConnector())\n"]}
@@ -1,3 +0,0 @@
1
- import * as genericPool from 'generic-pool';
2
- export declare function createHeadlessPool(options: genericPool.Options): genericPool.Pool<any>;
3
- export declare function destroyHeadlessPool(headlessPool: genericPool.Pool<any>): Promise<void>;
@@ -1,63 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createHeadlessPool = createHeadlessPool;
4
- exports.destroyHeadlessPool = destroyHeadlessPool;
5
- const tslib_1 = require("tslib");
6
- const genericPool = tslib_1.__importStar(require("generic-pool"));
7
- const env_1 = require("@things-factory/env");
8
- try {
9
- var puppeteer = require('puppeteer');
10
- }
11
- catch (err) {
12
- env_1.logger.error(err);
13
- }
14
- function createHeadlessPool(options) {
15
- return genericPool.createPool({
16
- create() {
17
- console.log('headless instance in headless-pool-integration about to create');
18
- return initializeChromium();
19
- },
20
- validate(browser) {
21
- return Promise.race([
22
- new Promise(res => setTimeout(() => res(false), 1500)),
23
- browser
24
- //@ts-ignore
25
- .version()
26
- .then(_ => true)
27
- .catch(_ => false)
28
- ]);
29
- },
30
- destroy(browser) {
31
- //@ts-ignore
32
- return browser.close();
33
- }
34
- }, options);
35
- }
36
- async function destroyHeadlessPool(headlessPool) {
37
- if (headlessPool) {
38
- console.log('headless-pool-integration about to destroy');
39
- await headlessPool.drain();
40
- await headlessPool.clear();
41
- }
42
- }
43
- const CHROMIUM_PATH = env_1.config.get('CHROMIUM_PATH');
44
- async function initializeChromium() {
45
- try {
46
- if (!puppeteer) {
47
- return;
48
- }
49
- var launchSetting = {
50
- args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--use-gl=egl'],
51
- headless: 'shell'
52
- };
53
- if (CHROMIUM_PATH) {
54
- launchSetting['executablePath'] = CHROMIUM_PATH;
55
- }
56
- const browser = await puppeteer.launch(launchSetting);
57
- return browser;
58
- }
59
- catch (err) {
60
- env_1.logger.error(err);
61
- }
62
- }
63
- //# sourceMappingURL=headless-pool.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"headless-pool.js","sourceRoot":"","sources":["../../../server/engine/connector/headless-pool.ts"],"names":[],"mappings":";;AAUA,gDAwBC;AAED,kDAOC;;AA3CD,kEAA2C;AAE3C,6CAAoD;AAEpD,IAAI,CAAC;IACH,IAAI,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;AACtC,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,YAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AACnB,CAAC;AAED,SAAgB,kBAAkB,CAAC,OAA4B;IAC7D,OAAO,WAAW,CAAC,UAAU,CAC3B;QACE,MAAM;YACJ,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAA;YAC7E,OAAO,kBAAkB,EAAE,CAAA;QAC7B,CAAC;QACD,QAAQ,CAAC,OAAO;YACd,OAAO,OAAO,CAAC,IAAI,CAAC;gBAClB,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;gBACtD,OAAO;oBACL,YAAY;qBACX,OAAO,EAAE;qBACT,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC;qBACf,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;aACrB,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,CAAC,OAAO;YACb,YAAY;YACZ,OAAO,OAAO,CAAC,KAAK,EAAE,CAAA;QACxB,CAAC;KAC0B,EAC7B,OAAO,CACR,CAAA;AACH,CAAC;AAEM,KAAK,UAAU,mBAAmB,CAAC,YAAmC;IAC3E,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;QAEzD,MAAM,YAAY,CAAC,KAAK,EAAE,CAAA;QAC1B,MAAM,YAAY,CAAC,KAAK,EAAE,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,aAAa,GAAG,YAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEjD,KAAK,UAAU,kBAAkB;IAC/B,IAAI,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,IAAI,aAAa,GAAG;YAClB,IAAI,EAAE,CAAC,mBAAmB,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,CAAC;YAC3E,QAAQ,EAAE,OAAO;SAClB,CAAA;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,aAAa,CAAC,gBAAgB,CAAC,GAAG,aAAa,CAAA;QACjD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QAErD,OAAO,OAAO,CAAA;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACnB,CAAC;AACH,CAAC","sourcesContent":["import * as genericPool from 'generic-pool'\n\nimport { config, logger } from '@things-factory/env'\n\ntry {\n var puppeteer = require('puppeteer')\n} catch (err) {\n logger.error(err)\n}\n\nexport function createHeadlessPool(options: genericPool.Options) {\n return genericPool.createPool(\n {\n create() {\n console.log('headless instance in headless-pool-integration about to create')\n return initializeChromium()\n },\n validate(browser) {\n return Promise.race([\n new Promise(res => setTimeout(() => res(false), 1500)),\n browser\n //@ts-ignore\n .version()\n .then(_ => true)\n .catch(_ => false)\n ])\n },\n destroy(browser) {\n //@ts-ignore\n return browser.close()\n }\n } as genericPool.Factory<any>,\n options\n )\n}\n\nexport async function destroyHeadlessPool(headlessPool: genericPool.Pool<any>) {\n if (headlessPool) {\n console.log('headless-pool-integration about to destroy')\n\n await headlessPool.drain()\n await headlessPool.clear()\n }\n}\n\nconst CHROMIUM_PATH = config.get('CHROMIUM_PATH')\n\nasync function initializeChromium() {\n try {\n if (!puppeteer) {\n return\n }\n\n var launchSetting = {\n args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--use-gl=egl'],\n headless: 'shell'\n }\n\n if (CHROMIUM_PATH) {\n launchSetting['executablePath'] = CHROMIUM_PATH\n }\n\n const browser = await puppeteer.launch(launchSetting)\n\n return browser\n } catch (err) {\n logger.error(err)\n }\n}\n"]}
@@ -1 +0,0 @@
1
- import './headless-connector';
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- require("./headless-connector");
4
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/engine/connector/index.ts"],"names":[],"mappings":";;AAAA,gCAA6B","sourcesContent":["import './headless-connector'\n"]}
@@ -1,68 +0,0 @@
1
- import { ConnectionManager, Connector } from '@things-factory/integration-base'
2
- import { createHeadlessPool, destroyHeadlessPool } from './headless-pool'
3
-
4
- export class HeadlessConnector implements Connector {
5
- async ready(connectionConfigs) {
6
- await Promise.all(connectionConfigs.map(this.connect.bind(this)))
7
-
8
- ConnectionManager.logger.info('headless-connector connections are ready')
9
- }
10
-
11
- async connect(connection) {
12
- var {
13
- endpoint: uri = '1',
14
- params: { min = 2, max = 10 }
15
- } = connection
16
-
17
- const headlessPool = createHeadlessPool({
18
- min,
19
- max,
20
- testOnBorrow: true,
21
- acquireTimeoutMillis: 15000
22
- })
23
-
24
- ConnectionManager.addConnectionInstance(connection, headlessPool)
25
-
26
- ConnectionManager.logger.info(
27
- `headless-connector connection(${connection.name}:${connection.endpoint}) is connected`
28
- )
29
- }
30
-
31
- async disconnect(connection) {
32
- const headlessPool = ConnectionManager.getConnectionInstance(connection)
33
- destroyHeadlessPool(headlessPool)
34
-
35
- ConnectionManager.removeConnectionInstance(connection)
36
-
37
- ConnectionManager.logger.info(`headless-connector connection(${connection.name}) is disconnected`)
38
- }
39
-
40
- get parameterSpec() {
41
- return [
42
- {
43
- type: 'number',
44
- name: 'min',
45
- label: 'pool-size-min'
46
- },
47
- {
48
- type: 'number',
49
- name: 'max',
50
- label: 'pool-size-max'
51
- }
52
- ]
53
- }
54
-
55
- get taskPrefixes() {
56
- return ['headless-pdf']
57
- }
58
-
59
- get description() {
60
- return 'Headless Pool Connector'
61
- }
62
-
63
- get help() {
64
- return 'integration/connector/headless-connector'
65
- }
66
- }
67
-
68
- ConnectionManager.registerConnector('headless-connector', new HeadlessConnector())
@@ -1,69 +0,0 @@
1
- import * as genericPool from 'generic-pool'
2
-
3
- import { config, logger } from '@things-factory/env'
4
-
5
- try {
6
- var puppeteer = require('puppeteer')
7
- } catch (err) {
8
- logger.error(err)
9
- }
10
-
11
- export function createHeadlessPool(options: genericPool.Options) {
12
- return genericPool.createPool(
13
- {
14
- create() {
15
- console.log('headless instance in headless-pool-integration about to create')
16
- return initializeChromium()
17
- },
18
- validate(browser) {
19
- return Promise.race([
20
- new Promise(res => setTimeout(() => res(false), 1500)),
21
- browser
22
- //@ts-ignore
23
- .version()
24
- .then(_ => true)
25
- .catch(_ => false)
26
- ])
27
- },
28
- destroy(browser) {
29
- //@ts-ignore
30
- return browser.close()
31
- }
32
- } as genericPool.Factory<any>,
33
- options
34
- )
35
- }
36
-
37
- export async function destroyHeadlessPool(headlessPool: genericPool.Pool<any>) {
38
- if (headlessPool) {
39
- console.log('headless-pool-integration about to destroy')
40
-
41
- await headlessPool.drain()
42
- await headlessPool.clear()
43
- }
44
- }
45
-
46
- const CHROMIUM_PATH = config.get('CHROMIUM_PATH')
47
-
48
- async function initializeChromium() {
49
- try {
50
- if (!puppeteer) {
51
- return
52
- }
53
-
54
- var launchSetting = {
55
- args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--use-gl=egl'],
56
- headless: 'shell'
57
- }
58
-
59
- if (CHROMIUM_PATH) {
60
- launchSetting['executablePath'] = CHROMIUM_PATH
61
- }
62
-
63
- const browser = await puppeteer.launch(launchSetting)
64
-
65
- return browser
66
- } catch (err) {
67
- logger.error(err)
68
- }
69
- }
@@ -1 +0,0 @@
1
- import './headless-connector'
@@ -1,2 +0,0 @@
1
- import './connector'
2
- import './task'