@things-factory/integration-headless 8.0.0 → 9.0.0-beta.3
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.
- package/dist-server/engine/index.d.ts +0 -1
- package/dist-server/engine/index.js +0 -1
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/task/pdf-capture-util.d.ts +1 -1
- package/dist-server/engine/task/pdf-capture-util.js +3 -3
- package/dist-server/engine/task/pdf-capture-util.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/helps/integration/connector/headless-connector.ja.md +183 -31
- package/helps/integration/connector/headless-connector.ko.md +177 -32
- package/helps/integration/connector/headless-connector.md +178 -31
- package/helps/integration/connector/headless-connector.ms.md +180 -32
- package/helps/integration/connector/headless-connector.zh.md +178 -31
- package/package.json +6 -6
- package/dist-server/engine/connector/headless-connector.d.ts +0 -14
- package/dist-server/engine/connector/headless-connector.js +0 -54
- package/dist-server/engine/connector/headless-connector.js.map +0 -1
- package/dist-server/engine/connector/headless-pool.d.ts +0 -3
- package/dist-server/engine/connector/headless-pool.js +0 -63
- package/dist-server/engine/connector/headless-pool.js.map +0 -1
- package/dist-server/engine/connector/index.d.ts +0 -1
- package/dist-server/engine/connector/index.js +0 -4
- package/dist-server/engine/connector/index.js.map +0 -1
- package/server/engine/connector/headless-connector.ts +0 -68
- package/server/engine/connector/headless-pool.ts +0 -69
- package/server/engine/connector/index.ts +0 -1
- package/server/engine/index.ts +0 -2
- package/server/engine/task/headless-pdf-capture-board.ts +0 -182
- package/server/engine/task/headless-pdf-capture-markdown.ts +0 -47
- package/server/engine/task/headless-pdf-capture.ts +0 -39
- package/server/engine/task/headless-pdf-open.ts +0 -98
- package/server/engine/task/headless-pdf-save.ts +0 -88
- package/server/engine/task/index.ts +0 -9
- package/server/engine/task/pdf-capture-util.ts +0 -331
- package/server/index.ts +0 -3
- package/server/tsconfig.json +0 -10
|
@@ -1,57 +1,168 @@
|
|
|
1
|
-
###
|
|
1
|
+
### 🏆 无头连接器 (Headless Connector)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**无头连接器** 利用无头浏览器执行 **自动登录**、**网页爬取**、**PDF 生成**、**表单提交**等任务。
|
|
4
|
+
它可以管理多个浏览器会话,支持会话保持、Cookie 处理、基于 API 的登录等功能。
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
---
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
## 🚀 1. 端点(Endpoint)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
无头连接器的端点需要填写 **目标网页的 URL**。
|
|
11
|
+
例如,可以输入 `https://example.com` 以抓取该网站的内容,或者使用 `localhost` 生成 PDF。
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
- 指定池中维持的最少无头浏览器实例数量。
|
|
13
|
-
- 默认值: `2`
|
|
14
|
-
- **max** (数字):
|
|
15
|
-
- 指定池中可以处理的最大无头浏览器实例数量。
|
|
16
|
-
- 默认值: `10`
|
|
13
|
+
---
|
|
17
14
|
|
|
18
|
-
|
|
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
|
-
"
|
|
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": "
|
|
109
|
+
"endpoint": "https://example.com",
|
|
27
110
|
"params": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
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
|
-
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 🔄 5. 连接生命周期(Connection Lifecycle)
|
|
128
|
+
|
|
129
|
+
- **connect**
|
|
35
130
|
|
|
36
|
-
|
|
131
|
+
- 根据参数执行登录流程,并保持会话。
|
|
132
|
+
- 存储 Cookie,以便后续请求使用。
|
|
37
133
|
|
|
38
|
-
- **
|
|
134
|
+
- **disconnect**
|
|
135
|
+
- 关闭浏览器会话,并清除 Cookie。
|
|
39
136
|
|
|
40
|
-
|
|
41
|
-
- 池会自动管理这些实例的生命周期,必要时创建新的实例并重用现有的实例。
|
|
137
|
+
---
|
|
42
138
|
|
|
43
|
-
|
|
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
|
-
|
|
147
|
+
#### 📌 任务示例
|
|
49
148
|
|
|
50
|
-
|
|
149
|
+
### 1️⃣ 网页爬取(`headless-scrap`)
|
|
51
150
|
|
|
52
|
-
|
|
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
|
-
|
|
218
|
+
📌 **通过无头连接器,你可以轻松地在不同网站上实现自动化操作!** 🚀
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/integration-headless",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0-beta.3",
|
|
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": "^
|
|
26
|
-
"@things-factory/board-service": "^
|
|
27
|
-
"@things-factory/integration-base": "^
|
|
28
|
-
"@things-factory/shell": "^
|
|
25
|
+
"@things-factory/attachment-base": "^9.0.0-beta.3",
|
|
26
|
+
"@things-factory/board-service": "^9.0.0-beta.3",
|
|
27
|
+
"@things-factory/integration-base": "^9.0.0-beta.3",
|
|
28
|
+
"@things-factory/shell": "^9.0.0-beta.0",
|
|
29
29
|
"ejs": "^3.1.10",
|
|
30
30
|
"pdf-lib": "^1.17.1"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "1d7e0dd4c88f3c3f3bd311c00e4b1d1542d53634"
|
|
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,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 +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'
|
package/server/engine/index.ts
DELETED