渲染器-puppeteer-客户端
插件用于 renderer-foundation. 招标人 kind=["document-pdf", "image-snapshot"] 通过 HTTP 呼叫请求 puppeteer-service (在 apps/puppeteer-service )),该存储器中.
建筑
Caller (e.g. Solon NKA-PDF-Generator)
│
│ const renderer = foundation.createRendererForTenant({ tenantId, kind: "document-pdf" })
│ await renderer.render({ kind: "document-pdf", payload: { content: "<html>…" } }, ctx)
▼
renderer-puppeteer-client (THIS package)
│
│ POST {PUPPETEER_SERVICE_URL}/render
│ Authorization: Bearer {PUPPETEER_AUTH_TOKEN}
│ Retry: 5xx + network-error (exponential backoff)
▼
puppeteer-service (separate app, K8s deployment)
│
│ Launches Puppeteer browser, renders HTML
▼
{kind: "document-pdf", pdfBytes, ...}
挂载
import { createRendererPuppeteerClientFeature } from "@cosmicdrift/kumiko-renderer-puppeteer-client";
import { createRendererFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-foundation";
const features = [
createRendererFoundationFeature(),
createRendererPuppeteerClientFeature(),
// additional renderer plugins (renderer-mail-html, …)
];
// In createKumikoApp's extraContext:
extraContext: ({ db, registry }) => ({
rendererFoundation: createRendererFoundationApi(collectRendererPlugins(registry)),
});
配置( env vars)
App-wide,通过 K8s Secret + ConfigMap 设置 :
| 瓦尔 | 默认 | 说明 |
|---|---|---|
PUPPETEER_SERVICE_URL | http://puppeteer-service:3000 | 服务的内部集群 URL |
PUPPETEER_AUTH_TOKEN | 所需经费 | 共享秘密( QQ16 字符, 匹配服务) |
PUPPETEER_CLIENT_TIMEOUT_MS | 60000 | 按请求暂停(客户方) |
PUPPETEER_CLIENT_MAX_RETRIES | 3 | 5xx/ 网络错误的重试次数 |
每个租户? 现 没有所有租户都使用同样的服务。 如有实际需要,将增加通过配置包覆盖的租户,例如企业租户专用的Puppeteer池.
重试策略
| 回应 | 行动 |
|---|---|
2xx | 成功, 返回字节 |
4xx (验证、认证、413) | 无重试返回时 RendererError("invalid_payload") 马上 |
5xx (回复出错,服务崩溃) | 以指数回放( 200ms × 2^ 尝试) + ± 50% 焦距重试 |
| 网络错误(ECONNREFUSED,超时) | 重试为 5xx |
之后 PUPPETEER_CLIENT_MAX_RETRIES 已用尽 : RendererError("other") 带有原始错误消息.
调用模式
import { requireRendererFoundation } from "@cosmicdrift/kumiko-bundled-features/renderer-foundation";
async function generatePdf(ctx, tenantId, htmlContent) {
const foundation = requireRendererFoundation(ctx, "generatePdf");
const renderer = foundation.createRendererForTenant({ tenantId, kind: "document-pdf" });
const result = await renderer.render(
{
kind: "document-pdf",
payload: { content: htmlContent },
options: { format: "A4", marginMm: { top: 15, bottom: 15 } },
},
{ db: ctx.db, registry: ctx.registry, tenantId },
);
if (result.kind !== "document-pdf") throw new Error("unexpected kind");
return result.pdfBytes; // → S3 upload or directly to the caller
}
测试
- 15个单元测试 (配置+HTTP客户端+提供+合同). 没有真正的网络呼叫, 获取嘲笑通过
vi.stubGlobal。 。 。 . - 没有实际服务的整合测试。服务堆栈要求傀儡服务在 URL 运行,这是 E2E 设置(第7步) 。 这里,只有HTTP层的单位覆盖.
范围外
- HTML sanitization: 呼叫者提供可信赖的 HTML( 从模板解析器和租户范围化)
- 文件存储: 响应总是包含内含字节, 必要时, 调用者会坚持使用 S3
- 特定租户的URL:目前是全应用的,可能以后通过一个配置包