diff --git a/.gitignore b/.gitignore index 62c1956..360b2d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,17 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +# ========================= # User-specific files +# ========================= *.suo *.user *.userosscache *.sln.docstates -node_modules/* -src/HtmlToPdfService.Admin/node_modules/* + +# ========================= # Build results +# ========================= [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -21,7 +24,11 @@ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ +# ========================= +# IDE and Editor files +# ========================= # Visual Studio 2015/2017/2019/2022 cache/options directory .vs/ @@ -32,7 +39,9 @@ bld/ .idea/ *.sln.iml +# ========================= # .NET Core +# ========================= project.lock.json project.fragment.lock.json artifacts/ @@ -40,19 +49,60 @@ artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt +# ========================= +# Node.js / Frontend +# ========================= +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* +dist/ +.nuxt/ +.output/ + +# ========================= +# Generated files +# ========================= # PDF files (generated) pdfs/ *.pdf +# Image files (generated output) +/src/HtmlToPdfService.Api/files/ +/mvp/HtmlToPdfService.Api/pdfs/ + +# ========================= # NuGet Packages +# ========================= *.nupkg *.snupkg **/packages/* -# Local Chromium +# ========================= +# Chromium / Puppeteer +# ========================= .local-chromium/ +**/.local-chromium/ +# ========================= +# Logs and runtime files +# ========================= +/src/HtmlToPdfService.Api/logs/ +/mvp/HtmlToPdfService.Api/logs/ +*.log + +# ========================= +# Environment files +# ========================= +.env +.env.local +.env.*.local +appsettings.*.local.json + +# ========================= # OS generated files +# ========================= .DS_Store .DS_Store? ._* @@ -60,7 +110,25 @@ pdfs/ .Trashes ehthumbs.db Thumbs.db +desktop.ini +# ========================= +# Test results +# ========================= +TestResults/ +*.trx +coverage/ +*.coveragexml -/src/HtmlToPdfService.Api/files/* -/src/HtmlToPdfService.Api/logs/* \ No newline at end of file +# ========================= +# Docker +# ========================= +docker-compose.override.yml + +# ========================= +# Temporary files +# ========================= +*.tmp +*.temp +*.swp +*~ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4db58db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,96 @@ +# 更新日志 + +所有重要更改都会记录在此文件中。 + +格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), +版本管理遵循 [语义化版本](https://semver.org/lang/zh-CN/)。 + +## [Unreleased] + +### 计划中 +- 支持 PDF 加密与密码保护 +- 支持自定义页眉页脚模板 +- 支持水印功能 +- 多语言管理后台 + +--- + +## [2.0.0] - 2024-12-10 + +### 🎉 正式版发布 + +这是一个重大版本更新,引入了异步任务处理架构。 + +### 新增 +- ✨ **异步任务队列** - 基于 Redis 的任务队列,支持高并发处理 +- ✨ **任务管理** - 完整的任务生命周期管理(创建、查询、取消、重试) +- ✨ **批量任务** - 支持一次提交多个转换任务 +- ✨ **管理后台** - Vue 3 构建的可视化管理界面 +- ✨ **API Key 认证** - 支持多租户 API Key 管理 +- ✨ **请求限流** - IP/用户维度的速率限制 +- ✨ **SSRF 防护** - 阻止内网地址访问 +- ✨ **幂等性支持** - Idempotency-Key 防止重复提交 +- ✨ **Prometheus 监控** - 完整的指标采集 +- ✨ **健康检查** - Ready/Live 探针,支持 K8s +- ✨ **云存储支持** - 支持阿里云 OSS 和腾讯云 COS +- ✨ **回调重试** - 带指数退避的回调重试机制 +- ✨ **HMAC 签名** - 回调请求签名验证 + +### 优化 +- ⚡ 浏览器池自愈机制(内存阈值重启、健康检查) +- ⚡ 任务状态新增 `Stalled`(卡死)状态 +- ⚡ 多队列隔离(fast/normal/slow) +- ⚡ Dry-run 预检 API + +### 技术栈升级 +- 升级到 .NET 9 +- 升级 PuppeteerSharp 到 20.x + +### 兼容性 +- 保留同步 API 接口,向后兼容 MVP 版本 + +--- + +## [1.0.0] - 2024-11-01 + +### 🚀 MVP 版本发布 + +首个公开版本,提供基础的 HTML/URL 转 PDF 功能。 + +### 新增 +- ✨ HTML 内容转 PDF +- ✨ URL 转 PDF +- ✨ HTML/URL 转图片(PNG/JPEG/WebP) +- ✨ 浏览器实例池化 +- ✨ 并发控制 +- ✨ 本地文件存储 +- ✨ 回调机制 +- ✨ Docker 容器化部署 +- ✨ 健康检查接口 + +### 技术栈 +- .NET 8 +- PuppeteerSharp 20.2.5 +- Chromium (Headless) + +--- + +## 版本对比 + +| 特性 | MVP (1.0) | 正式版 (2.0) | +|------|-----------|--------------| +| 接口模式 | 同步 | 异步 + 同步 | +| 任务管理 | ❌ | ✅ | +| 认证授权 | ❌ | ✅ | +| 请求限流 | ❌ | ✅ | +| 监控告警 | 基础 | 完整 | +| 云存储 | ❌ | ✅ | +| 管理后台 | ❌ | ✅ | +| 集群部署 | 有限 | 完整 | + +--- + +[Unreleased]: https://github.com/your-username/html-to-pdf/compare/v2.0.0...HEAD +[2.0.0]: https://github.com/your-username/html-to-pdf/compare/v1.0.0...v2.0.0 +[1.0.0]: https://github.com/your-username/html-to-pdf/releases/tag/v1.0.0 + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e0d4f99 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,178 @@ +# 贡献指南 + +感谢您对 HtmlToPdf Service 的关注!我们欢迎任何形式的贡献。 + +## 如何贡献 + +### 报告 Bug + +如果你发现了 Bug,请通过 [GitHub Issues](https://github.com/your-username/html-to-pdf/issues) 报告。创建 Issue 时请包含: + +- 问题的详细描述 +- 复现步骤 +- 预期行为 vs 实际行为 +- 运行环境(操作系统、.NET 版本、Docker 版本等) +- 相关的日志或错误信息 + +### 功能建议 + +有新功能想法?请创建一个 Feature Request Issue,描述: + +- 功能的详细说明 +- 使用场景 +- 可能的实现方案(可选) + +### 提交代码 + +1. **Fork 仓库** + +2. **克隆你的 Fork** + ```bash + git clone https://github.com/your-username/html-to-pdf.git + cd html-to-pdf + ``` + +3. **创建功能分支** + ```bash + git checkout -b feature/your-feature-name + ``` + +4. **进行开发** + - 遵循现有的代码风格 + - 添加必要的测试 + - 确保所有测试通过 + +5. **提交更改** + ```bash + git add . + git commit -m "feat: 添加某某功能" + ``` + +6. **推送到你的 Fork** + ```bash + git push origin feature/your-feature-name + ``` + +7. **创建 Pull Request** + - 描述你的更改 + - 关联相关的 Issue(如果有) + +## 开发环境设置 + +### 前置要求 + +- .NET 9.0 SDK +- Docker & Docker Compose +- Node.js 18+(用于管理后台开发) +- Redis(或使用 Docker) + +### 本地开发 + +```bash +# 启动 Redis +docker run -d --name redis -p 6379:6379 redis:7-alpine + +# 进入源码目录 +cd src + +# 还原依赖 +dotnet restore + +# 运行测试 +dotnet test + +# 启动服务 +dotnet run --project HtmlToPdfService.Api +``` + +### 管理后台开发 + +```bash +cd src/HtmlToPdfService.Admin +npm install +npm run dev +``` + +## 代码规范 + +### C# 代码规范 + +- 使用 4 空格缩进 +- 类名使用 PascalCase +- 私有字段使用 `_camelCase` 前缀 +- 方法名使用 PascalCase +- 异步方法以 `Async` 结尾 +- 添加 XML 文档注释 + +### 提交信息规范 + +使用 [Conventional Commits](https://www.conventionalcommits.org/) 格式: + +``` +(): + +[optional body] + +[optional footer] +``` + +**Type 类型:** +- `feat`: 新功能 +- `fix`: Bug 修复 +- `docs`: 文档更新 +- `style`: 代码格式(不影响功能) +- `refactor`: 重构 +- `test`: 测试相关 +- `chore`: 构建/工具相关 + +**示例:** +``` +feat(api): 添加批量任务提交接口 +fix(pool): 修复浏览器池内存泄漏问题 +docs: 更新 API 文档 +``` + +## 测试 + +### 运行测试 + +```bash +cd src +dotnet test +``` + +### 测试覆盖率 + +```bash +dotnet test --collect:"XPlat Code Coverage" +``` + +## Pull Request 检查清单 + +提交 PR 前请确保: + +- [ ] 代码可以正常编译 +- [ ] 所有测试通过 +- [ ] 添加了新功能的测试(如适用) +- [ ] 更新了相关文档 +- [ ] 提交信息符合规范 +- [ ] 代码已格式化 + +## 行为准则 + +参与本项目,请遵守以下准则: + +- 尊重所有贡献者 +- 保持友善和专业的交流 +- 接受建设性的批评 +- 关注项目和社区的最佳利益 + +## 问题? + +如有任何问题,欢迎通过以下方式联系: + +- [GitHub Discussions](https://github.com/your-username/html-to-pdf/discussions) +- [GitHub Issues](https://github.com/your-username/html-to-pdf/issues) + +再次感谢您的贡献!🎉 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..59360af --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2024-2025 HtmlToPdf Service Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..05d93c9 --- /dev/null +++ b/README.md @@ -0,0 +1,470 @@ +
+ +# 🎨 HtmlToPdf Service + +**高性能 HTML/URL 转 PDF/图片 微服务** + +基于 .NET 9 + PuppeteerSharp + Chromium 构建 + +[![.NET](https://img.shields.io/badge/.NET-9.0-512BD4?style=flat-square&logo=dotnet)](https://dotnet.microsoft.com/) +[![PuppeteerSharp](https://img.shields.io/badge/PuppeteerSharp-20.x-00D8FF?style=flat-square)](https://www.puppeteersharp.com/) +[![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?style=flat-square&logo=docker)](https://www.docker.com/) +[![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](LICENSE) + +[功能特性](#-功能特性) • +[快速开始](#-快速开始) • +[API 文档](#-api-接口) • +[配置说明](#-配置说明) • +[部署指南](#-部署指南) • +[贡献指南](#-贡献) + +
+ +--- + +## ✨ 功能特性 + +### 核心转换能力 +- 🔄 **HTML → PDF** — 支持复杂 HTML 内容转换 +- 🌐 **URL → PDF** — 支持任意网页转换,完美渲染 SPA(React/Vue/Angular) +- 🖼️ **HTML/URL → 图片** — 支持 PNG、JPEG、WebP 格式 +- 📐 **自定义选项** — 纸张大小、方向、边距、页眉页脚等 + +### 高性能架构 +- ⚡ **异步任务队列** — 基于 Redis 的任务队列,支持高并发 +- 🔧 **浏览器池化** — 实例复用、自动重启、健康检查 +- 📦 **批量处理** — 一次提交多个转换任务 +- 💾 **结果缓存** — 相同内容避免重复转换 + +### 企业级安全 +- 🔐 **API Key 认证** — 支持多租户 API Key 管理 +- 🛡️ **请求限流** — IP/用户维度的速率限制 +- 🚫 **SSRF 防护** — 阻止内网地址访问 +- 🔑 **幂等性支持** — Idempotency-Key 防止重复提交 + +### 运维友好 +- 📊 **Prometheus 监控** — 完整的指标采集 +- 🏥 **健康检查** — Ready/Live 探针,支持 K8s +- 📝 **结构化日志** — Serilog 日志输出 +- 🎛️ **管理后台** — Vue 3 构建的可视化管理界面 + +### 灵活存储 +- 💿 **本地存储** — 适合单机部署 +- ☁️ **阿里云 OSS** — 支持 CDN 加速 +- ☁️ **腾讯云 COS** — 多云存储方案 + +--- + +## 📁 项目结构 + +``` +html-to-pdf/ +├── src/ # 正式版源代码 +│ ├── HtmlToPdfService.Api/ # Web API 层 +│ ├── HtmlToPdfService.Core/ # 核心业务层 +│ ├── HtmlToPdfService.Queue/ # 任务队列层 +│ ├── HtmlToPdfService.Infrastructure/ # 基础设施层 +│ ├── HtmlToPdfService.Admin/ # 管理后台(Vue 3) +│ ├── HtmlToPdfService.Tests/ # 测试项目 +│ ├── docker-compose.yml # Docker 编排文件 +│ └── Dockerfile # Docker 构建文件 +├── mvp/ # MVP 简化版(同步模式) +├── docs/ # 设计文档 +└── README.md # 本文件 +``` + +--- + +## 🚀 快速开始 + +### 方式一:Docker Compose(推荐) + +```bash +# 克隆项目 +git clone https://github.com/your-username/html-to-pdf.git +cd html-to-pdf/src + +# 启动服务 +docker-compose up -d + +# 查看日志 +docker-compose logs -f htmltopdf +``` + +服务启动后访问: +- **API**: http://localhost:5000 +- **Swagger**: http://localhost:5000/swagger +- **健康检查**: http://localhost:5000/health +- **管理后台**: http://localhost:5000/admin + +### 方式二:本地开发 + +```bash +# 1. 启动 Redis +docker run -d --name redis -p 6379:6379 redis:7-alpine + +# 2. 进入源码目录 +cd src + +# 3. 还原依赖 +dotnet restore + +# 4. 运行服务 +dotnet run --project HtmlToPdfService.Api +``` + +### 方式三:使用 MVP 简化版 + +如果你只需要简单的同步转换功能,可以使用 MVP 版本: + +```bash +cd mvp +docker-compose up -d +``` + +> **MVP vs 正式版对比:** +> - MVP:同步模式,无需 Redis,适合低并发场景 +> - 正式版:异步队列,支持高并发、任务管理、监控告警 + +--- + +## 📖 API 接口 + +### 异步任务接口(推荐) + +#### 提交 PDF 转换任务 + +```bash +curl -X POST http://localhost:5000/api/tasks/pdf \ + -H "Content-Type: application/json" \ + -d '{ + "source": { + "type": "html", + "content": "

