# pdfforge-demo **Repository Path**: pdfforge/pdfforge-demo ## Basic Information - **Project Name**: pdfforge-demo - **Description**: 用于测试pdfforge项目。 - **Primary Language**: NodeJS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-09 - **Last Updated**: 2026-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PdfForge 接口测试台(pdfforge-demo) 专门用来手测 PdfForge 全部 HTTP 端点的小工具:**一个零依赖的 Node 静态服务器 + 反向代理,加两个页面**。 它自己不做登录鉴权,需要凭据的接口在页面顶栏的文本框里填 Bearer token 即可。 > 这个目录既不属于代码仓 `pdfforge`,也不属于文档仓 `pdfforge-docs`,别把它提交进那两个仓库。 ## 起法 ```bat :: Windows:双击 start.bat,或 node server.js :: 后端不在 8080 时 set PDFFORGE_BASE=http://127.0.0.1:18099 && node server.js ``` - 测试台:(`DEMO_PORT` 可改;默认只绑 `127.0.0.1`,要让别的机器访问就设 `DEMO_HOST=0.0.0.0`) - 业务端点页: - 控制台端点测试参考页: 后端地址改起来有两种:启动前设 `PDFFORGE_BASE`,或直接改页面顶栏那个框点"应用"(只影响本进程,重启回到默认)。 ## 为什么要代理 服务端不下发 CORS 头(本项目也没有 Actuator、没有网关)。页面若用 `file://` 打开或挂在别的端口上直连后端,浏览器会拦掉跨域请求。测试台把页面和 `/api/*`、`/console/*` 放在同一个源里,代理只做透传:multipart 请求体、PDF 响应流、`Authorization` 头原样过。 后端没起来时代理回一个 `DEMO_BACKEND_UNREACHABLE` 的 502 信封,页面上直接说"连不上哪个地址",不会让你以为是接口报错。 ## 两个页面各测什么 `index.html`(业务端点,7 张卡片): | 卡片 | 端点 | |------|------| | 能力查询 | `GET /api/v1/convert/supported` | | 字体清单 | `GET /api/v1/convert/fonts`(带 `names` 时逐个核对) | | 统一转换 | `POST /api/v1/convert`(s3 / base64 输入 × s3 / base64 / stream 输出) | | 上传转换 | `POST /api/v1/convert/upload` | | 合并 | `POST /api/v1/pdf/merge`(源可增删,`pages` 形如 `1-3,7`) | | 水印 | `POST /api/v1/pdf/watermark`(文字与图片二选一) | | 压缩 | `POST /api/v1/pdf/compress`(`FIDELITY_FIRST` / `SIZE_FIRST`) | 每张卡片都是"表单 → 请求体预览 → 发送",预览随输入即时更新,所以能先把报文看明白再打出去;`复制 curl` 生成等价命令,可以直接搬进脚本或工单。`stream` 输出的 PDF 在页内预览并给出下载链接。响应 JSON 默认折叠(点"详情"那行展开),卡片本身点标题就整张收起、收起哪几张会记住,刷新后照旧——只测一个端点时不必让另外七个占着屏幕。base64 输入有三种喂法:选本地文件(自动转 Base64)、粘 Base64、或者点"编码"把内联文本按 UTF-8 转成 content(测 `.txt` 最省事,不用准备文件、也不碰 WPS)。 最后那张**"串跑 5 次调用"**是给控制台页造数据的:txt→base64 转一份 PDF,再用同一份 PDF 依次跑 merge / watermark / compress,第 ⑤ 步故意让第二个源指向不存在的 S3 key——取源失败发生在服务端记账的 `try` 之内,所以会留下一条 `failed` 流水。四条 op 一次凑齐。 `console.html`(控制台端点,只测 `GET /console/v1/records`): - **查询区**:9 个参数全给,实时显示拼出来的 URL,结果画成表格(点一行看这条记录的原始 JSON)。 - **用例自检**:19 条用例,每条真发一次请求,把"期望"(来自接口契约)与"实际"(这次调用)并排列出来,判定 通过 / 不通过 / 跳过。判定不了的(比如需要昨天的数据、或当前服务鉴权是关闭的)老实标"跳过"并说明缺什么,不会假装绿。 - **口径对照**:`cost==wall`(失败条目)、默认只查当天、带 `traceId` 时不套默认、只回 `hasMore` 不回总数、`page=abc` 是 400 不是 500、参数校验阶段失败的调用留不下流水——这些读结果前要先对齐的东西写在页面最下面。 ## 打包成单文件 exe(目标机没有 Node 时) 在**有 Node 的打包机**上跑一次: ```bat build-exe.bat :: 产出 dist\pdfforge-demo.exe,约 69MB ``` 它做三件事:`build/make-bundle.js` 把 `public/` 下的页面与脚本读成 base64 内联进一个 JS 文件 → `node --experimental-sea-config` 生成 SEA blob → `npx postject` 把 blob 注进一份 `node.exe` 副本。之后拷 **那一个文件** 到云上 Windows 即可,目标机不需要 Node、不需要 npm、也不需要 `public/` 目录(页面在打包时就进了 exe)。 ```bat :: 目标机上,两种起法都行 pdfforge-demo.exe set PDFFORGE_BASE=http://127.0.0.1:8080 && set DEMO_PORT=5180 && pdfforge-demo.exe ``` 三个坑先说清楚: - postject 会打印 `warning: The signature seems corrupted!` —— 这是必然的,我们改动了一个已签名的 `node.exe` 二进制。功能不受影响,但**企业环境若按"签名损坏/未签名"拦截 exe**,就得给它补签名,或者退回 `node server.js` 那条路。 - 运行时是打进去的 Node(本机是 v20.19.3),所以 exe 体积 ≈ Node 运行时 + 88KB 页面。改了 `public/` 或 `server.js` 要重新跑一次 `build-exe.bat` 才会进 exe——直接改源码然后双击旧 exe 是不会生效的,这一点很容易踩。 - `dist/` 与 `build/bundle.js` 都是构建产物,别当源码提交。 已实测:从**没有** `public/` 的目录里跑这个 exe,`/`、`/console.html`、`/app.js`、`/common.css`、`/__demo/env` 全部 200 且 content-type 正确,`POST /api/v1/convert` 与 `GET /console/v1/records` 经 exe 代理打通,控制台页用例自检 **15 通过 / 0 不通过 / 4 跳过**(4 条跳过分别是:这个浏览器上下文还没记住 traceId、需要昨天的数据、当前服务鉴权是关闭的、坏行那条要手工配合)。 ## 连鉴权链路一起测 服务默认 `pdfforge.auth.enabled=false`,此时控制台页的"角色闸门"用例老实标**跳过**。要测 401/403/200,用一份**本地临时 token**起服务(下面两个 token 值是随手编的,只出现在这条命令行里;真实凭据按项目规矩只放 `local-conf`,不进任何仓库): ```bat java --add-opens java.base/java.lang=ALL-UNNAMED -Djava.library.path="D:\Java\jdk-17\bin" ^ -Dfile.encoding=UTF-8 -Xms256m -Xmx512m ^ -jar ..\pdfforge\pdfforge-web\target\pdfforge-web.jar ^ --spring.profiles.active=dev --server.port=18099 ^ --pdfforge.auth.enabled=true ^ --pdfforge.auth.tokens[0].subject=ops --pdfforge.auth.tokens[0].token=demo-console-token-8f3a --pdfforge.auth.tokens[0].roles[0]=convert --pdfforge.auth.tokens[0].roles[1]=console ^ --pdfforge.auth.tokens[1].subject=crm --pdfforge.auth.tokens[1].token=demo-biz-token-1b7c --pdfforge.auth.tokens[1].roles[0]=convert ``` 顶栏填 `demo-console-token-8f3a`:`/console/v1/records` 是 200,之后业务调用写进流水的 `subject` 就是 `ops`。换成 `demo-biz-token-1b7c`:控制台 403 `FORBIDDEN`(缺角色),业务端点照常。清空:401。`/api/v1/convert/supported` 在免鉴权名单里,无 token 也 200;`/fonts` 不在名单里,无 token 是 401。 这条链路实测过的结果:控制台页 19 条用例 **17 通过 / 0 不通过 / 2 跳过**(跳过的那两条一条要昨天的数据、一条要手工往 jsonl 追加残缺行)。半途追加的残缺行确认被跳过而不是让查询 5xx:文件里留 13 条、查询仍 200 且回 13 条。 ## 用之前知道这几件事 - **服务鉴权默认是关闭的**(`pdfforge.auth.enabled=false`),此时顶栏留空就能打通。开了鉴权才需要填 token;控制台端点还额外要 `console` 角色,缺角色是 403。 - **`stream` 输出没有 JSON 信封**,所以拿不到 `size`/`compressed` 这类字段,只能自己比字节数;想看这些字段就用 `base64` 输出。 - 转换类接口会真调用本机 WPS(`.txt`/图片走纯 PDFBox,不碰 WPS);后处理三端点全是 PDFBox 计算。实例池被占满是 `POOL_EXHAUSTED` 503。 - 页面里的 token 与暂存产物只在浏览器本地(token 进 `localStorage`,产物在内存里,刷新即清),不会写进这个目录的任何文件。