@kne/fastify-puppeteer 1.1.7 → 1.1.8

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.
@@ -1,7 +1,9 @@
1
1
  const fp = require('fastify-plugin');
2
2
  const { decode } = require('plantuml-encoder');
3
+
3
4
  module.exports = fp(async (fastify, options) => {
4
5
  const { services } = fastify.puppeteer;
6
+ const { photoSchema, pdfSchema } = services;
5
7
  fastify.post(
6
8
  `${options.prefix}/parseHtmlToPdf`,
7
9
  {
@@ -13,7 +15,7 @@ module.exports = fp(async (fastify, options) => {
13
15
  type: 'object',
14
16
  required: ['content'],
15
17
  properties: {
16
- options: { type: 'object' },
18
+ options: pdfSchema,
17
19
  content: { type: 'string' }
18
20
  }
19
21
  }
@@ -21,6 +23,7 @@ module.exports = fp(async (fastify, options) => {
21
23
  },
22
24
  async (request, reply) => {
23
25
  const filename = await services.parseHtmlToPdf({ html: request.body.content, options: request.body.options });
26
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
24
27
  return reply.sendFile(filename, { root: options.root });
25
28
  }
26
29
  );
@@ -33,13 +36,14 @@ module.exports = fp(async (fastify, options) => {
33
36
  type: 'object',
34
37
  required: ['content'],
35
38
  properties: {
36
- options: { type: 'object' },
39
+ options: pdfSchema,
37
40
  content: { type: 'string' }
38
41
  }
39
42
  }
40
43
  },
41
44
  async (request, reply) => {
42
45
  const filename = await services.parseHtmlToPdf({ html: decode(request.query.content), options: request.query.options });
46
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
43
47
  return reply.sendFile(filename, { root: options.root });
44
48
  }
45
49
  );
@@ -55,7 +59,7 @@ module.exports = fp(async (fastify, options) => {
55
59
  type: 'object',
56
60
  required: ['contentList'],
57
61
  properties: {
58
- options: { type: 'object' },
62
+ options: pdfSchema,
59
63
  contentList: {
60
64
  type: 'array',
61
65
  items: {
@@ -71,6 +75,7 @@ module.exports = fp(async (fastify, options) => {
71
75
  htmlList: request.body.contentList,
72
76
  options: request.body.options
73
77
  });
78
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
74
79
  return reply.sendFile(filename, { root: options.root });
75
80
  }
76
81
  );
@@ -86,7 +91,7 @@ module.exports = fp(async (fastify, options) => {
86
91
  type: 'object',
87
92
  required: ['contentList'],
88
93
  properties: {
89
- options: { type: 'object' },
94
+ options: pdfSchema,
90
95
  contentList: {
91
96
  type: 'array',
92
97
  items: {
@@ -102,6 +107,7 @@ module.exports = fp(async (fastify, options) => {
102
107
  htmlList: request.query.contentList.map(item => decode(item)),
103
108
  options: request.query.options
104
109
  });
110
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
105
111
  return reply.sendFile(filename, { root: options.root });
106
112
  }
107
113
  );
@@ -117,7 +123,7 @@ module.exports = fp(async (fastify, options) => {
117
123
  type: 'object',
118
124
  required: ['contentList'],
119
125
  properties: {
120
- options: { type: 'object' },
126
+ options: photoSchema,
121
127
  contentList: {
122
128
  type: 'array',
123
129
  items: {
@@ -133,6 +139,7 @@ module.exports = fp(async (fastify, options) => {
133
139
  htmlList: request.body.contentList,
134
140
  options: request.body.options
135
141
  });
142
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
136
143
  return reply.sendFile(filename, { root: options.root });
137
144
  }
138
145
  );
@@ -148,7 +155,7 @@ module.exports = fp(async (fastify, options) => {
148
155
  type: 'object',
149
156
  required: ['contentList'],
150
157
  properties: {
151
- options: { type: 'object' },
158
+ options: photoSchema,
152
159
  contentList: {
153
160
  type: 'array',
154
161
  items: {
@@ -164,6 +171,7 @@ module.exports = fp(async (fastify, options) => {
164
171
  htmlList: request.query.contentList.map(item => decode(item)),
165
172
  options: request.query.options
166
173
  });
174
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
167
175
  return reply.sendFile(filename, { root: options.root });
168
176
  }
169
177
  );
@@ -179,7 +187,7 @@ module.exports = fp(async (fastify, options) => {
179
187
  type: 'object',
180
188
  required: ['content'],
181
189
  properties: {
182
- options: { type: 'object' },
190
+ options: photoSchema,
183
191
  content: { type: 'string' }
184
192
  }
185
193
  }
@@ -190,6 +198,7 @@ module.exports = fp(async (fastify, options) => {
190
198
  html: request.body.content,
191
199
  options: request.body.options
192
200
  });
201
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
193
202
  return reply.sendFile(filename, { root: options.root });
194
203
  }
195
204
  );
@@ -205,7 +214,7 @@ module.exports = fp(async (fastify, options) => {
205
214
  type: 'object',
206
215
  required: ['content'],
207
216
  properties: {
208
- options: { type: 'object' },
217
+ options: photoSchema,
209
218
  content: { type: 'string' }
210
219
  }
211
220
  }
@@ -216,6 +225,7 @@ module.exports = fp(async (fastify, options) => {
216
225
  html: decode(request.query.content),
217
226
  options: request.query.options
218
227
  });
228
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
219
229
  return reply.sendFile(filename, { root: options.root });
220
230
  }
221
231
  );
@@ -231,7 +241,7 @@ module.exports = fp(async (fastify, options) => {
231
241
  type: 'object',
232
242
  required: ['url'],
233
243
  properties: {
234
- options: { type: 'object' },
244
+ options: pdfSchema,
235
245
  url: { type: 'string' }
236
246
  }
237
247
  }
@@ -239,6 +249,7 @@ module.exports = fp(async (fastify, options) => {
239
249
  },
240
250
  async (request, reply) => {
241
251
  const filename = await services.parseUrlToPdf({ url: request.body.url, options: request.body.options });
252
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
242
253
  return reply.sendFile(filename, { root: options.root });
243
254
  }
244
255
  );
@@ -254,7 +265,7 @@ module.exports = fp(async (fastify, options) => {
254
265
  type: 'object',
255
266
  required: ['url'],
256
267
  properties: {
257
- options: { type: 'object' },
268
+ options: pdfSchema,
258
269
  url: { type: 'string' }
259
270
  }
260
271
  }
@@ -262,6 +273,7 @@ module.exports = fp(async (fastify, options) => {
262
273
  },
263
274
  async (request, reply) => {
264
275
  const filename = await services.parseUrlToPdf({ url: request.query.url, options: request.query.options });
276
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
265
277
  return reply.sendFile(filename, { root: options.root });
266
278
  }
267
279
  );
@@ -277,7 +289,7 @@ module.exports = fp(async (fastify, options) => {
277
289
  type: 'object',
278
290
  required: ['urlList'],
279
291
  properties: {
280
- options: { type: 'object' },
292
+ options: pdfSchema,
281
293
  urlList: { type: 'array', items: { type: 'string' } }
282
294
  }
283
295
  }
@@ -288,6 +300,7 @@ module.exports = fp(async (fastify, options) => {
288
300
  urlList: request.query.urlList,
289
301
  options: request.query.options
290
302
  });
303
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
291
304
  return reply.sendFile(filename, { root: options.root });
292
305
  }
293
306
  );
@@ -303,7 +316,7 @@ module.exports = fp(async (fastify, options) => {
303
316
  type: 'object',
304
317
  required: ['url'],
305
318
  properties: {
306
- options: { type: 'object' },
319
+ options: photoSchema,
307
320
  url: { type: 'string' },
308
321
  selector: { type: 'string' }
309
322
  }
@@ -316,6 +329,7 @@ module.exports = fp(async (fastify, options) => {
316
329
  selector: request.body.selector,
317
330
  options: request.body.options
318
331
  });
332
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
319
333
  return reply.sendFile(filename, { root: options.root });
320
334
  }
321
335
  );
@@ -331,7 +345,7 @@ module.exports = fp(async (fastify, options) => {
331
345
  type: 'object',
332
346
  required: ['url'],
333
347
  properties: {
334
- options: { type: 'object' },
348
+ options: photoSchema,
335
349
  url: { type: 'string' },
336
350
  selector: { type: 'string' }
337
351
  }
@@ -344,6 +358,7 @@ module.exports = fp(async (fastify, options) => {
344
358
  selector: request.query.selector,
345
359
  options: request.query.options
346
360
  });
361
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
347
362
  return reply.sendFile(filename, { root: options.root });
348
363
  }
349
364
  );
@@ -359,7 +374,7 @@ module.exports = fp(async (fastify, options) => {
359
374
  type: 'object',
360
375
  required: ['urlList'],
361
376
  properties: {
362
- options: { type: 'object' },
377
+ options: photoSchema,
363
378
  urlList: { type: 'array', items: { type: 'string' } },
364
379
  selector: { type: 'string' }
365
380
  }
@@ -372,6 +387,7 @@ module.exports = fp(async (fastify, options) => {
372
387
  selector: request.body.selector,
373
388
  options: request.body.options
374
389
  });
390
+ request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
375
391
  return reply.sendFile(filename, { root: options.root });
376
392
  }
377
393
  );
@@ -387,7 +403,7 @@ module.exports = fp(async (fastify, options) => {
387
403
  type: 'object',
388
404
  required: ['urlList'],
389
405
  properties: {
390
- options: { type: 'object' },
406
+ options: photoSchema,
391
407
  urlList: { type: 'array', items: { type: 'string' } },
392
408
  selector: { type: 'string' }
393
409
  }
@@ -400,6 +416,7 @@ module.exports = fp(async (fastify, options) => {
400
416
  selector: request.query.selector,
401
417
  options: request.query.options
402
418
  });
419
+ request.query.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.query.filename)}`);
403
420
  return reply.sendFile(filename, { root: options.root });
404
421
  }
405
422
  );
package/libs/services.js CHANGED
@@ -5,6 +5,181 @@ const compressing = require('compressing');
5
5
  const path = require('node:path');
6
6
  const createDeferred = require('@kne/create-deferred');
7
7
 
8
+ const pdfSchema = {
9
+ type: 'object',
10
+ properties: {
11
+ scale: {
12
+ type: 'number',
13
+ description: '缩放比例(0.1-2.0)',
14
+ default: 1.0,
15
+ minimum: 0.1,
16
+ maximum: 2.0
17
+ },
18
+ displayHeaderFooter: {
19
+ type: 'boolean',
20
+ description: '显示页眉页脚(需配合 headerTemplate/footerTemplate)',
21
+ default: false
22
+ },
23
+ headerTemplate: {
24
+ type: 'string',
25
+ description: 'HTML 格式的页眉模板(需启用 displayHeaderFooter)',
26
+ default: ''
27
+ },
28
+ footerTemplate: {
29
+ type: 'string',
30
+ description: 'HTML 格式的页脚模板(需启用 displayHeaderFooter)',
31
+ default: ''
32
+ },
33
+ printBackground: {
34
+ type: 'boolean',
35
+ description: '打印背景图形和 CSS 颜色',
36
+ default: true
37
+ },
38
+ landscape: {
39
+ type: 'boolean',
40
+ description: '横向布局',
41
+ default: false
42
+ },
43
+ pageRanges: {
44
+ type: 'string',
45
+ description: "页面范围(如 '1-5, 8, 11-13')",
46
+ default: ''
47
+ },
48
+ format: {
49
+ type: 'string',
50
+ description: "纸张格式(如 'A4', 'Letter')",
51
+ enum: ['Letter', 'Legal', 'Tabloid', 'Ledger', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6'],
52
+ default: 'A4'
53
+ },
54
+ width: {
55
+ type: ['string', 'number'],
56
+ description: "自定义宽度(如 '8.5in', '200px', 200)"
57
+ },
58
+ height: {
59
+ type: ['string', 'number'],
60
+ description: "自定义高度(如 '11in', '300px', 300)"
61
+ },
62
+ margin: {
63
+ type: 'object',
64
+ properties: {
65
+ top: {
66
+ type: ['string', 'number'],
67
+ description: "上边距(如 '1in', '20px')",
68
+ default: '0.4in'
69
+ },
70
+ right: {
71
+ type: ['string', 'number'],
72
+ description: '右边距',
73
+ default: '0.4in'
74
+ },
75
+ bottom: {
76
+ type: ['string', 'number'],
77
+ description: '下边距',
78
+ default: '0.4in'
79
+ },
80
+ left: {
81
+ type: ['string', 'number'],
82
+ description: '左边距',
83
+ default: '0.4in'
84
+ }
85
+ },
86
+ additionalProperties: false
87
+ },
88
+ preferCSSPageSize: {
89
+ type: 'boolean',
90
+ description: '优先使用 CSS 定义的 @page 尺寸',
91
+ default: false
92
+ },
93
+ omitBackground: {
94
+ type: 'boolean',
95
+ description: '隐藏默认白色背景(需配合 printBackground: true)',
96
+ default: false
97
+ },
98
+ timeout: {
99
+ type: 'number',
100
+ description: '超时时间(毫秒)',
101
+ default: 30000
102
+ },
103
+ tagged: {
104
+ type: 'boolean',
105
+ description: '生成带标签的 PDF(增强可访问性)',
106
+ default: false
107
+ },
108
+ waitForMaxTime: { type: 'number' },
109
+ waitForVisible: { type: 'boolean' },
110
+ waitForSelector: {
111
+ oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }]
112
+ }
113
+ },
114
+ additionalProperties: false
115
+ };
116
+
117
+ const photoSchema = {
118
+ type: 'object',
119
+ properties: {
120
+ type: {
121
+ type: 'string',
122
+ enum: ['png', 'jpeg', 'webp'],
123
+ description: '截图图片格式',
124
+ default: 'png'
125
+ },
126
+ quality: {
127
+ type: 'integer',
128
+ description: '图片质量(仅适用于 jpeg 和 webp),取值 0-100',
129
+ minimum: 0,
130
+ maximum: 100
131
+ },
132
+ fullPage: {
133
+ type: 'boolean',
134
+ description: '是否截取完整页面(包括滚动部分)',
135
+ default: true
136
+ },
137
+ clip: {
138
+ type: 'object',
139
+ description: '指定裁剪区域',
140
+ properties: {
141
+ x: {
142
+ type: 'number',
143
+ description: '裁剪区域左上角 x 坐标'
144
+ },
145
+ y: {
146
+ type: 'number',
147
+ description: '裁剪区域左上角 y 坐标'
148
+ },
149
+ width: {
150
+ type: 'number',
151
+ description: '裁剪区域宽度'
152
+ },
153
+ height: {
154
+ type: 'number',
155
+ description: '裁剪区域高度'
156
+ }
157
+ },
158
+ required: ['x', 'y', 'width', 'height'],
159
+ additionalProperties: false
160
+ },
161
+ omitBackground: {
162
+ type: 'boolean',
163
+ description: '是否隐藏默认白色背景(使背景透明),仅适用于 png 格式'
164
+ },
165
+ encoding: {
166
+ type: 'string',
167
+ enum: ['binary', 'base64'],
168
+ description: '返回数据的编码格式'
169
+ },
170
+ captureBeyondViewport: {
171
+ type: 'boolean',
172
+ description: '是否捕获视口外的内容(仅当 fullPage 为 true 时有效)'
173
+ },
174
+ waitForMaxTime: { type: 'number' },
175
+ waitForVisible: { type: 'boolean' },
176
+ waitForSelector: {
177
+ oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }]
178
+ }
179
+ },
180
+ additionalProperties: false
181
+ };
182
+
8
183
  module.exports = fp(async (fastify, options) => {
9
184
  const { maxCacheKeys, maxConcurrent, root } = options;
10
185
  const cache = await createCache(options);
@@ -147,6 +322,8 @@ module.exports = fp(async (fastify, options) => {
147
322
  };
148
323
 
149
324
  Object.assign(fastify.puppeteer.services, {
325
+ pdfSchema,
326
+ photoSchema,
150
327
  puppeteerPage,
151
328
  getFilePath,
152
329
  parseHtmlToPdf,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kne/fastify-puppeteer",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "用于操作无头浏览器截图保存pdf等",
5
5
  "main": "index.js",
6
6
  "scripts": {