Hello World

这是测试内容

" + }, + "options": { + "format": "A4", + "printBackground": true + }, + "callback": { + "url": "https://your-app.com/webhook" + } + }' +``` + +**响应:** +```json +{ + "taskId": "550e8400-e29b-41d4-a716-446655440000", + "status": "pending", + "message": "任务已创建,正在排队处理", + "estimatedWaitTime": 3 +} +``` + +#### 查询任务状态 + +```bash +curl http://localhost:5000/api/tasks/{taskId} +``` + +#### 下载结果文件 + +```bash +curl -O http://localhost:5000/api/tasks/{taskId}/download +``` + +#### 提交图片转换任务 + +```bash +curl -X POST http://localhost:5000/api/tasks/image \ + -H "Content-Type: application/json" \ + -d '{ + "source": { + "type": "url", + "content": "https://www.example.com" + }, + "options": { + "format": "png", + "fullPage": true, + "width": 1920, + "height": 1080 + } + }' +``` + +#### 批量提交任务 + +```bash +curl -X POST http://localhost:5000/api/tasks/batch \ + -H "Content-Type: application/json" \ + -d '{ + "tasks": [ + { "type": "pdf", "source": { "type": "html", "content": "

Doc 1

" } }, + { "type": "pdf", "source": { "type": "html", "content": "

Doc 2

" } } + ] + }' +``` + +### 同步接口(MVP 兼容) + +```bash +# HTML 转 PDF(同步返回) +curl -X POST http://localhost:5000/api/pdf/convert/html \ + -H "Content-Type: application/json" \ + -d '{"html": "

Hello World

"}' \ + -o document.pdf + +# URL 转 PDF +curl -X POST http://localhost:5000/api/pdf/convert/url \ + -H "Content-Type: application/json" \ + -d '{"url": "https://example.com"}' \ + -o document.pdf +``` + +### 系统接口 + +```bash +# 健康检查 +curl http://localhost:5000/health + +# 就绪探针(K8s) +curl http://localhost:5000/health/ready + +# Prometheus 指标 +curl http://localhost:5000/metrics +``` + +--- + +## ⚙️ 配置说明 + +主要配置在 `appsettings.json` 中: + +```json +{ + "PdfService": { + "BrowserPool": { + "MaxInstances": 10, + "MinInstances": 2, + "MaxConcurrent": 5, + "MaxTasksPerBrowserInstance": 100, + "BrowserRestartMemoryMb": 600 + }, + "TaskQueue": { + "Type": "Redis", + "Redis": { + "ConnectionString": "localhost:6379" + }, + "WorkerCount": 5 + }, + "Storage": { + "Type": "Local", + "LocalPath": "/app/files", + "RetentionHours": 168 + }, + "Security": { + "RequireAuthentication": false, + "BlockPrivateNetworks": true + }, + "RateLimit": { + "Enabled": true, + "RequestsPerMinutePerIp": 60 + } + } +} +``` + +### 存储配置 + +
+本地存储(默认) + +```json +{ + "Storage": { + "Type": "Local", + "LocalPath": "/app/files", + "RetentionHours": 168, + "AutoCleanup": true + } +} +``` +
+ +
+阿里云 OSS + +```json +{ + "Storage": { + "Type": "OSS", + "OSS": { + "Endpoint": "oss-cn-hangzhou.aliyuncs.com", + "AccessKeyId": "your-access-key-id", + "AccessKeySecret": "your-access-key-secret", + "BucketName": "your-bucket-name", + "PathPrefix": "htmltopdf", + "UrlExpireSeconds": 3600 + } + } +} +``` +
+ +
+腾讯云 COS + +```json +{ + "Storage": { + "Type": "COS", + "COS": { + "Region": "ap-guangzhou", + "SecretId": "your-secret-id", + "SecretKey": "your-secret-key", + "BucketName": "your-bucket-1250000000", + "PathPrefix": "htmltopdf", + "UrlExpireSeconds": 3600 + } + } +} +``` +
+ +### 环境变量配置 + +所有配置都可以通过环境变量覆盖: + +```bash +export PdfService__BrowserPool__MaxInstances=20 +export PdfService__TaskQueue__Redis__ConnectionString=redis:6379 +export PdfService__Security__RequireAuthentication=true +``` + +--- + +## 🐳 部署指南 + +### Docker 单实例部署 + +```bash +cd src +docker-compose up -d +``` + +### Kubernetes 部署 + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: htmltopdf-service +spec: + replicas: 3 + template: + spec: + containers: + - name: htmltopdf + image: htmltopdf-service:2.0 + resources: + limits: + memory: "2Gi" + cpu: "2000m" + env: + - name: PdfService__TaskQueue__Redis__ConnectionString + valueFrom: + secretKeyRef: + name: redis-secret + key: connection-string +``` + +### 生产环境建议 + +| 资源 | 单实例 | 集群(3实例) | +|------|--------|--------------| +| 内存 | 2-4 GB | 6-12 GB | +| CPU | 2-4 核心 | 6-12 核心 | +| 并发能力 | 50+ QPS | 150+ QPS | + +--- + +## 📊 监控与告警 + +### Prometheus 指标 + +服务暴露 `/metrics` 端点,包含以下关键指标: + +- `conversion_tasks_total` - 转换任务总数 +- `conversion_duration_seconds` - 转换耗时分布 +- `browser_pool_instances` - 浏览器实例状态 +- `task_queue_length` - 队列长度 + +### Grafana Dashboard + +推荐监控面板: +- 任务成功率 +- 平均处理时间 +- 队列积压情况 +- 浏览器池使用率 + +--- + +## 🛠️ 技术栈 + +| 组件 | 技术 | 版本 | +|------|------|------| +| 运行时 | .NET | 9.0 | +| 浏览器控制 | PuppeteerSharp | 20.x | +| 渲染引擎 | Chromium | 自动下载 | +| 任务队列 | Redis | 7.0+ | +| 前端管理 | Vue 3 + Element Plus | 3.x | +| 容器化 | Docker | 20.x | +| 监控 | Prometheus | 2.40+ | + +--- + +## 📈 性能指标 + +| 场景 | 吞吐量 | 响应时间 | +|------|--------|----------| +| 简单 HTML → PDF | 100+ QPS | < 3s | +| 复杂 HTML → PDF | 50+ QPS | < 10s | +| URL → PDF | 30+ QPS | < 15s | +| HTML → 图片 | 150+ QPS | < 2s | + +--- + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request! + +1. Fork 本仓库 +2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 提交 Pull Request + +--- + +## 📄 许可证 + +本项目采用 [MIT 许可证](LICENSE) 开源。 + +--- + +## 📞 联系方式 + +- **Issues**: [GitHub Issues](https://github.com/your-username/html-to-pdf/issues) +- **Discussions**: [GitHub Discussions](https://github.com/your-username/html-to-pdf/discussions) + +--- + +
+ +**如果这个项目对你有帮助,请给一个 ⭐ Star!** + +Made with ❤️ by the HtmlToPdf Team + +
+ diff --git a/文档/HTML转PDF服务正式设计方案.md b/docs/HTML转PDF服务正式设计方案.md similarity index 100% rename from 文档/HTML转PDF服务正式设计方案.md rename to docs/HTML转PDF服务正式设计方案.md diff --git a/文档/html转pdf服务_正式版设计方案_v_2.md b/docs/html转pdf服务_正式版设计方案_v_2.md similarity index 100% rename from 文档/html转pdf服务_正式版设计方案_v_2.md rename to docs/html转pdf服务_正式版设计方案_v_2.md diff --git a/文档/动态配置管理功能设计文档.md b/docs/动态配置管理功能设计文档.md similarity index 100% rename from 文档/动态配置管理功能设计文档.md rename to docs/动态配置管理功能设计文档.md