"🎉 Initial release: AI Proxy Worker v1.0"

This commit is contained in:
Noah Qin
2025-08-17 19:59:10 +08:00
commit 9a971aa646
35 changed files with 13572 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

149
.gitignore vendored Normal file
View File

@@ -0,0 +1,149 @@
# Cloudflare Workers
/.wrangler/
/worker/
/dist/
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
# Environment variables
.env
.env.local
.env.development
.env.test
.env.production
# IDE and Editor files
.vscode/
.idea/
*.swp
*.swo
*~
# macOS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
# Linux
*~
# Logs
logs/
*.log
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# Build outputs
build/
dist/
out/
# Temporary files
tmp/
temp/
# Secret files
secrets.json
.secrets
*.key
*.pem
*.p12
*.pfx
# Backup files
*.bak
*.backup
*.orig
# Database
*.db
*.sqlite
# OS generated files
Icon?
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Cloudflare specific
wrangler.toml.backup
.dev.vars
# Testing
test-results/
coverage/
.coverage
# Documentation build
docs/build/
docs/_build/
# Local development
local/
dev/
# Archive files
*.tar.gz
*.zip
*.rar
# JetBrains IDEs
.idea/
*.iml
*.iws
# Sublime Text
*.sublime-project
*.sublime-workspace
# Atom
.atom/
# Security
*.p12
*.cer
*.crt
*.der
*.key
*.p7b
*.p7r
*.spc
*.pfx
# API Keys and Secrets (additional protection)
*secret*
*key*
*token*
*password*
api-keys.txt
credentials.json

89
CLAUDE.md Normal file
View File

@@ -0,0 +1,89 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Commands
### Development
- `wrangler login` - Authenticate with Cloudflare
- `wrangler publish` - Deploy the worker to Cloudflare Workers
- `wrangler publish --env development` - Deploy to development environment
- `wrangler publish --env production` - Deploy to production environment
- `wrangler tail` - View real-time logs from the deployed worker
- `wrangler secret put DEEPSEEK_API_KEY` - Set the DeepSeek API key
- `wrangler secret put PROXY_KEY` - Set the proxy authentication key
- `wrangler secret list` - List all configured secrets
- `wrangler deployments list` - View deployment history
### Testing
- Health check: `curl https://your-worker.workers.dev/`
- API test: `curl -X POST https://your-worker.workers.dev/chat -H "Authorization: Bearer YOUR_PROXY_KEY" -H "Content-Type: application/json" -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"Hello"}]}'`
## Architecture Overview
This is a **Cloudflare Workers-based AI proxy service** that securely proxies requests to AI APIs (currently DeepSeek) without exposing API keys to client applications.
### Core Components
**Main Entry Point**: `worker.js` - Single file containing all proxy logic
- Modular request validation system with separated concerns
- Upstream API communication with timeout handling
- Error handling and logging
- CORS and security headers
- Streaming response support
**Configuration**: `wrangler.toml` - Cloudflare Workers configuration
- Worker name and entry point
- Compatibility date
- Environment-specific settings
**Environment Variables** (stored as Cloudflare secrets):
- `DEEPSEEK_API_KEY` (required) - DeepSeek API authentication
- `PROXY_KEY` (optional but recommended) - Client authentication
### Request Flow
1. Client sends POST request to `/chat` endpoint
2. Worker validates authentication via `PROXY_KEY`
3. Worker validates request using modular validation functions:
- Content-Type validation (`validateContentType`)
- Content-Length validation (`validateContentLength`)
- Request body validation (`validateRequestBody`)
- Message format validation (`validateMessages`)
- Individual message validation (`validateSingleMessage`)
- Model validation (`validateModel`)
4. Worker forwards request to DeepSeek API with proper headers
5. Worker streams response back to client with appropriate headers
### Supported Models
- `deepseek-chat` - General conversation model (DeepSeek-V3)
- `deepseek-reasoner` - Complex reasoning model (DeepSeek-R1)
### Configuration Constants (in worker.js)
- `MAX_BODY_SIZE`: 1MB request limit
- `REQUEST_TIMEOUT`: 30 second timeout
- `VALIDATE_REQUEST_BODY`: Request validation toggle
- `SUPPORTED_MODELS`: Array of valid model names
### Security Features
- API key isolation (stored server-side only)
- Optional proxy authentication
- Request size limits
- Timeout protection
- CORS headers for web integration
- Security headers (XSS protection, content type validation)
### Error Handling
Standardized error responses with:
- Descriptive error codes
- Timestamps
- Proper HTTP status codes
- Detailed logging for debugging
### Code Quality Features
- **Low Cognitive Complexity**: Validation logic split into focused, single-responsibility functions
- **Modular Design**: Each validation concern separated for better maintainability
- **Error Isolation**: Specific error handling for different validation types
- **Extensible Architecture**: Easy to add new validation rules or modify existing ones
### Future Architecture
The codebase is designed to support multiple AI providers. The current implementation focuses on DeepSeek but includes extensible patterns for adding OpenAI, Claude, and other providers in future versions.

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 qinfuyao
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.

188
README.en.md Normal file
View File

@@ -0,0 +1,188 @@
# AI Proxy Worker
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./README.en.md) | [🇨🇳 中文](./README.md)
</div>
**Enterprise-grade AI API Security Proxy** - Enable your frontend applications to securely access AI services without exposing API keys, powered by Cloudflare's global edge network for millisecond response times.
> 🚀 A universal AI API proxy service based on Cloudflare Workers that allows your applications to securely call various AI APIs
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/qinfuyao/AI-Proxy-Worker)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
## ✨ Why Choose AI Proxy Worker?
- 🔐 **Security First**: API keys stored server-side only, never accessible to clients
-**Lightning Fast**: Built on Cloudflare's global edge network with millisecond response times
- 🤖 **Multi-Model Support**: Currently supports DeepSeek API, architecture designed to support future expansion to more AI service providers
- 🌊 **Streaming Support**: Full SSE streaming response support for real-time conversation experience
- 🛡️ **Production Ready**: Comprehensive error handling, security protection, and monitoring logs
- 💰 **Zero Cost Start**: Cloudflare Workers free tier is sufficient for personal use
## 🚀 5-Minute Quick Start
### 1. One-Click Deploy
```bash
# Install Wrangler CLI
npm install -g wrangler
# Clone project
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
cd ai-proxy-worker
# Login and deploy
wrangler login
wrangler secret put DEEPSEEK_API_KEY # Enter your DeepSeek API key
wrangler secret put PROXY_KEY # Set access key (optional but recommended)
wrangler publish
```
### 2. Test Immediately
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Hello!"}]
}'
```
## 🎯 Supported AI Models
| Model | Use Case | Features |
|-------|----------|----------|
| `deepseek-chat` | General conversation | DeepSeek-V3, 671B parameters, perfect for daily conversations |
| `deepseek-reasoner` | Complex reasoning | DeepSeek-R1, expert in logical reasoning and math problems |
### 🔮 Roadmap
**Current Version (v1.0)**:
- ✅ Complete DeepSeek API support
- ✅ Dual model support (conversation + reasoning)
- ✅ Streaming response and comprehensive error handling
**Planned Features**:
- 🔄 OpenAI API support
- 🔄 Claude API support
- 🔄 Gemini API support
- 🔄 Unified multi-AI routing
- 🔄 User-level access control
- 🔄 Request rate limiting and quota management
## ⚙️ Configuration
Only two environment variables needed to get started:
- `DEEPSEEK_API_KEY` - Your DeepSeek API key
- `PROXY_KEY` - Custom access key (recommended)
> 📖 **Complete Configuration Guide**: [Detailed Configuration](./docs/Configuration.en.md)
## 📱 Client Integration Examples
### iOS (Swift)
```swift
let response = try await URLSession.shared.data(for: URLRequest(
url: URL(string: "https://your-worker.workers.dev/chat")!,
headers: ["Authorization": "Bearer YOUR_PROXY_KEY"],
body: ["model": "deepseek-chat", "messages": [...]]
))
```
### JavaScript
```javascript
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello!' }]
})
});
```
## 📖 Complete Documentation
### 📚 Detailed Guides
- **[Installation Guide](./docs/Installation.en.md)** - Windows/macOS detailed installation steps
- **[Deployment Tutorial](./docs/Deployment.en.md)** - Local CLI vs Web deployment comparison
- **[API Documentation](./docs/API-Reference.en.md)** - Complete API reference and examples
- **[Configuration Guide](./docs/Configuration.en.md)** - Advanced configuration and optimization options
### 🔧 Operations Support
- **[Troubleshooting](./docs/Troubleshooting.en.md)** - Common issues and solutions
- **[Monitoring Guide](./docs/Monitoring.en.md)** - Log viewing and performance monitoring
- **[Security Best Practices](./docs/Security.en.md)** - Production environment security configuration
### 💡 Use Cases
- **[Usage Examples](./docs/Examples.en.md)** - Integration examples in various programming languages
- **[Best Practices](./docs/Best-Practices.en.md)** - Performance optimization and usage recommendations
## 🌟 Project Highlights
```javascript
// 🔐 Security: Server-side key storage
env.DEEPSEEK_API_KEY // Only stored in Cloudflare
// ⚡ Performance: Global edge computing
Cloudflare Workers // 180+ data centers
// 🛡️ Reliability: Comprehensive error handling
{
"error": "timeout",
"message": "Request timeout after 30s",
"timestamp": "2025-01-01T00:00:00.000Z"
}
// 🌊 Streaming: Real-time response
Accept: text/event-stream
```
## 🤝 Community & Support
### 💬 Get Help
- [📋 Issues](../../issues) - Report bugs or suggest features
- [💡 Discussions](../../discussions) - Community discussion and experience sharing
- [📖 Wiki](../../wiki) - Complete documentation and tutorials
### 🔧 Contribute
- [🤝 Contributing Guide](./docs/Contributing.en.md) - How to participate in project development
- [📝 Code Standards](./docs/Code-Style.en.md) - Code style and best practices
- [🧪 Testing Guide](./docs/Testing.en.md) - How to write and run tests
### 📊 Project Status
-**Stable Version**: v1.0.0
- 🔄 **Active Maintenance**: Regular updates and bug fixes
- 🌍 **Production Use**: Stable operation in multiple projects
## 🏆 Use Cases
> "AI Proxy Worker allows our iOS app to securely integrate AI features without worrying about API key exposure. Simple deployment and excellent performance!"
>
> — iOS Developer
> "Switching from DeepSeek to other AI service providers only requires a few lines of code changes. This flexibility is amazing."
>
> — Full-stack Engineer
## 📄 License
This project is licensed under the [MIT License](./LICENSE).
---
<div align="center">
**🌟 If this project helps you, please give it a Star!**
[⭐ Star](../../stargazers) • [🍴 Fork](../../fork) • [📢 Share](https://twitter.com/intent/tweet?text=AI%20Proxy%20Worker%20-%20Universal%20AI%20API%20proxy%20service%20based%20on%20Cloudflare%20Workers&url=https://github.com/qinfuyao/AI-Proxy-Worker)
</div>

188
README.md Normal file
View File

@@ -0,0 +1,188 @@
# AI Proxy Worker
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./README.en.md) | [🇨🇳 中文](./README.md)
</div>
**企业级 AI API 安全代理服务** - 让你的前端应用无需暴露 API 密钥即可安全调用 AI 服务,基于 Cloudflare 全球边缘网络提供毫秒级响应。
> 🚀 基于 Cloudflare Workers 的通用 AI API 代理服务,让你的应用安全调用各种 AI API
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/qinfuyao/AI-Proxy-Worker)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
## ✨ 为什么选择 AI Proxy Worker
- 🔐 **安全第一**API 密钥只存储在服务端,客户端永远无法获取
-**极速响应**:基于 Cloudflare 全球边缘网络,毫秒级响应
- 🤖 **多模型支持**:当前支持 DeepSeek API架构设计支持未来扩展更多 AI 服务商
- 🌊 **流式传输**:完整支持 SSE 流式响应,实时对话体验
- 🛡️ **生产就绪**:完善的错误处理、安全防护和监控日志
- 💰 **零成本起步**Cloudflare Workers 免费额度足够个人使用
## 🚀 5分钟快速开始
### 1. 一键部署
```bash
# 安装 Wrangler CLI
npm install -g wrangler
# 克隆项目
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
cd ai-proxy-worker
# 登录并部署
wrangler login
wrangler secret put DEEPSEEK_API_KEY # 输入你的 DeepSeek API 密钥
wrangler secret put PROXY_KEY # 设置访问密钥(可选但推荐)
wrangler publish
```
### 2. 立即测试
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "你好!"}]
}'
```
## 🎯 支持的 AI 模型
| 模型 | 用途 | 特点 |
|------|------|------|
| `deepseek-chat` | 通用对话 | DeepSeek-V3671B 参数,日常对话首选 |
| `deepseek-reasoner` | 复杂推理 | DeepSeek-R1逻辑推理和数学问题专家 |
### 🔮 发展路线图
**当前版本 (v1.0)**
- ✅ DeepSeek API 完整支持
- ✅ 双模型支持(对话 + 推理)
- ✅ 流式响应和完整错误处理
**计划中的功能**
- 🔄 OpenAI API 支持
- 🔄 Claude API 支持
- 🔄 Gemini API 支持
- 🔄 统一的多 AI 路由
- 🔄 用户级访问控制
- 🔄 请求限流和配额管理
## ⚙️ 配置
只需设置两个环境变量即可开始使用:
- `DEEPSEEK_API_KEY` - 你的 DeepSeek API 密钥
- `PROXY_KEY` - 自定义访问密钥(推荐)
> 📖 **完整配置指南**[详细配置说明](./docs/Configuration.md)
## 📱 客户端集成示例
### iOS (Swift)
```swift
let response = try await URLSession.shared.data(for: URLRequest(
url: URL(string: "https://your-worker.workers.dev/chat")!,
headers: ["Authorization": "Bearer YOUR_PROXY_KEY"],
body: ["model": "deepseek-chat", "messages": [...]]
))
```
### JavaScript
```javascript
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello!' }]
})
});
```
## 📖 完整文档
### 📚 详细指南
- **[安装指南](./docs/Installation.md)** - Windows/macOS 详细安装步骤
- **[部署教程](./docs/Deployment.md)** - 本地CLI vs 网页部署对比
- **[API 文档](./docs/API-Reference.md)** - 完整的 API 参考和示例
- **[配置说明](./docs/Configuration.md)** - 高级配置和优化选项
### 🔧 运维支持
- **[故障排除](./docs/Troubleshooting.md)** - 常见问题和解决方案
- **[监控指南](./docs/Monitoring.md)** - 日志查看和性能监控
- **[安全最佳实践](./docs/Security.md)** - 生产环境安全配置
### 💡 使用案例
- **[使用示例](./docs/Examples.md)** - 各种编程语言的集成示例
- **[最佳实践](./docs/Best-Practices.md)** - 性能优化和使用建议
## 🌟 项目亮点
```javascript
// 🔐 安全:密钥服务端存储
env.DEEPSEEK_API_KEY // 只在 Cloudflare 中存储
// ⚡ 性能:全局边缘计算
Cloudflare Workers // 180+ 数据中心
// 🛡️ 可靠:完善错误处理
{
"error": "timeout",
"message": "Request timeout after 30s",
"timestamp": "2025-01-01T00:00:00.000Z"
}
// 🌊 流式:实时响应
Accept: text/event-stream
```
## 🤝 社区与支持
### 💬 获取帮助
- [📋 Issues](../../issues) - 报告 Bug 或提出功能建议
- [💡 Discussions](../../discussions) - 社区讨论和经验分享
- [📖 Wiki](../../wiki) - 完整文档和教程
### 🔧 参与贡献
- [🤝 贡献指南](./docs/Contributing.md) - 如何参与项目开发
- [📝 代码规范](./docs/Code-Style.md) - 代码风格和最佳实践
- [🧪 测试指南](./docs/Testing.md) - 如何编写和运行测试
### 📊 项目状态
-**稳定版本**v1.0.0
- 🔄 **活跃维护**:定期更新和 Bug 修复
- 🌍 **生产使用**:已在多个项目中稳定运行
## 🏆 使用案例
> "AI Proxy Worker 让我们的 iOS 应用可以安全地集成 AI 功能,无需担心 API 密钥泄露。部署简单,性能出色!"
>
> — iOS 开发者
> "从 DeepSeek 切换到其他 AI 服务商只需要几行代码修改,这种灵活性太棒了。"
>
> — 全栈工程师
## 📄 许可证
本项目采用 [MIT License](./LICENSE) 开源许可证。
---
<div align="center">
**🌟 如果这个项目对你有帮助,请给个 Star 支持一下!**
[⭐ Star](../../stargazers) • [🍴 Fork](../../fork) • [📢 分享](https://twitter.com/intent/tweet?text=AI%20Proxy%20Worker%20-%20%E5%9F%BA%E4%BA%8E%20Cloudflare%20Workers%20%E7%9A%84%E9%80%9A%E7%94%A8%20AI%20API%20%E4%BB%A3%E7%90%86%E6%9C%8D%E5%8A%A1&url=https://github.com/qinfuyao/AI-Proxy-Worker)
</div>

636
docs/API-Reference.en.md Normal file
View File

@@ -0,0 +1,636 @@
# API Reference
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./API-Reference.en.md) | [🇨🇳 中文](./API-Reference.md)
</div>
AI Proxy Worker provides a simple yet powerful RESTful API that is fully compatible with OpenAI's Chat Completions API format, allowing you to easily integrate it into existing projects.
> **Current Support**: DeepSeek API (v1.0)
> **Future Plans**: Multi-AI service provider support including OpenAI, Claude, Gemini (v2.0)
## 🌐 Basic Information
### Base URL
```
https://your-worker.workers.dev
```
### Authentication
```http
Authorization: Bearer YOUR_PROXY_KEY
```
### Content-Type
```http
Content-Type: application/json
```
## 📚 API Endpoints
### 1. Health Check
Check service status and connectivity.
**Request:**
```http
GET /
```
**Response:**
```json
{
"status": "ok",
"service": "AI Proxy Worker",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Example:**
```bash
curl https://your-worker.workers.dev/
```
### 2. Chat Completions
Interact with AI models, supports both streaming and non-streaming responses.
**Request:**
```http
POST /chat
```
**Headers:**
```http
Authorization: Bearer YOUR_PROXY_KEY
Content-Type: application/json
Accept: application/json # Non-streaming
Accept: text/event-stream # Streaming
```
**Request Body:**
```json
{
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "You are a helpful AI assistant."
},
{
"role": "user",
"content": "Hello!"
}
],
"stream": false,
"max_tokens": 2048
}
```
## 🤖 Supported Models
### deepseek-chat
- **Use Case**: General conversation and text generation
- **Architecture**: Based on DeepSeek-V3 architecture
- **Features**: Suitable for daily conversations, content creation, text understanding
- **Context Length**: 64K tokens
- **Recommended Scenarios**: General text generation, conversational applications
### deepseek-reasoner
- **Use Case**: Complex reasoning and logical thinking
- **Architecture**: Based on DeepSeek-R1 architecture
- **Features**: Math problems, logical reasoning, code analysis, complex reasoning
- **Context Length**: 64K tokens
- **Recommended Scenarios**: Tasks requiring deep thinking
> **Note**: Model specifications and capabilities may change with DeepSeek updates. Check [DeepSeek Official Documentation](https://platform.deepseek.com/) for latest information.
## 📝 Request Parameters
### Required Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `model` | string | Model name to use |
| `messages` | array | Array of conversation messages |
### Optional Parameters
| Parameter | Type | Default | Description | Support Status |
|-----------|------|---------|-------------|----------------|
| `stream` | boolean | false | Enable streaming response | ✅ Fully supported |
| `max_tokens` | number | - | Maximum tokens to generate | ✅ Fully supported |
| `temperature` | number | 1.0 | Control randomness (0-2) | ⚠️ May not work |
| `top_p` | number | 1.0 | Nucleus sampling parameter (0-1) | ⚠️ May not work |
| `frequency_penalty` | number | 0 | Frequency penalty (-2 to 2) | ⚠️ May not work |
| `presence_penalty` | number | 0 | Presence penalty (-2 to 2) | ⚠️ May not work |
| `stop` | array/string | null | Stop sequences | ✅ Supported |
| `seed` | number | null | Random seed for consistent output | ✅ Supported |
> **Note**: Parameters marked "⚠️ May not work" may not have the expected effect due to DeepSeek API limitations. We recommend primarily using `stream`, `max_tokens`, `stop`, and `seed` parameters.
### Messages Format
Each message object contains:
```json
{
"role": "user|assistant|system",
"content": "Message content"
}
```
**Role Descriptions:**
- `system`: System prompt, defines AI behavior
- `user`: User input
- `assistant`: AI response
## 📤 Response Format
### Non-streaming Response
**Success Response:**
```json
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! I'm DeepSeek, happy to help you."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 15,
"total_tokens": 35
}
}
```
### Streaming Response
When `stream: true` is enabled, response is in Server-Sent Events (SSE) format:
```
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
## ⚠️ **Important: Parameter Compatibility**
According to DeepSeek official documentation, the following parameters may not work as expected:
- `temperature` - May be ignored, DeepSeek API may use fixed temperature values
- `top_p` - May not work
- `frequency_penalty` - May not work
- `presence_penalty` - May not work
**Recommended Approach:**
- Primarily use `model`, `messages`, `max_tokens`, `stream`, and `stop` parameters
- To control generation behavior, use `system` messages to guide the model
- You can try these parameters during testing, but don't rely on their effects
**Example - Recommended Request Format:**
```json
{
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "Please answer concisely, don't be overly detailed."
},
{
"role": "user",
"content": "What is artificial intelligence?"
}
],
"max_tokens": 500,
"stream": false
}
```
## 🔧 Complete Examples
### cURL Examples
**Non-streaming Request:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "You are a professional programming assistant."
},
{
"role": "user",
"content": "Please write a Python quicksort function for me."
}
],
"max_tokens": 1000
}'
```
**Streaming Request:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "user", "content": "Write a poem about programming"}
],
"stream": true
}'
```
### JavaScript Examples
**Basic Call:**
```javascript
async function callAI(message) {
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'user', content: message }
],
max_tokens: 1000
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content;
}
// Usage example
callAI('Hello, please introduce yourself')
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
```
**Streaming Call:**
```javascript
async function streamAI(message, onChunk) {
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: message }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content;
if (content) {
onChunk(content);
}
} catch (e) {
// Ignore parsing errors
}
}
}
}
} finally {
reader.releaseLock();
}
}
// Usage example
streamAI('Write a story about AI', (chunk) => {
process.stdout.write(chunk); // Real-time output
});
```
### Python Examples
**Basic Call:**
```python
import requests
import json
def call_ai(message, model="deepseek-chat"):
url = "https://your-worker.workers.dev/chat"
headers = {
"Authorization": "Bearer YOUR_PROXY_KEY",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": [
{"role": "user", "content": message}
],
"max_tokens": 1000
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
return data["choices"][0]["message"]["content"]
# Usage example
result = call_ai("Please explain what machine learning is")
print(result)
```
**Streaming Call:**
```python
import requests
import json
def stream_ai(message, model="deepseek-chat"):
url = "https://your-worker.workers.dev/chat"
headers = {
"Authorization": "Bearer YOUR_PROXY_KEY",
"Content-Type": "application/json",
"Accept": "text/event-stream"
}
payload = {
"model": model,
"messages": [{"role": "user", "content": message}],
"stream": True
}
response = requests.post(url, headers=headers, json=payload, stream=True)
response.raise_for_status()
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:]
if data == '[DONE]':
break
try:
parsed = json.loads(data)
content = parsed["choices"][0]["delta"].get("content")
if content:
print(content, end='', flush=True)
except json.JSONDecodeError:
continue
# Usage example
stream_ai("Write a poem about spring")
```
### iOS Swift Examples
```swift
import Foundation
class AIProxyClient {
private let baseURL = "https://your-worker.workers.dev"
private let apiKey = "YOUR_PROXY_KEY"
func chatCompletion(
model: String = "deepseek-chat",
messages: [[String: String]],
maxTokens: Int = 1000
) async throws -> String {
guard let url = URL(string: "\(baseURL)/chat") else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestBody: [String: Any] = [
"model": model,
"messages": messages,
"max_tokens": maxTokens
]
request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw APIError.requestFailed
}
let result = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let choices = result["choices"] as! [[String: Any]]
let message = choices[0]["message"] as! [String: Any]
return message["content"] as! String
}
}
enum APIError: Error {
case invalidURL
case requestFailed
}
// Usage example
let client = AIProxyClient()
Task {
do {
let response = try await client.chatCompletion(
messages: [
["role": "user", "content": "Hello, please introduce yourself"]
]
)
print(response)
} catch {
print("Error: \(error)")
}
}
```
## ❌ Error Handling
### Error Response Format
All errors return a unified JSON format:
```json
{
"error": "error_type",
"details": "Detailed error message",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
### Common Error Codes
| HTTP Status | Error Type | Description |
|-------------|------------|-------------|
| 400 | `invalid_request` | Request format error |
| 401 | `unauthorized` | Authentication failed |
| 404 | `not_found` | Endpoint not found |
| 413 | `payload_too_large` | Request body too large |
| 500 | `internal_error` | Internal server error |
| 502 | `upstream_error` | Upstream API error |
| 504 | `timeout` | Request timeout |
### Error Handling Example
```javascript
async function handleAPICall(message) {
try {
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: message }]
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error (${response.status}): ${errorData.error} - ${errorData.details}`);
}
return await response.json();
} catch (error) {
console.error('API call failed:', error.message);
// Handle different error types
if (error.message.includes('401')) {
console.log('Please check if API key is correct');
} else if (error.message.includes('504')) {
console.log('Request timeout, please try again later');
} else if (error.message.includes('413')) {
console.log('Request content too long, please reduce input');
}
throw error;
}
}
```
## 🔒 Security Best Practices
### 1. API Key Management
- Never hardcode `PROXY_KEY` in client code
- Use environment variables or secure configuration management
- Rotate keys regularly
### 2. Request Validation
- Validate user input to prevent injection attacks
- Limit request frequency to prevent abuse
- Log and monitor abnormal requests
### 3. Content Filtering
```javascript
function sanitizeInput(content) {
// Remove potentially malicious content
return content
.replace(/<script[^>]*>.*?<\/script>/gi, '')
.replace(/<[^>]*>/g, '')
.trim();
}
const sanitizedMessage = sanitizeInput(userInput);
```
## 📊 Usage Limits
### Cloudflare Workers Limits
- **Request Timeout**: 30 seconds (configurable)
- **Request Body Size**: 1MB (configurable)
- **Concurrent Requests**: 1000/minute (free tier)
- **CPU Time**: 10ms (free tier)
### DeepSeek API Limits
- **Rate Limits**: Based on your DeepSeek account plan
- **Context Length**: 64K tokens
- **Concurrent Connections**: Based on account type
## 🚀 Performance Optimization Tips
### 1. Caching Strategy
```javascript
// Simple memory cache example
const cache = new Map();
function getCachedResponse(key) {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < 300000) { // 5-minute cache
return cached.data;
}
return null;
}
```
### 2. Request Optimization
- Set reasonable `max_tokens` to avoid unnecessarily long responses
- Use appropriate `temperature` values
- Use faster models for simple tasks
### 3. Streaming Response
- Use streaming response for long text generation to improve user experience
- Implement appropriate error retry mechanisms
- Consider implementing request cancellation
---
**Need More Help?** 👉 [View Usage Examples](./Examples.en) | [Troubleshooting](./Troubleshooting.en)

636
docs/API-Reference.md Normal file
View File

@@ -0,0 +1,636 @@
# API 参考文档
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./API-Reference.en.md) | [🇨🇳 中文](./API-Reference.md)
</div>
AI Proxy Worker 提供了简洁而强大的 RESTful API完全兼容 OpenAI 的 Chat Completions API 格式,让你可以轻松集成到现有项目中。
> **当前支持**: DeepSeek API (v1.0)
> **未来计划**: OpenAI、Claude、Gemini 等多 AI 服务商支持 (v2.0)
## 🌐 基础信息
### 基础 URL
```
https://your-worker.workers.dev
```
### 认证方式
```http
Authorization: Bearer YOUR_PROXY_KEY
```
### Content-Type
```http
Content-Type: application/json
```
## 📚 API 端点
### 1. 健康检查
检查服务状态和连通性。
**请求:**
```http
GET /
```
**响应:**
```json
{
"status": "ok",
"service": "AI Proxy Worker",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**示例:**
```bash
curl https://your-worker.workers.dev/
```
### 2. 聊天补全
与 AI 模型进行对话,支持流式和非流式响应。
**请求:**
```http
POST /chat
```
**请求头:**
```http
Authorization: Bearer YOUR_PROXY_KEY
Content-Type: application/json
Accept: application/json # 非流式
Accept: text/event-stream # 流式
```
**请求体:**
```json
{
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "你是一个有用的AI助手。"
},
{
"role": "user",
"content": "你好!"
}
],
"stream": false,
"max_tokens": 2048
}
```
## 🤖 支持的模型
### deepseek-chat
- **用途**:通用对话和文本生成
- **架构**:基于 DeepSeek-V3 架构
- **特点**:适合日常对话、内容创作、文本理解
- **上下文长度**64K tokens
- **推荐场景**:通用文本生成、对话应用
### deepseek-reasoner
- **用途**:复杂推理和逻辑思考
- **架构**:基于 DeepSeek-R1 架构
- **特点**:数学问题、逻辑推理、代码分析、复杂推理
- **上下文长度**64K tokens
- **推荐场景**:需要深度思考的任务
> **注意**:模型的具体参数和能力可能会根据 DeepSeek 的更新而变化。建议查看 [DeepSeek 官方文档](https://platform.deepseek.com/) 获取最新信息。
## 📝 请求参数详解
### 必需参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `model` | string | 要使用的模型名称 |
| `messages` | array | 对话消息数组 |
### 可选参数
| 参数 | 类型 | 默认值 | 说明 | 支持状态 |
|------|------|--------|------|----------|
| `stream` | boolean | false | 是否启用流式响应 | ✅ 完全支持 |
| `max_tokens` | number | - | 最大生成 tokens 数量 | ✅ 完全支持 |
| `temperature` | number | 1.0 | 控制随机性 (0-2) | ⚠️ 可能不生效 |
| `top_p` | number | 1.0 | 核采样参数 (0-1) | ⚠️ 可能不生效 |
| `frequency_penalty` | number | 0 | 频率惩罚 (-2 to 2) | ⚠️ 可能不生效 |
| `presence_penalty` | number | 0 | 存在惩罚 (-2 to 2) | ⚠️ 可能不生效 |
| `stop` | array/string | null | 停止序列 | ✅ 支持 |
| `seed` | number | null | 随机种子,确保输出一致性 | ✅ 支持 |
> **注意**:标记为 "⚠️ 可能不生效" 的参数由于 DeepSeek API 的限制,设置后可能不会产生预期效果。建议主要使用 `stream`、`max_tokens`、`stop` 和 `seed` 参数。
### Messages 格式
每个消息对象包含:
```json
{
"role": "user|assistant|system",
"content": "消息内容"
}
```
**角色说明:**
- `system`: 系统提示,定义 AI 的行为
- `user`: 用户输入
- `assistant`: AI 回复
## 📤 响应格式
### 非流式响应
**成功响应:**
```json
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!我是 DeepSeek很高兴为你提供帮助。"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 15,
"total_tokens": 35
}
}
```
### 流式响应
启用 `stream: true` 时,响应为 Server-Sent Events (SSE) 格式:
```
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{"content":"你好"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{"content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"deepseek-chat","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
## ⚠️ **重要说明:参数兼容性**
根据 DeepSeek 官方文档,以下参数可能不会按预期工作:
- `temperature` - 可能被忽略DeepSeek API 可能使用固定的温度值
- `top_p` - 可能不生效
- `frequency_penalty` - 可能不生效
- `presence_penalty` - 可能不生效
**推荐做法:**
- 主要使用 `model``messages``max_tokens``stream``stop` 参数
- 如果需要控制生成行为,建议通过 `system` 消息来指导模型
- 测试时可以尝试这些参数,但不要依赖它们的效果
**示例 - 推荐的请求格式:**
```json
{
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "请用简洁的语言回答,不要过于详细。"
},
{
"role": "user",
"content": "什么是人工智能?"
}
],
"max_tokens": 500,
"stream": false
}
```
## 🔧 完整示例
### cURL 示例
**非流式请求:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [
{
"role": "system",
"content": "你是一个专业的编程助手。"
},
{
"role": "user",
"content": "请帮我写一个 Python 快速排序函数。"
}
],
"max_tokens": 1000
}'
```
**流式请求:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "user", "content": "写一首关于编程的诗"}
],
"stream": true
}'
```
### JavaScript 示例
**基础调用:**
```javascript
async function callAI(message) {
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'user', content: message }
],
max_tokens: 1000
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content;
}
// 使用示例
callAI('你好,请介绍一下你自己')
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
```
**流式调用:**
```javascript
async function streamAI(message, onChunk) {
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: message }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content;
if (content) {
onChunk(content);
}
} catch (e) {
// 忽略解析错误
}
}
}
}
} finally {
reader.releaseLock();
}
}
// 使用示例
streamAI('写一个关于 AI 的故事', (chunk) => {
process.stdout.write(chunk); // 实时输出
});
```
### Python 示例
**基础调用:**
```python
import requests
import json
def call_ai(message, model="deepseek-chat"):
url = "https://your-worker.workers.dev/chat"
headers = {
"Authorization": "Bearer YOUR_PROXY_KEY",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": [
{"role": "user", "content": message}
],
"max_tokens": 1000
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
return data["choices"][0]["message"]["content"]
# 使用示例
result = call_ai("请解释什么是机器学习")
print(result)
```
**流式调用:**
```python
import requests
import json
def stream_ai(message, model="deepseek-chat"):
url = "https://your-worker.workers.dev/chat"
headers = {
"Authorization": "Bearer YOUR_PROXY_KEY",
"Content-Type": "application/json",
"Accept": "text/event-stream"
}
payload = {
"model": model,
"messages": [{"role": "user", "content": message}],
"stream": True
}
response = requests.post(url, headers=headers, json=payload, stream=True)
response.raise_for_status()
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
data = line[6:]
if data == '[DONE]':
break
try:
parsed = json.loads(data)
content = parsed["choices"][0]["delta"].get("content")
if content:
print(content, end='', flush=True)
except json.JSONDecodeError:
continue
# 使用示例
stream_ai("写一首关于春天的诗")
```
### iOS Swift 示例
```swift
import Foundation
class AIProxyClient {
private let baseURL = "https://your-worker.workers.dev"
private let apiKey = "YOUR_PROXY_KEY"
func chatCompletion(
model: String = "deepseek-chat",
messages: [[String: String]],
maxTokens: Int = 1000
) async throws -> String {
guard let url = URL(string: "\(baseURL)/chat") else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestBody: [String: Any] = [
"model": model,
"messages": messages,
"max_tokens": maxTokens
]
request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw APIError.requestFailed
}
let result = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let choices = result["choices"] as! [[String: Any]]
let message = choices[0]["message"] as! [String: Any]
return message["content"] as! String
}
}
enum APIError: Error {
case invalidURL
case requestFailed
}
// 使用示例
let client = AIProxyClient()
Task {
do {
let response = try await client.chatCompletion(
messages: [
["role": "user", "content": "你好,请介绍一下你自己"]
]
)
print(response)
} catch {
print("Error: \(error)")
}
}
```
## ❌ 错误处理
### 错误响应格式
所有错误都返回统一的 JSON 格式:
```json
{
"error": "error_type",
"details": "详细错误信息",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
### 常见错误码
| HTTP 状态码 | 错误类型 | 说明 |
|-------------|----------|------|
| 400 | `invalid_request` | 请求格式错误 |
| 401 | `unauthorized` | 认证失败 |
| 404 | `not_found` | 端点不存在 |
| 413 | `payload_too_large` | 请求体过大 |
| 500 | `internal_error` | 服务器内部错误 |
| 502 | `upstream_error` | 上游 API 错误 |
| 504 | `timeout` | 请求超时 |
### 错误处理示例
```javascript
async function handleAPICall(message) {
try {
const response = await fetch('https://your-worker.workers.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: message }]
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error (${response.status}): ${errorData.error} - ${errorData.details}`);
}
return await response.json();
} catch (error) {
console.error('API call failed:', error.message);
// 根据错误类型进行处理
if (error.message.includes('401')) {
console.log('请检查 API 密钥是否正确');
} else if (error.message.includes('504')) {
console.log('请求超时,请稍后重试');
} else if (error.message.includes('413')) {
console.log('请求内容过长,请减少输入');
}
throw error;
}
}
```
## 🔒 安全最佳实践
### 1. API 密钥管理
- 永远不要在客户端代码中硬编码 `PROXY_KEY`
- 使用环境变量或安全的配置管理
- 定期轮换密钥
### 2. 请求验证
- 验证用户输入,防止注入攻击
- 限制请求频率,防止滥用
- 记录和监控异常请求
### 3. 内容过滤
```javascript
function sanitizeInput(content) {
// 移除潜在的恶意内容
return content
.replace(/<script[^>]*>.*?<\/script>/gi, '')
.replace(/<[^>]*>/g, '')
.trim();
}
const sanitizedMessage = sanitizeInput(userInput);
```
## 📊 使用限制
### Cloudflare Workers 限制
- **请求超时**30秒可配置
- **请求体大小**1MB可配置
- **并发请求**1000个/分钟(免费版)
- **CPU 时间**10ms免费版
### DeepSeek API 限制
- **速率限制**:根据你的 DeepSeek 账户套餐
- **上下文长度**128K tokens
- **并发连接**:根据账户类型
## 🚀 性能优化建议
### 1. 缓存策略
```javascript
// 简单的内存缓存示例
const cache = new Map();
function getCachedResponse(key) {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < 300000) { // 5分钟缓存
return cached.data;
}
return null;
}
```
### 2. 请求优化
- 合理设置 `max_tokens` 避免不必要的长响应
- 使用适当的 `temperature`
- 对于简单任务使用更快的模型
### 3. 流式响应
- 对于长文本生成,使用流式响应提升用户体验
- 实现适当的错误重试机制
- 考虑实现请求取消功能
---
**需要更多帮助?** 👉 [查看使用示例](./Examples) | [故障排除](./Troubleshooting)

448
docs/Best-Practices.en.md Normal file
View File

@@ -0,0 +1,448 @@
# Best Practices
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Best-Practices.en.md) | [🇨🇳 中文](./Best-Practices.md)
</div>
This guide provides best practice recommendations for using AI Proxy Worker, helping you maximize the advantages of this proxy service while ensuring security and performance.
## 🔐 Security Best Practices
### 1. API Key Management
**✅ Recommended Practices:**
```bash
# Use strong keys as proxy access keys
wrangler secret put PROXY_KEY
# Input: sk-proxy-your-very-secure-random-key-2025
# Rotate keys regularly
wrangler secret put DEEPSEEK_API_KEY # Update DeepSeek key
wrangler secret put PROXY_KEY # Update proxy key
```
**❌ Avoid These Practices:**
```javascript
// Don't hardcode keys in client code
const API_KEY = 'your-secret-key'; // Wrong!
// Don't use simple keys
PROXY_KEY: '123456' // Too simple!
```
### 2. Access Control
**Production CORS Configuration:**
```javascript
// Restrict specific domains in worker.js
const CORS_HEADERS = {
'Access-Control-Allow-Origin': 'https://yourdomain.com', // Restrict to specific domain
'Access-Control-Allow-Methods': 'POST, OPTIONS', // Only allow necessary methods
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
```
### 3. Key Rotation Strategy
```bash
# Recommend monthly rotation
echo "$(date): Updating API keys" >> key-rotation.log
wrangler secret put DEEPSEEK_API_KEY
wrangler secret put PROXY_KEY
```
## ⚡ Performance Optimization
### 1. Request Optimization
**Reasonable Configuration Parameters:**
```javascript
// worker.js CONFIG optimization
const CONFIG = {
MAX_BODY_SIZE: 512 * 1024, // 512KB, suitable for most conversations
REQUEST_TIMEOUT: 30000, // 30 seconds, balance performance and reliability
VALIDATE_REQUEST_BODY: false, // Disable validation for better performance
};
```
**Client Request Optimization:**
```javascript
// Use appropriate models
const request = {
model: 'deepseek-chat', // Use chat model for daily conversations
messages: messages,
max_tokens: 1000, // Limit response length
temperature: 0.7, // Balance creativity and consistency
};
// Use reasoner model for complex reasoning tasks
const complexRequest = {
model: 'deepseek-reasoner', // For math and logical reasoning tasks
messages: messages,
max_tokens: 2000, // Reasoning tasks may need more tokens
};
```
### 2. Streaming Response Usage
**Recommended for Real-time Conversations:**
```javascript
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
'Accept': 'text/event-stream', // Enable streaming response
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: messages,
stream: true, // Enable streaming
})
});
```
### 3. Caching Strategy
```javascript
// Client-side simple caching
const messageCache = new Map();
function getCachedResponse(messageHash) {
return messageCache.get(messageHash);
}
function setCachedResponse(messageHash, response) {
// Limit cache size
if (messageCache.size > 100) {
const firstKey = messageCache.keys().next().value;
messageCache.delete(firstKey);
}
messageCache.set(messageHash, response);
}
```
## 🛡️ Error Handling
### 1. Client Error Handling
```javascript
async function callAI(messages) {
try {
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: messages,
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error: ${errorData.error} - ${errorData.details || errorData.message || 'Unknown error'}`);
}
return await response.json();
} catch (error) {
console.error('AI API call failed:', error);
// Handle based on error type
if (error.message.includes('timeout')) {
return { error: 'Request timeout, please try again later' };
} else if (error.message.includes('unauthorized')) {
return { error: 'Authentication failed, please check access key' };
} else {
return { error: 'Service temporarily unavailable, please try again later' };
}
}
}
```
### 2. Retry Mechanism
```javascript
async function callAIWithRetry(messages, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const result = await callAI(messages);
if (!result.error) {
return result;
}
// Don't retry on authentication errors
if (result.error.includes('Authentication failed')) {
throw new Error(result.error);
}
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
// Exponential backoff delay
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
);
}
}
}
```
## 📊 Monitoring and Logging
### 1. Client Monitoring
```javascript
// Request statistics
const stats = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageResponseTime: 0,
};
async function monitoredAICall(messages) {
const startTime = Date.now();
stats.totalRequests++;
try {
const result = await callAI(messages);
stats.successfulRequests++;
// Update average response time
const responseTime = Date.now() - startTime;
stats.averageResponseTime =
(stats.averageResponseTime * (stats.successfulRequests - 1) + responseTime)
/ stats.successfulRequests;
return result;
} catch (error) {
stats.failedRequests++;
throw error;
}
}
```
### 2. Worker Log Monitoring
```bash
# View Worker logs in real-time
wrangler tail
# Check deployment status
wrangler deployments list
# View usage statistics
wrangler metrics
```
## 🔧 Development Environment Configuration
### 1. Environment Separation
```toml
# wrangler.toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-08-17"
# Development environment
[env.development]
name = "ai-proxy-worker-dev"
vars = { ENVIRONMENT = "development" }
# Production environment
[env.production]
name = "ai-proxy-worker-prod"
vars = { ENVIRONMENT = "production" }
```
**Deploy to Different Environments:**
```bash
# Development environment
wrangler publish --env development
# Production environment
wrangler publish --env production
```
### 2. Local Development
```bash
# Local development server
wrangler dev
# Specify port
wrangler dev --port 8080
# Local testing
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
## 📱 Mobile Application Integration
### iOS (Swift)
```swift
class AIProxyService {
private let baseURL = "https://your-worker.workers.dev"
private let proxyKey = "YOUR_PROXY_KEY"
func chat(messages: [[String: String]]) async throws -> ChatResponse {
let url = URL(string: "\(baseURL)/chat")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(proxyKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = [
"model": "deepseek-chat",
"messages": messages
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw AIError.requestFailed
}
return try JSONDecoder().decode(ChatResponse.self, from: data)
}
}
```
### Android (Kotlin)
```kotlin
class AIProxyService {
private val baseUrl = "https://your-worker.workers.dev"
private val proxyKey = "YOUR_PROXY_KEY"
private val client = OkHttpClient()
suspend fun chat(messages: List<Message>): ChatResponse {
val requestBody = JSONObject().apply {
put("model", "deepseek-chat")
put("messages", JSONArray(messages.map { it.toJson() }))
}
val request = Request.Builder()
.url("$baseUrl/chat")
.post(requestBody.toString().toRequestBody("application/json".toMediaType()))
.addHeader("Authorization", "Bearer $proxyKey")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException("Request failed: ${response.code}")
}
val responseBody = response.body?.string() ?: throw IOException("Empty response")
Gson().fromJson(responseBody, ChatResponse::class.java)
}
}
}
```
## 🚀 Production Deployment Recommendations
### 1. Pre-deployment Checklist
- [ ] Strong keys set (PROXY_KEY and DEEPSEEK_API_KEY)
- [ ] CORS domains restricted (production environment)
- [ ] Appropriate timeout and size limits configured
- [ ] All API endpoints tested
- [ ] Monitoring and logging set up
- [ ] Error handling and retry mechanisms prepared
### 2. Performance Benchmarking
```bash
# Use Apache Bench for stress testing
ab -n 100 -c 10 -H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-p test-payload.json \
https://your-worker.workers.dev/chat
# test-payload.json content:
echo '{"model":"deepseek-chat","messages":[{"role":"user","content":"Hello"}]}' > test-payload.json
```
### 3. Capacity Planning
According to Cloudflare Workers limits:
- **Free Tier**: 100,000 requests per day
- **Paid Tier**: Unlimited, pay-per-use
- **Memory**: Maximum 128MB
- **CPU Time**: Maximum 30 seconds
## 💡 Usage Tips
### 1. Model Selection Guide
```javascript
// Choose appropriate model
function selectModel(taskType) {
switch (taskType) {
case 'chat':
case 'creative':
case 'translation':
return 'deepseek-chat'; // Daily conversation, creative writing, translation
case 'math':
case 'logic':
case 'analysis':
return 'deepseek-reasoner'; // Math, logical reasoning, analysis
default:
return 'deepseek-chat'; // Default to chat model
}
}
```
### 2. Message Optimization
```javascript
// Optimize conversation context
function optimizeMessages(messages, maxTokens = 4000) {
// Keep system messages and recent conversations
const systemMessages = messages.filter(m => m.role === 'system');
const recentMessages = messages.filter(m => m.role !== 'system').slice(-10);
return [...systemMessages, ...recentMessages];
}
```
### 3. Error Recovery Strategy
```javascript
// Intelligent error recovery
async function resilientAICall(messages) {
try {
return await callAI(messages);
} catch (error) {
if (error.message.includes('too_long')) {
// If message too long, try to shorten
const shorterMessages = optimizeMessages(messages, 2000);
return await callAI(shorterMessages);
}
throw error;
}
}
```
---
**Next Steps?** 👉 [View Usage Examples](./Examples.en.md) | [Monitoring Guide](./Monitoring.en.md)

448
docs/Best-Practices.md Normal file
View File

@@ -0,0 +1,448 @@
# 最佳实践
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Best-Practices.en.md) | [🇨🇳 中文](./Best-Practices.md)
</div>
这份指南提供了使用 AI Proxy Worker 的最佳实践建议,帮助你充分利用这个代理服务的优势,同时确保安全性和性能。
## 🔐 安全最佳实践
### 1. API 密钥管理
**✅ 推荐做法:**
```bash
# 使用强密钥作为代理访问密钥
wrangler secret put PROXY_KEY
# 输入sk-proxy-your-very-secure-random-key-2025
# 定期轮换密钥
wrangler secret put DEEPSEEK_API_KEY # 更新 DeepSeek 密钥
wrangler secret put PROXY_KEY # 更新代理密钥
```
**❌ 避免做法:**
```javascript
// 不要在客户端代码中硬编码密钥
const API_KEY = 'your-secret-key'; // 错误!
// 不要使用简单密钥
PROXY_KEY: '123456' // 太简单!
```
### 2. 访问控制
**生产环境 CORS 配置:**
```javascript
// worker.js 中限制特定域名
const CORS_HEADERS = {
'Access-Control-Allow-Origin': 'https://yourdomain.com', // 限制特定域名
'Access-Control-Allow-Methods': 'POST, OPTIONS', // 只允许必要方法
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
```
### 3. 密钥轮换策略
```bash
# 建议每月轮换一次
echo "$(date): 更新 API 密钥" >> key-rotation.log
wrangler secret put DEEPSEEK_API_KEY
wrangler secret put PROXY_KEY
```
## ⚡ 性能优化
### 1. 请求优化
**合理的配置参数:**
```javascript
// worker.js CONFIG 优化
const CONFIG = {
MAX_BODY_SIZE: 512 * 1024, // 512KB适合大多数对话
REQUEST_TIMEOUT: 30000, // 30秒平衡性能和可靠性
VALIDATE_REQUEST_BODY: false, // 关闭验证以提高性能
};
```
**客户端请求优化:**
```javascript
// 使用适当的模型
const request = {
model: 'deepseek-chat', // 日常对话使用 chat 模型
messages: messages,
max_tokens: 1000, // 限制响应长度
temperature: 0.7, // 平衡创造性和一致性
};
// 复杂推理任务使用 reasoner 模型
const complexRequest = {
model: 'deepseek-reasoner', // 数学、逻辑推理任务
messages: messages,
max_tokens: 2000, // 推理任务可能需要更多 token
};
```
### 2. 流式响应使用
**推荐用于实时对话:**
```javascript
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
'Accept': 'text/event-stream', // 启用流式响应
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: messages,
stream: true, // 启用流式传输
})
});
```
### 3. 缓存策略
```javascript
// 客户端实现简单缓存
const messageCache = new Map();
function getCachedResponse(messageHash) {
return messageCache.get(messageHash);
}
function setCachedResponse(messageHash, response) {
// 限制缓存大小
if (messageCache.size > 100) {
const firstKey = messageCache.keys().next().value;
messageCache.delete(firstKey);
}
messageCache.set(messageHash, response);
}
```
## 🛡️ 错误处理
### 1. 客户端错误处理
```javascript
async function callAI(messages) {
try {
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PROXY_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: messages,
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error: ${errorData.error} - ${errorData.details || errorData.message || 'Unknown error'}`);
}
return await response.json();
} catch (error) {
console.error('AI API调用失败:', error);
// 根据错误类型处理
if (error.message.includes('timeout')) {
return { error: '请求超时,请稍后重试' };
} else if (error.message.includes('unauthorized')) {
return { error: '认证失败,请检查访问密钥' };
} else {
return { error: '服务暂时不可用,请稍后重试' };
}
}
}
```
### 2. 重试机制
```javascript
async function callAIWithRetry(messages, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const result = await callAI(messages);
if (!result.error) {
return result;
}
// 如果是认证错误,不重试
if (result.error.includes('认证失败')) {
throw new Error(result.error);
}
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
// 指数退避延迟
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
);
}
}
}
```
## 📊 监控和日志
### 1. 客户端监控
```javascript
// 请求统计
const stats = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageResponseTime: 0,
};
async function monitoredAICall(messages) {
const startTime = Date.now();
stats.totalRequests++;
try {
const result = await callAI(messages);
stats.successfulRequests++;
// 更新平均响应时间
const responseTime = Date.now() - startTime;
stats.averageResponseTime =
(stats.averageResponseTime * (stats.successfulRequests - 1) + responseTime)
/ stats.successfulRequests;
return result;
} catch (error) {
stats.failedRequests++;
throw error;
}
}
```
### 2. Worker 日志监控
```bash
# 实时查看 Worker 日志
wrangler tail
# 查看部署状态
wrangler deployments list
# 查看使用统计
wrangler metrics
```
## 🔧 开发环境配置
### 1. 环境分离
```toml
# wrangler.toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-08-17"
# 开发环境
[env.development]
name = "ai-proxy-worker-dev"
vars = { ENVIRONMENT = "development" }
# 生产环境
[env.production]
name = "ai-proxy-worker-prod"
vars = { ENVIRONMENT = "production" }
```
**部署到不同环境:**
```bash
# 开发环境
wrangler publish --env development
# 生产环境
wrangler publish --env production
```
### 2. 本地开发
```bash
# 本地开发服务器
wrangler dev
# 指定端口
wrangler dev --port 8080
# 本地测试
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
## 📱 移动应用集成
### iOS (Swift)
```swift
class AIProxyService {
private let baseURL = "https://your-worker.workers.dev"
private let proxyKey = "YOUR_PROXY_KEY"
func chat(messages: [[String: String]]) async throws -> ChatResponse {
let url = URL(string: "\(baseURL)/chat")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(proxyKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = [
"model": "deepseek-chat",
"messages": messages
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw AIError.requestFailed
}
return try JSONDecoder().decode(ChatResponse.self, from: data)
}
}
```
### Android (Kotlin)
```kotlin
class AIProxyService {
private val baseUrl = "https://your-worker.workers.dev"
private val proxyKey = "YOUR_PROXY_KEY"
private val client = OkHttpClient()
suspend fun chat(messages: List<Message>): ChatResponse {
val requestBody = JSONObject().apply {
put("model", "deepseek-chat")
put("messages", JSONArray(messages.map { it.toJson() }))
}
val request = Request.Builder()
.url("$baseUrl/chat")
.post(requestBody.toString().toRequestBody("application/json".toMediaType()))
.addHeader("Authorization", "Bearer $proxyKey")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException("请求失败: ${response.code}")
}
val responseBody = response.body?.string() ?: throw IOException("空响应")
Gson().fromJson(responseBody, ChatResponse::class.java)
}
}
}
```
## 🚀 生产部署建议
### 1. 部署前检查清单
- [ ] 已设置强密钥PROXY_KEY 和 DEEPSEEK_API_KEY
- [ ] 已限制 CORS 域名(生产环境)
- [ ] 已配置适当的超时和大小限制
- [ ] 已测试所有API端点
- [ ] 已设置监控和日志
- [ ] 已准备错误处理和重试机制
### 2. 性能基准测试
```bash
# 使用 Apache Bench 进行压力测试
ab -n 100 -c 10 -H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-p test-payload.json \
https://your-worker.workers.dev/chat
# test-payload.json 内容:
echo '{"model":"deepseek-chat","messages":[{"role":"user","content":"Hello"}]}' > test-payload.json
```
### 3. 容量规划
根据 Cloudflare Workers 限制:
- **免费版**:每天 100,000 次请求
- **付费版**:无限制,按使用量计费
- **内存**:最大 128MB
- **CPU 时间**:最大 30 秒
## 💡 使用技巧
### 1. 模型选择指南
```javascript
// 选择合适的模型
function selectModel(taskType) {
switch (taskType) {
case 'chat':
case 'creative':
case 'translation':
return 'deepseek-chat'; // 日常对话、创作、翻译
case 'math':
case 'logic':
case 'analysis':
return 'deepseek-reasoner'; // 数学、逻辑推理、分析
default:
return 'deepseek-chat'; // 默认使用 chat 模型
}
}
```
### 2. 消息优化
```javascript
// 优化对话上下文
function optimizeMessages(messages, maxTokens = 4000) {
// 保留系统消息和最近的对话
const systemMessages = messages.filter(m => m.role === 'system');
const recentMessages = messages.filter(m => m.role !== 'system').slice(-10);
return [...systemMessages, ...recentMessages];
}
```
### 3. 错误恢复策略
```javascript
// 智能错误恢复
async function resilientAICall(messages) {
try {
return await callAI(messages);
} catch (error) {
if (error.message.includes('too_long')) {
// 如果消息太长,尝试缩短
const shorterMessages = optimizeMessages(messages, 2000);
return await callAI(shorterMessages);
}
throw error;
}
}
```
---
**下一步?** 👉 [查看使用示例](./Examples.md) | [监控指南](./Monitoring.md)

588
docs/Code-Style.en.md Normal file
View File

@@ -0,0 +1,588 @@
# Code Style Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Code-Style.en.md) | [🇨🇳 中文](./Code-Style.md)
</div>
This guide outlines the coding standards and best practices for the AI Proxy Worker project. Following these guidelines ensures code consistency, maintainability, and readability across the project.
## 📋 General Principles
### Code Quality Standards
- **Readability First**: Write code that tells a story
- **Consistency**: Follow established patterns throughout the codebase
- **Simplicity**: Prefer simple solutions over complex ones
- **Modular Design**: Break down complex functions into single-responsibility small functions
- **Low Cognitive Complexity**: Keep function cognitive complexity below 15
- **Performance**: Consider performance implications of your code
- **Security**: Always prioritize security in your implementations
### File Organization
```
ai-proxy-worker/
├── worker.js # Main worker script
├── wrangler.toml # Configuration file
├── docs/ # Documentation
├── examples/ # Usage examples
└── tests/ # Test files (future)
```
## 🔧 JavaScript/TypeScript Standards
### Code Formatting
- **Indentation**: Use 2 spaces (no tabs)
- **Line Length**: Maximum 100 characters per line
- **Semicolons**: Optional, but be consistent
- **Quotes**: Use single quotes for strings
- **Trailing Commas**: Use trailing commas in multiline objects/arrays
```javascript
// ✅ Good
const config = {
apiUrl: 'https://api.deepseek.com',
timeout: 30000,
retries: 3,
}
const models = [
'deepseek-chat',
'deepseek-reasoner',
]
// ❌ Avoid
const config = {
"apiUrl": "https://api.deepseek.com",
"timeout": 30000,
"retries": 3
};
```
### Naming Conventions
#### Variables and Functions
Use camelCase for variables and functions:
```javascript
// ✅ Good
const apiResponse = await fetchData()
const userMessage = request.body.message
const isValidRequest = validateInput(data)
function processUserRequest(request) {
// Implementation
}
async function sendUpstreamRequest(payload) {
// Implementation
}
// ❌ Avoid
const api_response = await fetchData()
const user_message = request.body.message
const IsValidRequest = validateInput(data)
function ProcessUserRequest(request) {
// Implementation
}
```
#### Constants
Use UPPER_SNAKE_CASE for constants:
```javascript
// ✅ Good
const API_BASE_URL = 'https://api.deepseek.com'
const DEFAULT_TIMEOUT = 30000
const MAX_RETRIES = 3
const SUPPORTED_MODELS = ['deepseek-chat', 'deepseek-reasoner']
// ❌ Avoid
const apiBaseUrl = 'https://api.deepseek.com'
const defaultTimeout = 30000
```
#### Classes and Objects
Use PascalCase for classes and constructor functions:
```javascript
// ✅ Good
class RequestHandler {
constructor(config) {
this.config = config
}
}
class ApiError extends Error {
constructor(message, statusCode) {
super(message)
this.statusCode = statusCode
}
}
// ❌ Avoid
class requestHandler {
// Implementation
}
```
### Function Structure
#### Function Declaration
Prefer arrow functions for short functions, regular functions for complex logic:
```javascript
// ✅ Good - Short utility functions
const isValidModel = (model) => SUPPORTED_MODELS.includes(model)
const createErrorResponse = (message, status = 400) =>
new Response(JSON.stringify({ error: message }), { status })
// ✅ Good - Complex functions
async function handleChatRequest(request, env) {
try {
// Validate request
const validation = await validateRequest(request)
if (!validation.valid) {
return createErrorResponse(validation.error, 400)
}
// Process request
const response = await processChat(request, env)
return response
} catch (error) {
console.error('Chat request failed:', error)
return createErrorResponse('Internal server error', 500)
}
}
```
#### Parameter Handling
Use destructuring for object parameters:
```javascript
// ✅ Good
async function processChat({ messages, model, temperature }, env) {
// Implementation
}
// Alternative with validation
async function processChat(params, env) {
const { messages, model, temperature = 0.7 } = params
// Validate required parameters
if (!messages || !model) {
throw new Error('Missing required parameters')
}
// Implementation
}
// ❌ Avoid
async function processChat(params, env) {
const messages = params.messages
const model = params.model
const temperature = params.temperature
// Implementation
}
```
## 📝 Documentation Standards
### JSDoc Comments
Use JSDoc for function documentation:
```javascript
/**
* Sends a chat request to the upstream API
* @param {Object} request - The chat request object
* @param {Array} request.messages - Array of chat messages
* @param {string} request.model - The model to use
* @param {number} [request.temperature=0.7] - Sampling temperature
* @param {Object} env - Environment variables
* @param {string} env.DEEPSEEK_API_KEY - DeepSeek API key
* @returns {Promise<Response>} The API response
* @throws {Error} When API key is missing or request fails
*/
async function sendChatRequest(request, env) {
// Implementation
}
```
### Inline Comments
Use comments to explain complex logic:
```javascript
// ✅ Good - Explains the "why"
async function handleRequest(request, env) {
// Extract client IP for rate limiting
const clientIP = request.headers.get('CF-Connecting-IP') ||
request.headers.get('X-Forwarded-For') ||
'unknown'
// Check rate limit before processing expensive operations
if (!await checkRateLimit(clientIP)) {
return new Response('Rate limit exceeded', { status: 429 })
}
// Process the actual request
return await processRequest(request, env)
}
// ❌ Avoid - States the obvious
async function handleRequest(request, env) {
// Get the client IP
const clientIP = request.headers.get('CF-Connecting-IP')
// Return rate limit response
if (!await checkRateLimit(clientIP)) {
return new Response('Rate limit exceeded', { status: 429 })
}
}
```
## 🔧 Modular Validation Architecture
### Validation Function Design Principles
This project adopts a modular validation architecture, breaking down complex validation logic into multiple single-responsibility functions:
```javascript
// ✅ Good Example - Modular Validation
async function validateRequest(request) {
validateContentType(request); // Validate Content-Type
validateContentLength(request); // Validate request size
if (CONFIG.VALIDATE_REQUEST_BODY) {
await validateRequestBody(request); // Validate request body
}
}
function validateContentType(request) {
const contentType = request.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
throw new Error('Invalid content type. Expected application/json');
}
}
async function validateRequestBody(request) {
try {
const body = await request.clone().json();
validateMessages(body.messages); // Validate message array
validateModel(body.model); // Validate model
} catch (e) {
// Error handling logic
}
}
```
### Function Complexity Control
- **Cognitive Complexity Limit**: Keep each function's cognitive complexity ≤ 15
- **Single Responsibility Principle**: Each validation function handles only one type of validation
- **Composability**: Validation functions can be independently tested and reused
```javascript
// ✅ Good Example - Single Responsibility
function validateSingleMessage(message) {
if (!message.role || !message.content) {
throw new Error('Invalid request format. Each message must have role and content');
}
if (!['system', 'user', 'assistant', 'tool'].includes(message.role)) {
throw new Error('Invalid request format. Invalid message role');
}
}
// ❌ Avoid This - Complex Monolithic Function
function validateEverything(request) {
// 100+ lines of validation code, cognitive complexity > 20
}
```
## 🛡️ Error Handling
### Error Response Format
Use consistent error response format:
```javascript
// ✅ Good - Consistent error format
function createErrorResponse(error, statusCode = 500, details = null) {
const errorResponse = {
error: error.code || 'unknown_error',
message: error.message || 'An unexpected error occurred',
timestamp: new Date().toISOString(),
}
// Add details in development/debug mode
if (details && env.DEBUG_MODE === 'true') {
errorResponse.details = details
}
return new Response(JSON.stringify(errorResponse), {
status: statusCode,
headers: { 'Content-Type': 'application/json' }
})
}
// Usage
try {
const result = await riskyOperation()
return new Response(JSON.stringify(result))
} catch (error) {
console.error('Operation failed:', error)
return createErrorResponse(error, 500, { operation: 'riskyOperation' })
}
```
### Error Types
Define custom error types for different scenarios:
```javascript
// ✅ Good - Custom error classes
class ValidationError extends Error {
constructor(message, field = null) {
super(message)
this.name = 'ValidationError'
this.code = 'validation_error'
this.field = field
}
}
class ApiError extends Error {
constructor(message, statusCode = 500, originalError = null) {
super(message)
this.name = 'ApiError'
this.code = 'api_error'
this.statusCode = statusCode
this.originalError = originalError
}
}
// Usage
function validateChatRequest(data) {
if (!data.messages || !Array.isArray(data.messages)) {
throw new ValidationError('Messages must be an array', 'messages')
}
if (!data.model || typeof data.model !== 'string') {
throw new ValidationError('Model must be a string', 'model')
}
}
```
## 🔒 Security Best Practices
### Input Validation
Always validate and sanitize inputs:
```javascript
// ✅ Good - Comprehensive validation
function validateChatRequest(data) {
const errors = []
// Required fields
if (!data.messages || !Array.isArray(data.messages)) {
errors.push('messages must be an array')
}
if (!data.model || typeof data.model !== 'string') {
errors.push('model must be a string')
}
// Validate messages array
if (data.messages) {
data.messages.forEach((msg, index) => {
if (!msg.role || !['user', 'assistant', 'system'].includes(msg.role)) {
errors.push(`messages[${index}].role must be user, assistant, or system`)
}
if (!msg.content || typeof msg.content !== 'string') {
errors.push(`messages[${index}].content must be a non-empty string`)
}
// Content length limits
if (msg.content && msg.content.length > 100000) {
errors.push(`messages[${index}].content exceeds maximum length`)
}
})
}
// Optional parameter validation
if (data.temperature !== undefined) {
if (typeof data.temperature !== 'number' ||
data.temperature < 0 ||
data.temperature > 2) {
errors.push('temperature must be a number between 0 and 2')
}
}
return {
valid: errors.length === 0,
errors
}
}
```
### Sensitive Data Handling
Never log sensitive information:
```javascript
// ✅ Good - Sanitized logging
function logRequest(request, response) {
const logData = {
method: request.method,
url: new URL(request.url).pathname, // Don't log query params
status: response.status,
timestamp: new Date().toISOString(),
// Don't log authorization headers or body content
}
console.log('Request processed:', logData)
}
// ❌ Avoid - Logging sensitive data
function logRequest(request, response) {
console.log('Request:', {
headers: Object.fromEntries(request.headers), // Contains API keys!
body: request.body, // Contains user data!
url: request.url // May contain sensitive query params!
})
}
```
## ⚡ Performance Guidelines
### Async/Await Best Practices
Use async/await properly:
```javascript
// ✅ Good - Parallel execution when possible
async function processMultipleRequests(requests, env) {
// Execute requests in parallel
const promises = requests.map(request => processRequest(request, env))
const results = await Promise.allSettled(promises)
return results.map(result =>
result.status === 'fulfilled' ? result.value : null
).filter(Boolean)
}
// ✅ Good - Sequential when needed
async function processWithDependencies(request, env) {
const validation = await validateRequest(request)
if (!validation.valid) {
throw new ValidationError(validation.errors.join(', '))
}
const processed = await processRequest(request, env)
const logged = await logRequest(processed)
return processed
}
// ❌ Avoid - Unnecessary sequential execution
async function processMultipleRequests(requests, env) {
const results = []
for (const request of requests) {
const result = await processRequest(request, env) // Blocking!
results.push(result)
}
return results
}
```
### Memory Management
Be mindful of memory usage:
```javascript
// ✅ Good - Clean up resources
async function processLargeRequest(request, env) {
let reader = null
try {
reader = request.body.getReader()
const chunks = []
while (true) {
const { done, value } = await reader.read()
if (done) break
chunks.push(value)
}
return await processChunks(chunks)
} finally {
// Clean up resources
if (reader) {
reader.releaseLock()
}
}
}
```
## 🧪 Testing Guidelines
### Test Structure
When writing tests (future implementation):
```javascript
// ✅ Good - Clear test structure
describe('Chat Request Handler', () => {
describe('validateChatRequest', () => {
it('should accept valid chat request', () => {
const validRequest = {
messages: [{ role: 'user', content: 'Hello' }],
model: 'deepseek-chat'
}
const result = validateChatRequest(validRequest)
expect(result.valid).toBe(true)
})
it('should reject request without messages', () => {
const invalidRequest = { model: 'deepseek-chat' }
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages must be an array')
})
})
})
```
## 📋 Code Review Checklist
Before submitting code, ensure:
### Functionality
- [ ] Code works as expected
- [ ] Edge cases are handled
- [ ] Error conditions are properly managed
- [ ] Performance implications are considered
### Code Quality
- [ ] Follows project naming conventions
- [ ] Functions are reasonably sized (< 50 lines)
- [ ] Code is self-documenting
- [ ] Complex logic is commented
- [ ] No debugging code left behind
### Security
- [ ] Input validation is implemented
- [ ] No sensitive data in logs
- [ ] Proper error handling without information leakage
- [ ] Security headers are set appropriately
### Documentation
- [ ] JSDoc comments for public functions
- [ ] README updated if needed
- [ ] Examples provided for new features
- [ ] Breaking changes documented
---
**Consistent code style makes collaboration easier**
Following these guidelines helps maintain a high-quality, maintainable codebase that's easy for all contributors to work with.

588
docs/Code-Style.md Normal file
View File

@@ -0,0 +1,588 @@
# 代码风格指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Code-Style.en.md) | [🇨🇳 中文](./Code-Style.md)
</div>
本指南概述了 AI Proxy Worker 项目的编码标准和最佳实践。遵循这些准则确保整个项目的代码一致性、可维护性和可读性。
## 📋 通用原则
### 代码质量标准
- **可读性优先**:编写能讲述故事的代码
- **一致性**:在整个代码库中遵循既定模式
- **简洁性**:优先选择简单的解决方案而非复杂的
- **模块化设计**:拆分复杂函数为单一职责的小函数
- **低认知复杂度**保持函数的认知复杂度在15以下
- **性能**:考虑代码的性能影响
- **安全性**:在实现中始终优先考虑安全性
### 文件组织
```
ai-proxy-worker/
├── worker.js # 主要 worker 脚本
├── wrangler.toml # 配置文件
├── docs/ # 文档
├── examples/ # 使用示例
└── tests/ # 测试文件(未来)
```
## 🔧 JavaScript/TypeScript 标准
### 代码格式化
- **缩进**:使用 2 个空格(不使用制表符)
- **行长度**:每行最多 100 个字符
- **分号**:可选,但要保持一致
- **引号**:字符串使用单引号
- **尾随逗号**:在多行对象/数组中使用尾随逗号
```javascript
// ✅ 好的示例
const config = {
apiUrl: 'https://api.deepseek.com',
timeout: 30000,
retries: 3,
}
const models = [
'deepseek-chat',
'deepseek-reasoner',
]
// ❌ 避免这样写
const config = {
"apiUrl": "https://api.deepseek.com",
"timeout": 30000,
"retries": 3
};
```
### 命名约定
#### 变量和函数
变量和函数使用 camelCase
```javascript
// ✅ 好的示例
const apiResponse = await fetchData()
const userMessage = request.body.message
const isValidRequest = validateInput(data)
function processUserRequest(request) {
// 实现
}
async function sendUpstreamRequest(payload) {
// 实现
}
// ❌ 避免这样写
const api_response = await fetchData()
const user_message = request.body.message
const IsValidRequest = validateInput(data)
function ProcessUserRequest(request) {
// 实现
}
```
#### 常量
常量使用 UPPER_SNAKE_CASE
```javascript
// ✅ 好的示例
const API_BASE_URL = 'https://api.deepseek.com'
const DEFAULT_TIMEOUT = 30000
const MAX_RETRIES = 3
const SUPPORTED_MODELS = ['deepseek-chat', 'deepseek-reasoner']
// ❌ 避免这样写
const apiBaseUrl = 'https://api.deepseek.com'
const defaultTimeout = 30000
```
#### 类和对象
类和构造函数使用 PascalCase
```javascript
// ✅ 好的示例
class RequestHandler {
constructor(config) {
this.config = config
}
}
class ApiError extends Error {
constructor(message, statusCode) {
super(message)
this.statusCode = statusCode
}
}
// ❌ 避免这样写
class requestHandler {
// 实现
}
```
### 函数结构
#### 函数声明
短函数优先使用箭头函数,复杂逻辑使用常规函数:
```javascript
// ✅ 好的示例 - 短工具函数
const isValidModel = (model) => SUPPORTED_MODELS.includes(model)
const createErrorResponse = (message, status = 400) =>
new Response(JSON.stringify({ error: message }), { status })
// ✅ 好的示例 - 复杂函数
async function handleChatRequest(request, env) {
try {
// 验证请求
const validation = await validateRequest(request)
if (!validation.valid) {
return createErrorResponse(validation.error, 400)
}
// 处理请求
const response = await processChat(request, env)
return response
} catch (error) {
console.error('聊天请求失败:', error)
return createErrorResponse('内部服务器错误', 500)
}
}
```
#### 参数处理
对象参数使用解构:
```javascript
// ✅ 好的示例
async function processChat({ messages, model, temperature }, env) {
// 实现
}
// 带验证的替代方案
async function processChat(params, env) {
const { messages, model, temperature = 0.7 } = params
// 验证必需参数
if (!messages || !model) {
throw new Error('缺少必需参数')
}
// 实现
}
// ❌ 避免这样写
async function processChat(params, env) {
const messages = params.messages
const model = params.model
const temperature = params.temperature
// 实现
}
```
## 📝 文档标准
### JSDoc 注释
函数文档使用 JSDoc
```javascript
/**
* 向上游 API 发送聊天请求
* @param {Object} request - 聊天请求对象
* @param {Array} request.messages - 聊天消息数组
* @param {string} request.model - 要使用的模型
* @param {number} [request.temperature=0.7] - 采样温度
* @param {Object} env - 环境变量
* @param {string} env.DEEPSEEK_API_KEY - DeepSeek API 密钥
* @returns {Promise<Response>} API 响应
* @throws {Error} 当 API 密钥缺失或请求失败时
*/
async function sendChatRequest(request, env) {
// 实现
}
```
### 行内注释
使用注释解释复杂逻辑:
```javascript
// ✅ 好的示例 - 解释"为什么"
async function handleRequest(request, env) {
// 提取客户端 IP 用于速率限制
const clientIP = request.headers.get('CF-Connecting-IP') ||
request.headers.get('X-Forwarded-For') ||
'unknown'
// 在处理昂贵操作之前检查速率限制
if (!await checkRateLimit(clientIP)) {
return new Response('超出速率限制', { status: 429 })
}
// 处理实际请求
return await processRequest(request, env)
}
// ❌ 避免这样写 - 陈述显而易见的事实
async function handleRequest(request, env) {
// 获取客户端 IP
const clientIP = request.headers.get('CF-Connecting-IP')
// 返回速率限制响应
if (!await checkRateLimit(clientIP)) {
return new Response('超出速率限制', { status: 429 })
}
}
```
## 🔧 模块化验证架构
### 验证函数设计原则
本项目采用模块化验证架构,将复杂的验证逻辑拆分为多个单一职责的函数:
```javascript
// ✅ 好的示例 - 模块化验证
async function validateRequest(request) {
validateContentType(request); // 验证Content-Type
validateContentLength(request); // 验证请求大小
if (CONFIG.VALIDATE_REQUEST_BODY) {
await validateRequestBody(request); // 验证请求体
}
}
function validateContentType(request) {
const contentType = request.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
throw new Error('Invalid content type. Expected application/json');
}
}
async function validateRequestBody(request) {
try {
const body = await request.clone().json();
validateMessages(body.messages); // 验证消息数组
validateModel(body.model); // 验证模型
} catch (e) {
// 错误处理逻辑
}
}
```
### 函数复杂度控制
- **认知复杂度限制**: 每个函数保持认知复杂度 ≤ 15
- **单一职责原则**: 每个验证函数只负责一种验证
- **可组合性**: 验证函数可以独立测试和复用
```javascript
// ✅ 好的示例 - 单一职责
function validateSingleMessage(message) {
if (!message.role || !message.content) {
throw new Error('Invalid request format. Each message must have role and content');
}
if (!['system', 'user', 'assistant', 'tool'].includes(message.role)) {
throw new Error('Invalid request format. Invalid message role');
}
}
// ❌ 避免这样写 - 复杂的巨大函数
function validateEverything(request) {
// 100+ 行验证代码,认知复杂度 > 20
}
```
## 🛡️ 错误处理
### 错误响应格式
使用一致的错误响应格式:
```javascript
// ✅ 好的示例 - 一致的错误格式
function createErrorResponse(error, statusCode = 500, details = null) {
const errorResponse = {
error: error.code || 'unknown_error',
message: error.message || '发生意外错误',
timestamp: new Date().toISOString(),
}
// 在开发/调试模式下添加详细信息
if (details && env.DEBUG_MODE === 'true') {
errorResponse.details = details
}
return new Response(JSON.stringify(errorResponse), {
status: statusCode,
headers: { 'Content-Type': 'application/json' }
})
}
// 使用方法
try {
const result = await riskyOperation()
return new Response(JSON.stringify(result))
} catch (error) {
console.error('操作失败:', error)
return createErrorResponse(error, 500, { operation: 'riskyOperation' })
}
```
### 错误类型
为不同场景定义自定义错误类型:
```javascript
// ✅ 好的示例 - 自定义错误类
class ValidationError extends Error {
constructor(message, field = null) {
super(message)
this.name = 'ValidationError'
this.code = 'validation_error'
this.field = field
}
}
class ApiError extends Error {
constructor(message, statusCode = 500, originalError = null) {
super(message)
this.name = 'ApiError'
this.code = 'api_error'
this.statusCode = statusCode
this.originalError = originalError
}
}
// 使用方法
function validateChatRequest(data) {
if (!data.messages || !Array.isArray(data.messages)) {
throw new ValidationError('消息必须是数组', 'messages')
}
if (!data.model || typeof data.model !== 'string') {
throw new ValidationError('模型必须是字符串', 'model')
}
}
```
## 🔒 安全最佳实践
### 输入验证
始终验证和清理输入:
```javascript
// ✅ 好的示例 - 全面验证
function validateChatRequest(data) {
const errors = []
// 必需字段
if (!data.messages || !Array.isArray(data.messages)) {
errors.push('messages 必须是数组')
}
if (!data.model || typeof data.model !== 'string') {
errors.push('model 必须是字符串')
}
// 验证消息数组
if (data.messages) {
data.messages.forEach((msg, index) => {
if (!msg.role || !['user', 'assistant', 'system'].includes(msg.role)) {
errors.push(`messages[${index}].role 必须是 user、assistant 或 system`)
}
if (!msg.content || typeof msg.content !== 'string') {
errors.push(`messages[${index}].content 必须是非空字符串`)
}
// 内容长度限制
if (msg.content && msg.content.length > 100000) {
errors.push(`messages[${index}].content 超过最大长度`)
}
})
}
// 可选参数验证
if (data.temperature !== undefined) {
if (typeof data.temperature !== 'number' ||
data.temperature < 0 ||
data.temperature > 2) {
errors.push('temperature 必须是 0 到 2 之间的数字')
}
}
return {
valid: errors.length === 0,
errors
}
}
```
### 敏感数据处理
永远不要记录敏感信息:
```javascript
// ✅ 好的示例 - 清理后的日志
function logRequest(request, response) {
const logData = {
method: request.method,
url: new URL(request.url).pathname, // 不记录查询参数
status: response.status,
timestamp: new Date().toISOString(),
// 不记录授权头或正文内容
}
console.log('请求已处理:', logData)
}
// ❌ 避免这样写 - 记录敏感数据
function logRequest(request, response) {
console.log('请求:', {
headers: Object.fromEntries(request.headers), // 包含 API 密钥!
body: request.body, // 包含用户数据!
url: request.url // 可能包含敏感查询参数!
})
}
```
## ⚡ 性能指南
### Async/Await 最佳实践
正确使用 async/await
```javascript
// ✅ 好的示例 - 尽可能并行执行
async function processMultipleRequests(requests, env) {
// 并行执行请求
const promises = requests.map(request => processRequest(request, env))
const results = await Promise.allSettled(promises)
return results.map(result =>
result.status === 'fulfilled' ? result.value : null
).filter(Boolean)
}
// ✅ 好的示例 - 需要时顺序执行
async function processWithDependencies(request, env) {
const validation = await validateRequest(request)
if (!validation.valid) {
throw new ValidationError(validation.errors.join(', '))
}
const processed = await processRequest(request, env)
const logged = await logRequest(processed)
return processed
}
// ❌ 避免这样写 - 不必要的顺序执行
async function processMultipleRequests(requests, env) {
const results = []
for (const request of requests) {
const result = await processRequest(request, env) // 阻塞!
results.push(result)
}
return results
}
```
### 内存管理
注意内存使用:
```javascript
// ✅ 好的示例 - 清理资源
async function processLargeRequest(request, env) {
let reader = null
try {
reader = request.body.getReader()
const chunks = []
while (true) {
const { done, value } = await reader.read()
if (done) break
chunks.push(value)
}
return await processChunks(chunks)
} finally {
// 清理资源
if (reader) {
reader.releaseLock()
}
}
}
```
## 🧪 测试指南
### 测试结构
编写测试时(未来实现):
```javascript
// ✅ 好的示例 - 清晰的测试结构
describe('聊天请求处理器', () => {
describe('validateChatRequest', () => {
it('应该接受有效的聊天请求', () => {
const validRequest = {
messages: [{ role: 'user', content: '你好' }],
model: 'deepseek-chat'
}
const result = validateChatRequest(validRequest)
expect(result.valid).toBe(true)
})
it('应该拒绝没有消息的请求', () => {
const invalidRequest = { model: 'deepseek-chat' }
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages 必须是数组')
})
})
})
```
## 📋 代码审查清单
提交代码前,确保:
### 功能性
- [ ] 代码按预期工作
- [ ] 处理边界情况
- [ ] 正确管理错误条件
- [ ] 考虑性能影响
### 代码质量
- [ ] 遵循项目命名约定
- [ ] 函数大小合理(< 50
- [ ] 代码自文档化
- [ ] 复杂逻辑有注释
- [ ] 没有遗留调试代码
### 安全性
- [ ] 实现输入验证
- [ ] 日志中无敏感数据
- [ ] 适当的错误处理不泄露信息
- [ ] 适当设置安全头
### 文档
- [ ] 公共函数有 JSDoc 注释
- [ ] 需要时更新 README
- [ ] 为新功能提供示例
- [ ] 记录重大变更
---
**一致的代码风格让协作更容易**
遵循这些指南有助于维护高质量可维护的代码库让所有贡献者都能轻松使用

315
docs/Configuration.en.md Normal file
View File

@@ -0,0 +1,315 @@
# Configuration Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Configuration.en.md) | [🇨🇳 中文](./Configuration.md)
</div>
AI Proxy Worker provides various configuration options to customize the proxy behavior according to your needs. Configuration is divided into two categories: environment variables and code configuration.
## 🔑 Environment Variables
These configurations are set through Cloudflare Workers' Secret feature, ensuring they remain invisible in the code for security.
### Required Environment Variables
| Variable | Type | Description | Example |
|----------|------|-------------|---------|
| `DEEPSEEK_API_KEY` | Secret | DeepSeek API key | `sk-...` |
**Setup Method:**
```bash
wrangler secret put DEEPSEEK_API_KEY
# Enter your DeepSeek API key
```
### Optional Environment Variables
| Variable | Type | Description | Default |
|----------|------|-------------|---------|
| `PROXY_KEY` | Secret | Proxy access key for protecting your proxy | None (no authentication) |
**Setup Method:**
```bash
wrangler secret put PROXY_KEY
# Enter a custom access key, e.g., my-secret-key-2025
```
**Recommendations:**
- Strongly recommended to set `PROXY_KEY` in production
- Use a strong password generator for access keys
- Rotate keys periodically for enhanced security
## ⚙️ Code Configuration
These configurations are defined in the `CONFIG` object in `worker.js` and can be modified as needed.
### Core Configuration Options
#### DEEPSEEK_API_URL
- **Type**: String
- **Default**: `'https://api.deepseek.com/chat/completions'`
- **Description**: DeepSeek API endpoint URL
- **When to modify**: Usually no need to change unless DeepSeek changes their API endpoint
```javascript
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions'
```
#### MAX_BODY_SIZE
- **Type**: Number
- **Default**: `1024 * 1024` (1MB)
- **Description**: Maximum request body size limit (bytes)
- **When to modify**:
- Increase: For longer conversation history
- Decrease: For stricter limits to prevent abuse
```javascript
MAX_BODY_SIZE: 1024 * 1024 // 1MB
MAX_BODY_SIZE: 2 * 1024 * 1024 // 2MB (more lenient)
MAX_BODY_SIZE: 512 * 1024 // 512KB (stricter)
```
#### REQUEST_TIMEOUT
- **Type**: Number
- **Default**: `30000` (30 seconds)
- **Description**: Timeout for requests to DeepSeek API (milliseconds)
- **When to modify**:
- Increase: If frequently encountering timeout errors
- Decrease: For faster failure responses
```javascript
REQUEST_TIMEOUT: 30000 // 30 seconds (default)
REQUEST_TIMEOUT: 60000 // 60 seconds (more lenient)
REQUEST_TIMEOUT: 15000 // 15 seconds (stricter)
```
#### VALIDATE_REQUEST_BODY
- **Type**: Boolean
- **Default**: `false`
- **Description**: Whether to enable strict request body format validation
- **When to modify**:
- `true`: Enable strict validation, checks messages format
- `false`: Lenient mode, let DeepSeek API handle validation
```javascript
VALIDATE_REQUEST_BODY: false // Lenient mode (recommended)
VALIDATE_REQUEST_BODY: true // Strict mode
```
#### DEFAULT_MODEL
- **Type**: String
- **Default**: `'deepseek-chat'`
- **Description**: Default model to use when not specified in request
- **When to modify**: Based on your primary use case
```javascript
DEFAULT_MODEL: 'deepseek-chat' // General conversation (default)
DEFAULT_MODEL: 'deepseek-reasoner' // Reasoning tasks primary
```
#### SUPPORTED_MODELS
- **Type**: Array
- **Default**: `['deepseek-chat', 'deepseek-reasoner']`
- **Description**: List of supported models for validation and documentation
- **When to modify**: When DeepSeek releases new models
```javascript
SUPPORTED_MODELS: [
'deepseek-chat', // General conversation model
'deepseek-reasoner' // Reasoning model
]
```
### CORS Configuration
#### CORS_HEADERS
- **Description**: Cross-origin request response headers configuration
- **Default**: Allows all domains
```javascript
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
```
**Production Recommendations:**
```javascript
// Restrict to specific domains
'Access-Control-Allow-Origin': 'https://yourdomain.com'
// Or support multiple domains
'Access-Control-Allow-Origin': request.headers.get('origin') // Needs additional validation logic
```
### Security Configuration
#### SECURITY_HEADERS
- **Description**: Security-related response headers
- **Default**: Basic security protection
```javascript
const SECURITY_HEADERS = {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
};
```
## 🛠️ Advanced Configuration
### Custom Configuration Examples
For more complex configurations, you can modify like this:
```javascript
// Development environment configuration
const CONFIG = {
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions',
MAX_BODY_SIZE: 2 * 1024 * 1024, // 2MB, more lenient
REQUEST_TIMEOUT: 60000, // 60 seconds, longer timeout
VALIDATE_REQUEST_BODY: true, // Enable strict validation
DEFAULT_MODEL: 'deepseek-chat',
SUPPORTED_MODELS: [
'deepseek-chat',
'deepseek-reasoner'
]
};
// Production environment configuration
const CONFIG = {
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions',
MAX_BODY_SIZE: 512 * 1024, // 512KB, stricter
REQUEST_TIMEOUT: 15000, // 15 seconds, faster failure
VALIDATE_REQUEST_BODY: false, // Lenient validation, better compatibility
DEFAULT_MODEL: 'deepseek-chat',
SUPPORTED_MODELS: [
'deepseek-chat',
'deepseek-reasoner'
]
};
```
### Environment-Specific Configuration
You can also set environment-specific configurations in `wrangler.toml`:
```toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-08-17"
# Production environment
[env.production]
name = "ai-proxy-worker-prod"
vars = { ENVIRONMENT = "production" }
# Development environment
[env.development]
name = "ai-proxy-worker-dev"
vars = { ENVIRONMENT = "development" }
```
Then use in code:
```javascript
// Adjust configuration based on environment
const isProduction = env.ENVIRONMENT === 'production';
const CONFIG = {
// ... other configurations
MAX_BODY_SIZE: isProduction ? 512 * 1024 : 2 * 1024 * 1024,
REQUEST_TIMEOUT: isProduction ? 15000 : 60000,
VALIDATE_REQUEST_BODY: !isProduction, // Enable strict validation in development
};
```
## 📝 Configuration Best Practices
### 1. Security Configuration
```javascript
// ✅ Recommended: Restrict CORS in production
'Access-Control-Allow-Origin': 'https://yourdomain.com'
// ❌ Avoid: Using wildcards in production
'Access-Control-Allow-Origin': '*'
```
### 2. Performance Configuration
```javascript
// ✅ Recommended: Reasonable timeout
REQUEST_TIMEOUT: 30000 // 30 seconds
// ❌ Avoid: Excessive timeout
REQUEST_TIMEOUT: 300000 // 5 minutes (too long)
```
### 3. Compatibility Configuration
```javascript
// ✅ Recommended: Lenient validation mode
VALIDATE_REQUEST_BODY: false
// ⚠️ Note: Strict mode may cause some clients to fail
VALIDATE_REQUEST_BODY: true
```
## 🔧 Configuration Modification Steps
### 1. Modify Code Configuration
1. Edit `worker.js` file
2. Modify corresponding values in `CONFIG` object
3. Save file
### 2. Redeploy
```bash
wrangler publish
```
### 3. Verify Configuration
```bash
# Test health check
curl https://your-worker.workers.dev/
# Test API call
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
## ❓ Common Configuration Questions
### Q: How to increase request body size limit?
A: Modify `MAX_BODY_SIZE` configuration:
```javascript
MAX_BODY_SIZE: 2 * 1024 * 1024 // Increase to 2MB
```
### Q: How to set stricter timeout?
A: Modify `REQUEST_TIMEOUT` configuration:
```javascript
REQUEST_TIMEOUT: 15000 // Reduce to 15 seconds
```
### Q: How to disable request validation?
A: Set `VALIDATE_REQUEST_BODY` to false:
```javascript
VALIDATE_REQUEST_BODY: false
```
### Q: How to restrict CORS domains?
A: Modify `Access-Control-Allow-Origin` in `CORS_HEADERS`:
```javascript
'Access-Control-Allow-Origin': 'https://yourdomain.com'
```
---
**Need Help?** 👉 [Troubleshooting Guide](./Troubleshooting) | [GitHub Issues](../../issues)

315
docs/Configuration.md Normal file
View File

@@ -0,0 +1,315 @@
# 配置指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Configuration.en.md) | [🇨🇳 中文](./Configuration.md)
</div>
AI Proxy Worker 提供了多种配置选项,让你可以根据需求定制代理的行为。配置分为两类:环境变量配置和代码配置。
## 🔑 环境变量配置
这些配置通过 Cloudflare Workers 的 Secret 功能设置,在代码中无法看到具体值,确保安全性。
### 必需的环境变量
| 变量名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| `DEEPSEEK_API_KEY` | Secret | DeepSeek API 密钥 | `sk-...` |
**设置方法:**
```bash
wrangler secret put DEEPSEEK_API_KEY
# 输入你的 DeepSeek API 密钥
```
### 可选的环境变量
| 变量名 | 类型 | 说明 | 默认值 |
|--------|------|------|--------|
| `PROXY_KEY` | Secret | 代理访问密钥,用于保护你的代理 | 无(不启用认证) |
**设置方法:**
```bash
wrangler secret put PROXY_KEY
# 输入自定义的访问密钥my-secret-key-2025
```
**使用建议:**
- 生产环境强烈建议设置 `PROXY_KEY`
- 使用强密码生成器生成访问密钥
- 定期轮换密钥以提高安全性
## ⚙️ 代码配置
这些配置在 `worker.js` 文件的 `CONFIG` 对象中定义,可以根据需要修改。
### 核心配置项
#### DEEPSEEK_API_URL
- **类型**String
- **默认值**`'https://api.deepseek.com/chat/completions'`
- **说明**DeepSeek API 的端点地址
- **修改场景**:通常不需要修改,除非 DeepSeek 更改了 API 端点
```javascript
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions'
```
#### MAX_BODY_SIZE
- **类型**Number
- **默认值**`1024 * 1024` (1MB)
- **说明**:请求体的最大大小限制(字节)
- **修改场景**
- 增大:如果需要处理更长的对话历史
- 减小:如果想要更严格的限制以防止滥用
```javascript
MAX_BODY_SIZE: 1024 * 1024 // 1MB
MAX_BODY_SIZE: 2 * 1024 * 1024 // 2MB更宽松
MAX_BODY_SIZE: 512 * 1024 // 512KB更严格
```
#### REQUEST_TIMEOUT
- **类型**Number
- **默认值**`30000` (30秒)
- **说明**:向 DeepSeek API 请求的超时时间(毫秒)
- **修改场景**
- 增大:如果经常遇到超时错误
- 减小:如果想要更快的失败响应
```javascript
REQUEST_TIMEOUT: 30000 // 30秒默认
REQUEST_TIMEOUT: 60000 // 60秒更宽松
REQUEST_TIMEOUT: 15000 // 15秒更严格
```
#### VALIDATE_REQUEST_BODY
- **类型**Boolean
- **默认值**`false`
- **说明**:是否启用严格的请求体格式验证
- **修改场景**
- `true`:启用严格验证,会检查 messages 格式
- `false`:宽松模式,让 DeepSeek API 自己处理验证
```javascript
VALIDATE_REQUEST_BODY: false // 宽松模式(推荐)
VALIDATE_REQUEST_BODY: true // 严格模式
```
#### DEFAULT_MODEL
- **类型**String
- **默认值**`'deepseek-chat'`
- **说明**:当请求中没有指定模型时使用的默认模型
- **修改场景**:根据你的主要使用场景选择
```javascript
DEFAULT_MODEL: 'deepseek-chat' // 通用对话(默认)
DEFAULT_MODEL: 'deepseek-reasoner' // 推理任务为主
```
#### SUPPORTED_MODELS
- **类型**Array
- **默认值**`['deepseek-chat', 'deepseek-reasoner']`
- **说明**:支持的模型列表,用于验证和文档
- **修改场景**:当 DeepSeek 发布新模型时更新
```javascript
SUPPORTED_MODELS: [
'deepseek-chat', // 通用对话模型
'deepseek-reasoner' // 推理模型
]
```
### CORS 配置
#### CORS_HEADERS
- **说明**:跨域请求的响应头配置
- **默认配置**:允许所有域名访问
```javascript
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
```
**生产环境建议:**
```javascript
// 限制特定域名
'Access-Control-Allow-Origin': 'https://yourdomain.com'
// 或者支持多个域名
'Access-Control-Allow-Origin': request.headers.get('origin') // 需要额外的验证逻辑
```
### 安全配置
#### SECURITY_HEADERS
- **说明**:安全相关的响应头
- **默认配置**:基础安全防护
```javascript
const SECURITY_HEADERS = {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
};
```
## 🛠️ 高级配置
### 自定义配置示例
如果你需要更复杂的配置,可以这样修改:
```javascript
// 开发环境配置
const CONFIG = {
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions',
MAX_BODY_SIZE: 2 * 1024 * 1024, // 2MB更宽松
REQUEST_TIMEOUT: 60000, // 60秒更长的超时
VALIDATE_REQUEST_BODY: true, // 启用严格验证
DEFAULT_MODEL: 'deepseek-chat',
SUPPORTED_MODELS: [
'deepseek-chat',
'deepseek-reasoner'
]
};
// 生产环境配置
const CONFIG = {
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions',
MAX_BODY_SIZE: 512 * 1024, // 512KB更严格
REQUEST_TIMEOUT: 15000, // 15秒更快失败
VALIDATE_REQUEST_BODY: false, // 宽松验证,更好的兼容性
DEFAULT_MODEL: 'deepseek-chat',
SUPPORTED_MODELS: [
'deepseek-chat',
'deepseek-reasoner'
]
};
```
### 环境特定配置
你也可以在 `wrangler.toml` 中设置环境特定的配置:
```toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-08-17"
# 生产环境
[env.production]
name = "ai-proxy-worker-prod"
vars = { ENVIRONMENT = "production" }
# 开发环境
[env.development]
name = "ai-proxy-worker-dev"
vars = { ENVIRONMENT = "development" }
```
然后在代码中使用:
```javascript
// 根据环境调整配置
const isProduction = env.ENVIRONMENT === 'production';
const CONFIG = {
// ... 其他配置
MAX_BODY_SIZE: isProduction ? 512 * 1024 : 2 * 1024 * 1024,
REQUEST_TIMEOUT: isProduction ? 15000 : 60000,
VALIDATE_REQUEST_BODY: !isProduction, // 开发环境启用严格验证
};
```
## 📝 配置最佳实践
### 1. 安全配置
```javascript
// ✅ 推荐:生产环境限制 CORS
'Access-Control-Allow-Origin': 'https://yourdomain.com'
// ❌ 避免:生产环境使用通配符
'Access-Control-Allow-Origin': '*'
```
### 2. 性能配置
```javascript
// ✅ 推荐:合理的超时时间
REQUEST_TIMEOUT: 30000 // 30秒
// ❌ 避免:过长的超时时间
REQUEST_TIMEOUT: 300000 // 5分钟太长
```
### 3. 兼容性配置
```javascript
// ✅ 推荐:宽松的验证模式
VALIDATE_REQUEST_BODY: false
// ⚠️ 注意:严格模式可能导致某些客户端失败
VALIDATE_REQUEST_BODY: true
```
## 🔧 配置修改步骤
### 1. 修改代码配置
1. 编辑 `worker.js` 文件
2. 修改 `CONFIG` 对象中的相应值
3. 保存文件
### 2. 重新部署
```bash
wrangler publish
```
### 3. 验证配置
```bash
# 测试健康检查
curl https://your-worker.workers.dev/
# 测试 API 调用
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
## ❓ 常见配置问题
### Q: 如何增加请求体大小限制?
A: 修改 `MAX_BODY_SIZE` 配置:
```javascript
MAX_BODY_SIZE: 2 * 1024 * 1024 // 增加到 2MB
```
### Q: 如何设置更严格的超时?
A: 修改 `REQUEST_TIMEOUT` 配置:
```javascript
REQUEST_TIMEOUT: 15000 // 减少到 15秒
```
### Q: 如何禁用请求验证?
A: 设置 `VALIDATE_REQUEST_BODY` 为 false
```javascript
VALIDATE_REQUEST_BODY: false
```
### Q: 如何限制 CORS 域名?
A: 修改 `CORS_HEADERS` 中的 `Access-Control-Allow-Origin`
```javascript
'Access-Control-Allow-Origin': 'https://yourdomain.com'
```
---
**需要帮助?** 👉 [故障排除指南](./Troubleshooting) | [GitHub Issues](../../issues)

277
docs/Contributing.en.md Normal file
View File

@@ -0,0 +1,277 @@
# Contributing Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Contributing.en.md) | [🇨🇳 中文](./Contributing.md)
</div>
Thank you for your interest in contributing to AI Proxy Worker! This guide will help you understand how to participate in the project development.
## 🚀 Quick Start
### Prerequisites
- Node.js 18+ and npm
- Git
- Cloudflare account (for testing)
- Basic knowledge of JavaScript and Cloudflare Workers
### Development Setup
```bash
# Clone the repository
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
cd ai-proxy-worker
# Install dependencies
npm install -g wrangler
# Login to Cloudflare
wrangler login
# Set up development environment
cp wrangler.toml.example wrangler.toml
# Edit wrangler.toml with your settings
```
## 🔧 Development Workflow
### 1. Fork and Clone
```bash
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
cd ai-proxy-worker
# Add upstream remote
git remote add upstream https://github.com/original-owner/ai-proxy-worker.git
```
### 2. Create Feature Branch
```bash
# Create and switch to a new branch
git checkout -b feature/your-feature-name
# Or for bug fixes
git checkout -b fix/bug-description
```
### 3. Local Development
```bash
# Start local development server
wrangler dev
# Test your changes
curl -X POST http://localhost:8787/chat \
-H "Content-Type: application/json" \
-d '{"model": "deepseek-chat", "messages": [{"role": "user", "content": "test"}]}'
```
### 4. Testing
```bash
# Run tests (when available)
npm test
# Manual testing checklist:
# - Basic chat functionality
# - Streaming responses
# - Error handling
# - Authentication
```
### 5. Commit and Push
```bash
# Add changes
git add .
# Commit with descriptive message
git commit -m "feat: add support for new AI model"
# Push to your fork
git push origin feature/your-feature-name
```
### 6. Create Pull Request
1. Go to your fork on GitHub
2. Click "New Pull Request"
3. Fill out the PR template
4. Wait for review
## 📝 Code Standards
### JavaScript Style
- Use modern ES6+ features
- Follow consistent indentation (2 spaces)
- Use meaningful variable names
- Add comments for complex logic
```javascript
// Good
const handleChatRequest = async (request, env) => {
const { messages, model } = await request.json();
// Validate required fields
if (!messages || !Array.isArray(messages)) {
throw new Error('Messages must be an array');
}
return await processChat(messages, model, env);
};
// Avoid
const h = async (r, e) => {
const d = await r.json();
return await p(d.m, d.mod, e);
};
```
### Error Handling
Always provide meaningful error messages:
```javascript
// Good
if (!env.DEEPSEEK_API_KEY) {
return new Response(JSON.stringify({
error: 'configuration_error',
message: 'DEEPSEEK_API_KEY environment variable is required',
timestamp: new Date().toISOString()
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
```
### Security Best Practices
- Never log sensitive information (API keys, user data)
- Validate all input parameters
- Use proper HTTP status codes
- Implement rate limiting where appropriate
## 🐛 Bug Reports
### Before Reporting
1. Check existing issues
2. Test with latest version
3. Reproduce the issue consistently
### Bug Report Template
```markdown
**Bug Description**
A clear description of what the bug is.
**Steps to Reproduce**
1. Deploy worker with configuration X
2. Send request with payload Y
3. Observe error Z
**Expected Behavior**
What should have happened.
**Environment**
- Cloudflare Workers version
- Browser/client used
- Any relevant configuration
**Additional Context**
Logs, screenshots, or other helpful information.
```
## 💡 Feature Requests
### Feature Request Template
```markdown
**Feature Description**
A clear description of the feature you'd like to see.
**Use Case**
Explain why this feature would be useful.
**Proposed Implementation**
If you have ideas about how to implement this.
**Alternatives Considered**
Other solutions you've considered.
```
## 📖 Documentation
### Writing Guidelines
- Use clear, concise language
- Provide practical examples
- Include both English and Chinese versions
- Test all code examples
### Documentation Structure
```
docs/
├── Installation.md/en.md # Setup guides
├── Configuration.md/en.md # Configuration options
├── API-Reference.md/en.md # API documentation
├── Examples.md/en.md # Usage examples
├── Troubleshooting.md/en.md # Common issues
└── Contributing.md/en.md # This file
```
## 🔄 Release Process
### Version Numbering
We follow [Semantic Versioning](https://semver.org/):
- `MAJOR.MINOR.PATCH`
- Major: Breaking changes
- Minor: New features, backward compatible
- Patch: Bug fixes
### Release Checklist
- [ ] Update version in `wrangler.toml`
- [ ] Update CHANGELOG.md
- [ ] Test all functionality
- [ ] Update documentation
- [ ] Create release notes
- [ ] Tag release
## 🏆 Recognition
### Contributors
All contributors will be recognized in:
- README.md contributors section
- Release notes
- GitHub contributors page
### Types of Contributions
- 🐛 Bug fixes
- ✨ New features
- 📝 Documentation
- 🎨 UI/UX improvements
- 🔧 Infrastructure
- 🌍 Translations
## ❓ Getting Help
### Community Support
- [GitHub Discussions](../../discussions) - General questions
- [Issues](../../issues) - Bug reports and feature requests
- [Discord/Slack] - Real-time chat (if available)
### Code Review Process
1. All PRs require at least one review
2. Maintainers will review within 48 hours
3. Address feedback promptly
4. Squash commits before merging
## 📋 Contribution Checklist
Before submitting a PR, ensure:
- [ ] Code follows project style guidelines
- [ ] All tests pass
- [ ] Documentation updated if needed
- [ ] Commit messages are descriptive
- [ ] PR description explains the changes
- [ ] No sensitive information in code
- [ ] Feature works in production environment
---
**Thank you for contributing to AI Proxy Worker!** 🎉
Your contributions make this project better for everyone.

277
docs/Contributing.md Normal file
View File

@@ -0,0 +1,277 @@
# 贡献指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Contributing.en.md) | [🇨🇳 中文](./Contributing.md)
</div>
感谢你对 AI Proxy Worker 项目的关注!本指南将帮助你了解如何参与项目开发。
## 🚀 快速开始
### 前置要求
- Node.js 18+ 和 npm
- Git
- Cloudflare 账户(用于测试)
- JavaScript 和 Cloudflare Workers 基础知识
### 开发环境设置
```bash
# 克隆仓库
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
cd ai-proxy-worker
# 安装依赖
npm install -g wrangler
# 登录 Cloudflare
wrangler login
# 设置开发环境
cp wrangler.toml.example wrangler.toml
# 编辑 wrangler.toml 配置你的设置
```
## 🔧 开发流程
### 1. Fork 和 Clone
```bash
# 在 GitHub 上 Fork 仓库,然后克隆你的 fork
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
cd ai-proxy-worker
# 添加上游远程仓库
git remote add upstream https://github.com/original-owner/ai-proxy-worker.git
```
### 2. 创建功能分支
```bash
# 创建并切换到新分支
git checkout -b feature/your-feature-name
# 或者修复 bug
git checkout -b fix/bug-description
```
### 3. 本地开发
```bash
# 启动本地开发服务器
wrangler dev
# 测试你的更改
curl -X POST http://localhost:8787/chat \
-H "Content-Type: application/json" \
-d '{"model": "deepseek-chat", "messages": [{"role": "user", "content": "测试"}]}'
```
### 4. 测试
```bash
# 运行测试(如果可用)
npm test
# 手动测试清单:
# - 基本聊天功能
# - 流式响应
# - 错误处理
# - 身份验证
```
### 5. 提交和推送
```bash
# 添加更改
git add .
# 使用描述性消息提交
git commit -m "feat: 添加新 AI 模型支持"
# 推送到你的 fork
git push origin feature/your-feature-name
```
### 6. 创建 Pull Request
1. 在 GitHub 上访问你的 fork
2. 点击 "New Pull Request"
3. 填写 PR 模板
4. 等待审查
## 📝 代码规范
### JavaScript 风格
- 使用现代 ES6+ 特性
- 保持一致的缩进2个空格
- 使用有意义的变量名
- 为复杂逻辑添加注释
```javascript
// 好的示例
const handleChatRequest = async (request, env) => {
const { messages, model } = await request.json();
// 验证必需字段
if (!messages || !Array.isArray(messages)) {
throw new Error('消息必须是数组格式');
}
return await processChat(messages, model, env);
};
// 避免这样写
const h = async (r, e) => {
const d = await r.json();
return await p(d.m, d.mod, e);
};
```
### 错误处理
始终提供有意义的错误消息:
```javascript
// 好的示例
if (!env.DEEPSEEK_API_KEY) {
return new Response(JSON.stringify({
error: 'configuration_error',
message: '需要设置 DEEPSEEK_API_KEY 环境变量',
timestamp: new Date().toISOString()
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
```
### 安全最佳实践
- 永远不要记录敏感信息API 密钥、用户数据)
- 验证所有输入参数
- 使用适当的 HTTP 状态码
- 在适当的地方实施速率限制
## 🐛 Bug 报告
### 报告前检查
1. 检查现有 issues
2. 使用最新版本测试
3. 能够一致地重现问题
### Bug 报告模板
```markdown
**Bug 描述**
清楚地描述 bug 是什么。
**重现步骤**
1. 使用配置 X 部署 worker
2. 发送载荷 Y 的请求
3. 观察到错误 Z
**预期行为**
应该发生什么。
**环境信息**
- Cloudflare Workers 版本
- 使用的浏览器/客户端
- 任何相关配置
**附加上下文**
日志、截图或其他有用信息。
```
## 💡 功能请求
### 功能请求模板
```markdown
**功能描述**
清楚地描述你希望看到的功能。
**使用场景**
解释为什么这个功能有用。
**建议实现**
如果你对如何实现有想法。
**考虑的替代方案**
你考虑过的其他解决方案。
```
## 📖 文档
### 写作指南
- 使用清晰、简洁的语言
- 提供实用示例
- 包含中英文版本
- 测试所有代码示例
### 文档结构
```
docs/
├── Installation.md/en.md # 安装指南
├── Configuration.md/en.md # 配置选项
├── API-Reference.md/en.md # API 文档
├── Examples.md/en.md # 使用示例
├── Troubleshooting.md/en.md # 常见问题
└── Contributing.md/en.md # 本文件
```
## 🔄 发布流程
### 版本编号
我们遵循 [语义化版本](https://semver.org/lang/zh-CN/)
- `主版本.次版本.修订版本`
- 主版本:不兼容的 API 修改
- 次版本:向下兼容的功能性新增
- 修订版本:向下兼容的问题修正
### 发布清单
- [ ] 更新 `wrangler.toml` 中的版本
- [ ] 更新 CHANGELOG.md
- [ ] 测试所有功能
- [ ] 更新文档
- [ ] 创建发布说明
- [ ] 标记发布
## 🏆 致谢
### 贡献者
所有贡献者将在以下地方得到认可:
- README.md 贡献者部分
- 发布说明
- GitHub 贡献者页面
### 贡献类型
- 🐛 Bug 修复
- ✨ 新功能
- 📝 文档
- 🎨 UI/UX 改进
- 🔧 基础设施
- 🌍 翻译
## ❓ 获取帮助
### 社区支持
- [GitHub Discussions](../../discussions) - 一般问题
- [Issues](../../issues) - Bug 报告和功能请求
- [Discord/Slack] - 实时聊天(如果可用)
### 代码审查流程
1. 所有 PR 需要至少一次审查
2. 维护者将在 48 小时内审查
3. 及时处理反馈
4. 合并前压缩提交
## 📋 贡献清单
提交 PR 前,确保:
- [ ] 代码遵循项目风格指南
- [ ] 所有测试通过
- [ ] 如需要已更新文档
- [ ] 提交消息具有描述性
- [ ] PR 描述解释了更改
- [ ] 代码中没有敏感信息
- [ ] 功能在生产环境中正常工作
---
**感谢你为 AI Proxy Worker 做出贡献!** 🎉
你的贡献让这个项目对每个人都更好。

331
docs/Deployment.en.md Normal file
View File

@@ -0,0 +1,331 @@
# Deployment Tutorial
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Deployment.en.md) | [🇨🇳 中文](./Deployment.md)
</div>
This guide provides detailed instructions on deploying AI Proxy Worker to the Cloudflare Workers platform. We offer two deployment methods - choose based on your preference.
## 🎯 Deployment Methods Comparison
| Feature | Local CLI Deployment | Web Deployment |
|---------|---------------------|----------------|
| **Difficulty** | Medium | Easy |
| **Speed** | Fast | Medium |
| **Automation** | High | Low |
| **Version Control** | Git Support | Manual Management |
| **Batch Operations** | Script Support | Single Operation |
| **Recommended For** | Developers, CI/CD | Beginners, Quick Testing |
## 📋 Pre-deployment Preparation
### 1. Register Cloudflare Account
1. Visit [Cloudflare](https://www.cloudflare.com/)
2. Click **"Sign Up"** to register a free account
3. Verify email address
4. Login to Cloudflare Dashboard
### 2. Get DeepSeek API Key
1. Visit [DeepSeek Open Platform](https://platform.deepseek.com/)
2. Register and login to account
3. Go to **"API Keys"** page
4. Click **"Create new secret key"**
5. Copy and save the key (**Note: Only shown once**)
### 3. Prepare Project Files
Ensure you've completed the [Installation Guide](./Installation.en) steps and project files are ready.
## 🚀 Method 1: Local CLI Deployment (Recommended)
This is the recommended deployment method, especially suitable for developers and automated deployment scenarios.
### Step 1: Login to Cloudflare
```bash
# Login to Cloudflare account
wrangler login
```
This will:
1. Open browser window
2. Redirect to Cloudflare authorization page
3. Click **"Allow"** to authorize Wrangler
4. Automatically return to terminal showing login success
**Troubleshooting:**
```bash
# If browser doesn't open automatically
wrangler login --browser=false
# Manually copy the displayed URL to browser
# Check login status
wrangler whoami
```
### Step 2: Configure Project Information
Edit `wrangler.toml` file (optional):
```toml
name = "ai-proxy-worker" # Your Worker name, can be modified
main = "worker.js"
compatibility_date = "2025-08-17"
# Optional: Custom domain configuration
# [[routes]]
# pattern = "ai.yourdomain.com/*"
# zone_name = "yourdomain.com"
```
### Step 3: Set Environment Variables
Set required secrets:
```bash
# Set DeepSeek API key (required)
wrangler secret put DEEPSEEK_API_KEY
# Input prompt: Please enter value for DEEPSEEK_API_KEY:
# Paste your DeepSeek API key
# Set proxy access key (strongly recommended)
wrangler secret put PROXY_KEY
# Input prompt: Please enter value for PROXY_KEY:
# Enter a custom strong password, e.g.: sk-proxy-your-secret-key-2025
```
**Key Setting Recommendations:**
- `DEEPSEEK_API_KEY`: Real API key from DeepSeek platform
- `PROXY_KEY`: Custom access key, recommend using strong password generator
### Step 4: Deploy to Cloudflare Workers
```bash
# Deploy project
wrangler publish
```
Success output example:
```
⛅️ wrangler 3.15.0
-------------------
✨ Successfully published your Worker to
https://ai-proxy-worker.your-subdomain.workers.dev
✨ Success! Your worker is now live at
https://ai-proxy-worker.your-subdomain.workers.dev
```
### Step 5: Test Deployment
```bash
# Health check
curl https://ai-proxy-worker.your-subdomain.workers.dev/
# API test
curl -X POST https://ai-proxy-worker.your-subdomain.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Hello!"}]
}'
```
## 🌐 Method 2: Cloudflare Web Deployment
Suitable for beginners or quick testing scenarios.
### Step 1: Prepare Code Files
1. Open `worker.js` file in project directory
2. Select all and copy all code content (Ctrl+A, Ctrl+C)
### Step 2: Create Worker
1. Login to [Cloudflare Dashboard](https://dash.cloudflare.com/)
2. Click **"Workers & Pages"** in left menu
3. Click **"Create application"** button
4. Select **"Create Worker"** option
5. Enter Worker name, e.g.: `ai-proxy-worker`
6. Click **"Deploy"** button
### Step 3: Edit Code
1. Click **"Edit code"** button on Worker page
2. Delete default code in editor
3. Paste copied `worker.js` content
4. Click **"Save and deploy"** button
### Step 4: Set Environment Variables
1. Return to Worker homepage
2. Click **"Settings"** tab
3. Find **"Environment variables"** section
4. Click **"Add variable"** button
Add the following variables:
**Variable 1: DEEPSEEK_API_KEY**
- Variable name: `DEEPSEEK_API_KEY`
- Value: Your DeepSeek API key
- Type: **Secret** (Important: Select encrypted type)
**Variable 2: PROXY_KEY**
- Variable name: `PROXY_KEY`
- Value: Custom access key
- Type: **Secret**
5. Click **"Save and deploy"** button
### Step 5: Get Deployment URL
After deployment, you'll see the Worker's access URL:
```
https://ai-proxy-worker.your-subdomain.workers.dev
```
### Step 6: Test Deployment
Use browser or curl to test:
```bash
# Health check
curl https://your-worker-url.workers.dev/
# API call test
curl -X POST https://your-worker-url.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Hello!"}]
}'
```
## 📊 Post-deployment Verification
### 1. Function Testing
**Health Check:**
```bash
curl https://your-worker.workers.dev/
# Expected response: {"status":"ok","service":"AI Proxy Worker","timestamp":"..."}
```
**API Call Test:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "user", "content": "Hello, please introduce yourself"}
]
}'
```
**Streaming Response Test:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Write a short poem"}],
"stream": true
}'
```
## 🔄 Updates and Maintenance
### Update Code
**CLI Method:**
```bash
# After modifying code, redeploy
wrangler publish
# View deployment history
wrangler deployments list
```
**Web Method:**
1. Find your Worker in Cloudflare Dashboard
2. Click **"Edit code"**
3. Modify code
4. Click **"Save and deploy"**
### Manage Environment Variables
```bash
# View set secrets (doesn't show values)
wrangler secret list
# Update secrets
wrangler secret put DEEPSEEK_API_KEY
# Delete secrets
wrangler secret delete OLD_KEY_NAME
```
### Monitoring and Logs
```bash
# View real-time logs
wrangler tail
# View deployment status
wrangler deployments list
# View usage statistics
wrangler metrics
```
## 🚨 Common Deployment Issues
### Authentication Failed
```bash
# Re-login
wrangler logout
wrangler login
```
### Deployment Timeout
```bash
# Check network connection
curl -I https://api.cloudflare.com/
# Use proxy (if needed)
wrangler publish --proxy http://proxy.example.com:8080
```
### Environment Variables Not Taking Effect
```bash
# Confirm secrets are set
wrangler secret list
# Reset secrets
wrangler secret put DEEPSEEK_API_KEY
```
### Worker Inaccessible
1. Check Worker status is "Active"
2. Confirm URL spelling is correct
3. Check Cloudflare service status page
## 🎯 Next Steps
After successful deployment, you can:
1. **[Configure API Usage](./API-Reference.en)** - Learn complete API documentation
2. **[Integrate into Applications](./Examples.en)** - View integration examples in various programming languages
3. **[Monitor and Maintain](./Monitoring.en)** - Set up monitoring and log analysis
4. **[Performance Optimization](./Performance.en)** - Optimize Worker performance
---
**Deployment Successful?** 👉 [Start Using API](./API-Reference.en)

421
docs/Deployment.md Normal file
View File

@@ -0,0 +1,421 @@
# 部署教程
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Deployment.en.md) | [🇨🇳 中文](./Deployment.md)
</div>
本指南详细介绍如何将 AI Proxy Worker 部署到 Cloudflare Workers 平台。我们提供两种部署方式,你可以根据自己的偏好选择。
## 🎯 部署方式对比
| 特性 | 本地 CLI 部署 | 网页部署 |
|------|---------------|----------|
| **难度** | 中等 | 简单 |
| **速度** | 快速 | 中等 |
| **自动化** | 高 | 低 |
| **版本控制** | 支持 Git | 手动管理 |
| **批量操作** | 支持脚本 | 单个操作 |
| **推荐场景** | 开发者、CI/CD | 新手、快速测试 |
## 📋 部署前准备
### 1. 注册 Cloudflare 账户
1. 访问 [Cloudflare](https://www.cloudflare.com/)
2. 点击 **"Sign Up"** 注册免费账户
3. 验证邮箱地址
4. 登录到 Cloudflare Dashboard
### 2. 获取 DeepSeek API 密钥
1. 访问 [DeepSeek 开放平台](https://platform.deepseek.com/)
2. 注册并登录账户
3. 前往 **"API Keys"** 页面
4. 点击 **"Create new secret key"**
5. 复制并保存密钥(**注意:只显示一次**
### 3. 准备项目文件
确保你已经完成了 [安装指南](./Installation) 中的步骤,并且项目文件已就绪。
## 🚀 方法一:本地 CLI 部署(推荐)
这是推荐的部署方式,特别适合开发者和需要自动化部署的场景。
### 步骤 1登录 Cloudflare
```bash
# 登录 Cloudflare 账户
wrangler login
```
这会:
1. 打开浏览器窗口
2. 跳转到 Cloudflare 授权页面
3. 点击 **"Allow"** 授权 Wrangler
4. 自动返回终端,显示登录成功
**故障排除:**
```bash
# 如果浏览器没有自动打开
wrangler login --browser=false
# 手动复制显示的 URL 到浏览器
# 检查登录状态
wrangler whoami
```
### 步骤 2配置项目信息
编辑 `wrangler.toml` 文件(可选):
```toml
name = "ai-proxy-worker" # 你的 Worker 名称,可以修改
main = "worker.js"
compatibility_date = "2025-01-01"
# 可选:自定义域名配置
# [[routes]]
# pattern = "ai.yourdomain.com/*"
# zone_name = "yourdomain.com"
```
### 步骤 3设置环境变量
设置必需的密钥:
```bash
# 设置 DeepSeek API 密钥(必需)
wrangler secret put DEEPSEEK_API_KEY
# 输入提示:请输入 DEEPSEEK_API_KEY 的值:
# 粘贴你的 DeepSeek API 密钥
# 设置代理访问密钥(强烈推荐)
wrangler secret put PROXY_KEY
# 输入提示:请输入 PROXY_KEY 的值:
# 输入一个自定义的强密码sk-proxy-your-secret-key-2025
```
**密钥设置建议:**
- `DEEPSEEK_API_KEY`: 从 DeepSeek 平台获取的真实 API 密钥
- `PROXY_KEY`: 自定义的访问密钥,建议使用强密码生成器
### 步骤 4部署到 Cloudflare Workers
```bash
# 部署项目
wrangler publish
```
成功输出示例:
```
⛅️ wrangler 3.15.0
-------------------
✨ Successfully published your Worker to
https://ai-proxy-worker.your-subdomain.workers.dev
✨ Success! Your worker is now live at
https://ai-proxy-worker.your-subdomain.workers.dev
```
### 步骤 5测试部署
```bash
# 健康检查
curl https://ai-proxy-worker.your-subdomain.workers.dev/
# API 测试
curl -X POST https://ai-proxy-worker.your-subdomain.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "你好!"}]
}'
```
## 🌐 方法二Cloudflare 网页部署
适合新手用户或需要快速测试的场景。
### 步骤 1准备代码文件
1. 打开项目目录中的 `worker.js` 文件
2. 全选并复制所有代码内容Ctrl+A, Ctrl+C
### 步骤 2创建 Worker
1. 登录 [Cloudflare Dashboard](https://dash.cloudflare.com/)
2. 在左侧菜单中点击 **"Workers & Pages"**
3. 点击 **"Create application"** 按钮
4. 选择 **"Create Worker"** 选项
5. 输入 Worker 名称,如:`ai-proxy-worker`
6. 点击 **"Deploy"** 按钮
### 步骤 3编辑代码
1. 在 Worker 页面点击 **"Edit code"** 按钮
2. 删除编辑器中的默认代码
3. 粘贴复制的 `worker.js` 内容
4. 点击 **"Save and deploy"** 按钮
### 步骤 4设置环境变量
1. 返回 Worker 主页面
2. 点击 **"Settings"** 标签页
3. 找到 **"Environment variables"** 部分
4. 点击 **"Add variable"** 按钮
添加以下变量:
**变量 1DEEPSEEK_API_KEY**
- Variable name: `DEEPSEEK_API_KEY`
- Value: 你的 DeepSeek API 密钥
- Type: **Secret** (重要:选择加密类型)
**变量 2PROXY_KEY**
- Variable name: `PROXY_KEY`
- Value: 自定义的访问密钥
- Type: **Secret**
5. 点击 **"Save and deploy"** 按钮
### 步骤 5获取部署 URL
部署完成后,你会看到 Worker 的访问 URL
```
https://ai-proxy-worker.your-subdomain.workers.dev
```
### 步骤 6测试部署
使用浏览器或 curl 测试:
```bash
# 健康检查
curl https://your-worker-url.workers.dev/
# API 调用测试
curl -X POST https://your-worker-url.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Hello!"}]
}'
```
## ⚙️ 高级部署配置
### 自定义域名
如果你有自己的域名,可以绑定到 Worker
#### 方法 1通过 Dashboard
1. 在 Worker 页面点击 **"Triggers"** 标签
2. 点击 **"Add Custom Domain"**
3. 输入你的域名,如:`api.yourdomain.com`
4. 按照提示完成 DNS 配置
#### 方法 2通过 wrangler.toml
```toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-01-01"
[[routes]]
pattern = "api.yourdomain.com/*"
zone_name = "yourdomain.com"
```
### 环境配置
为不同环境设置不同的配置:
```toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-01-01"
# 生产环境
[env.production]
name = "ai-proxy-worker-prod"
vars = { ENVIRONMENT = "production" }
# 开发环境
[env.development]
name = "ai-proxy-worker-dev"
vars = { ENVIRONMENT = "development" }
```
部署到特定环境:
```bash
# 部署到开发环境
wrangler publish --env development
# 部署到生产环境
wrangler publish --env production
```
## 📊 部署后验证
### 1. 功能测试
**健康检查:**
```bash
curl https://your-worker.workers.dev/
# 期望响应:{"status":"ok","service":"AI Proxy Worker","timestamp":"..."}
```
**API 调用测试:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "user", "content": "你好,请介绍一下你自己"}
]
}'
```
**流式响应测试:**
```bash
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "写一首短诗"}],
"stream": true
}'
```
### 2. 性能测试
```bash
# 响应时间测试
curl -w "@curl-format.txt" -s -o /dev/null https://your-worker.workers.dev/
# 创建 curl-format.txt 文件:
echo " time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_pretransfer: %{time_pretransfer}\n
time_redirect: %{time_redirect}\n
time_starttransfer: %{time_starttransfer}\n
----------\n
time_total: %{time_total}\n" > curl-format.txt
```
### 3. 错误处理测试
```bash
# 测试未授权访问
curl -X POST https://your-worker.workers.dev/chat \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[]}'
# 期望401 Unauthorized
# 测试无效路径
curl https://your-worker.workers.dev/invalid-path
# 期望404 Not Found
# 测试无效请求体
curl -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d 'invalid-json'
# 期望400 Bad Request
```
## 🔄 更新和维护
### 更新代码
**CLI 方式:**
```bash
# 修改代码后重新部署
wrangler publish
# 查看部署历史
wrangler deployments list
```
**网页方式:**
1. 在 Cloudflare Dashboard 中找到你的 Worker
2. 点击 **"Edit code"**
3. 修改代码
4. 点击 **"Save and deploy"**
### 管理环境变量
```bash
# 查看已设置的密钥(不显示值)
wrangler secret list
# 更新密钥
wrangler secret put DEEPSEEK_API_KEY
# 删除密钥
wrangler secret delete OLD_KEY_NAME
```
### 监控和日志
```bash
# 实时查看日志
wrangler tail
# 查看部署状态
wrangler deployments list
# 查看使用统计
wrangler metrics
```
## 🚨 常见部署问题
### 认证失败
```bash
# 重新登录
wrangler logout
wrangler login
```
### 部署超时
```bash
# 检查网络连接
curl -I https://api.cloudflare.com/
# 使用代理(如果需要)
wrangler publish --proxy http://proxy.example.com:8080
```
### 环境变量未生效
```bash
# 确认密钥已设置
wrangler secret list
# 重新设置密钥
wrangler secret put DEEPSEEK_API_KEY
```
### Worker 无法访问
1. 检查 Worker 状态是否为 "Active"
2. 确认 URL 拼写正确
3. 查看 Cloudflare 服务状态页面
## 🎯 下一步
部署完成后,你可以:
1. **[配置 API 使用](./API-Reference)** - 了解完整的 API 文档
2. **[集成到应用](./Examples)** - 查看各种编程语言的集成示例
3. **[监控和维护](./Monitoring)** - 设置监控和日志分析
4. **[性能优化](./Performance)** - 优化 Worker 性能
---
**部署成功?** 👉 [开始使用 API](./API-Reference)

1150
docs/Examples.en.md Normal file

File diff suppressed because it is too large Load Diff

875
docs/Examples.md Normal file
View File

@@ -0,0 +1,875 @@
# 使用示例
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Examples.en.md) | [🇨🇳 中文](./Examples.md)
</div>
各种编程语言和框架集成 AI Proxy Worker 的实用示例。复制并调整这些示例以适应你的具体使用场景。
## 🌐 Web 应用
### JavaScript (原生)
基本网页实现:
```html
<!DOCTYPE html>
<html>
<head>
<title>AI 聊天演示</title>
<meta charset="UTF-8">
</head>
<body>
<div id="chat-container">
<div id="messages"></div>
<input type="text" id="user-input" placeholder="输入你的消息...">
<button onclick="sendMessage()">发送</button>
</div>
<script>
const PROXY_URL = 'https://your-worker.workers.dev';
const PROXY_KEY = 'your-proxy-key';
async function sendMessage() {
const input = document.getElementById('user-input');
const message = input.value.trim();
if (!message) return;
// 添加用户消息到聊天
addMessage('user', message);
input.value = '';
try {
const response = await fetch(`${PROXY_URL}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${PROXY_KEY}`
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'user', content: message }
]
})
});
if (!response.ok) {
throw new Error(`HTTP 错误! 状态: ${response.status}`);
}
const data = await response.json();
addMessage('assistant', data.choices[0].message.content);
} catch (error) {
console.error('错误:', error);
addMessage('error', '获取 AI 响应失败');
}
}
function addMessage(role, content) {
const messages = document.getElementById('messages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
messageDiv.textContent = `${role}: ${content}`;
messages.appendChild(messageDiv);
messages.scrollTop = messages.scrollHeight;
}
// 允许使用回车键发送消息
document.getElementById('user-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
<style>
#chat-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
#messages {
height: 400px;
overflow-y: auto;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
background-color: #f9f9f9;
}
.message {
margin-bottom: 10px;
padding: 5px;
border-radius: 5px;
}
.message.user {
background-color: #e3f2fd;
text-align: right;
}
.message.assistant {
background-color: #f3e5f5;
}
.message.error {
background-color: #ffebee;
color: #c62828;
}
#user-input {
width: 70%;
padding: 10px;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #2196f3;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #1976d2;
}
</style>
</body>
</html>
```
### React.js
支持流式响应的 React 组件:
```jsx
import React, { useState, useEffect } from 'react';
const PROXY_URL = 'https://your-worker.workers.dev';
const PROXY_KEY = 'your-proxy-key';
function ChatComponent() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const sendMessage = async () => {
if (!input.trim()) return;
const userMessage = { role: 'user', content: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
try {
const response = await fetch(`${PROXY_URL}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${PROXY_KEY}`,
'Accept': 'text/event-stream'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [...messages, userMessage],
stream: true
})
});
if (!response.ok) {
throw new Error(`HTTP 错误! 状态: ${response.status}`);
}
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let assistantMessage = { role: 'assistant', content: '' };
setMessages(prev => [...prev, assistantMessage]);
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content || '';
if (content) {
setMessages(prev => {
const updated = [...prev];
updated[updated.length - 1].content += content;
return updated;
});
}
} catch (e) {
console.error('解析块错误:', e);
}
}
}
}
} catch (error) {
console.error('错误:', error);
setMessages(prev => [...prev, {
role: 'error',
content: '获取 AI 响应失败'
}]);
} finally {
setIsLoading(false);
}
};
return (
<div className="chat-container">
<div className="messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.role}`}>
<strong>{message.role === 'user' ? '用户' : message.role === 'assistant' ? '助手' : '错误'}:</strong> {message.content}
</div>
))}
{isLoading && <div className="loading">AI 正在思考...</div>}
</div>
<div className="input-container">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="输入你的消息..."
disabled={isLoading}
/>
<button onClick={sendMessage} disabled={isLoading}>
发送
</button>
</div>
</div>
);
}
export default ChatComponent;
```
### Vue.js
Vue 3 组合式 API 示例:
```vue
<template>
<div class="chat-container">
<div class="messages" ref="messagesContainer">
<div
v-for="(message, index) in messages"
:key="index"
:class="`message ${message.role}`"
>
<strong>{{ getRoleText(message.role) }}:</strong> {{ message.content }}
</div>
<div v-if="isLoading" class="loading">AI 正在思考...</div>
</div>
<div class="input-container">
<input
v-model="input"
@keypress.enter="sendMessage"
placeholder="输入你的消息..."
:disabled="isLoading"
/>
<button @click="sendMessage" :disabled="isLoading">
发送
</button>
</div>
</div>
</template>
<script setup>
import { ref, nextTick, onMounted } from 'vue';
const PROXY_URL = 'https://your-worker.workers.dev';
const PROXY_KEY = 'your-proxy-key';
const messages = ref([]);
const input = ref('');
const isLoading = ref(false);
const messagesContainer = ref(null);
const getRoleText = (role) => {
switch (role) {
case 'user': return '用户';
case 'assistant': return '助手';
case 'error': return '错误';
default: return role;
}
};
const scrollToBottom = () => {
nextTick(() => {
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
}
});
};
const sendMessage = async () => {
if (!input.value.trim()) return;
const userMessage = { role: 'user', content: input.value };
messages.value.push(userMessage);
input.value = '';
isLoading.value = true;
scrollToBottom();
try {
const response = await fetch(`${PROXY_URL}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${PROXY_KEY}`
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: messages.value
})
});
if (!response.ok) {
throw new Error(`HTTP 错误! 状态: ${response.status}`);
}
const data = await response.json();
messages.value.push({
role: 'assistant',
content: data.choices[0].message.content
});
scrollToBottom();
} catch (error) {
console.error('错误:', error);
messages.value.push({
role: 'error',
content: '获取 AI 响应失败'
});
} finally {
isLoading.value = false;
}
};
onMounted(() => {
// 添加欢迎消息
messages.value.push({
role: 'assistant',
content: '你好!我是你的 AI 助手。今天我能为你做些什么?'
});
});
</script>
<style scoped>
.chat-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.messages {
height: 400px;
overflow-y: auto;
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
background-color: #f9f9f9;
}
.message {
margin-bottom: 10px;
padding: 8px;
border-radius: 4px;
}
.message.user {
background-color: #e3f2fd;
text-align: right;
}
.message.assistant {
background-color: #f3e5f5;
}
.message.error {
background-color: #ffebee;
color: #c62828;
}
.loading {
font-style: italic;
color: #666;
text-align: center;
}
.input-container {
display: flex;
gap: 10px;
}
input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
background-color: #2196f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover:not(:disabled) {
background-color: #1976d2;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
</style>
```
## 📱 移动应用
### iOS (Swift)
使用 async/await 的 Swift 实现:
```swift
import Foundation
class AIProxyService {
private let proxyURL = "https://your-worker.workers.dev"
private let proxyKey = "your-proxy-key"
struct ChatRequest: Codable {
let model: String
let messages: [Message]
let stream: Bool?
}
struct Message: Codable {
let role: String
let content: String
}
struct ChatResponse: Codable {
let choices: [Choice]
}
struct Choice: Codable {
let message: Message
}
func sendMessage(_ messages: [Message]) async throws -> String {
guard let url = URL(string: "\(proxyURL)/chat") else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(proxyKey)", forHTTPHeaderField: "Authorization")
let chatRequest = ChatRequest(
model: "deepseek-chat",
messages: messages,
stream: false
)
request.httpBody = try JSONEncoder().encode(chatRequest)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
let chatResponse = try JSONDecoder().decode(ChatResponse.self, from: data)
return chatResponse.choices.first?.message.content ?? ""
}
}
// SwiftUI 视图中的使用
import SwiftUI
struct ChatView: View {
@State private var messages: [AIProxyService.Message] = []
@State private var inputText = ""
@State private var isLoading = false
private let aiService = AIProxyService()
var body: some View {
VStack {
ScrollView {
LazyVStack(alignment: .leading, spacing: 10) {
ForEach(messages.indices, id: \.self) { index in
MessageBubble(message: messages[index])
}
if isLoading {
HStack {
ProgressView()
.scaleEffect(0.8)
Text("AI 正在思考...")
.foregroundColor(.secondary)
}
.padding()
}
}
}
HStack {
TextField("输入你的消息...", text: $inputText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(isLoading)
Button("发送") {
Task {
await sendMessage()
}
}
.disabled(inputText.isEmpty || isLoading)
}
.padding()
}
.navigationTitle("AI 聊天")
}
private func sendMessage() async {
let userMessage = AIProxyService.Message(role: "user", content: inputText)
messages.append(userMessage)
let currentInput = inputText
inputText = ""
isLoading = true
do {
let response = try await aiService.sendMessage(messages)
let assistantMessage = AIProxyService.Message(role: "assistant", content: response)
messages.append(assistantMessage)
} catch {
let errorMessage = AIProxyService.Message(role: "error", content: "获取响应失败: \(error.localizedDescription)")
messages.append(errorMessage)
}
isLoading = false
}
}
struct MessageBubble: View {
let message: AIProxyService.Message
var body: some View {
HStack {
if message.role == "user" {
Spacer()
}
VStack(alignment: .leading) {
Text(getRoleText(message.role))
.font(.caption)
.foregroundColor(.secondary)
Text(message.content)
.padding()
.background(backgroundColor)
.cornerRadius(10)
}
if message.role == "assistant" || message.role == "error" {
Spacer()
}
}
.padding(.horizontal)
}
private func getRoleText(_ role: String) -> String {
switch role {
case "user": return "用户"
case "assistant": return "助手"
case "error": return "错误"
default: return role
}
}
private var backgroundColor: Color {
switch message.role {
case "user":
return .blue.opacity(0.2)
case "assistant":
return .purple.opacity(0.2)
case "error":
return .red.opacity(0.2)
default:
return .gray.opacity(0.2)
}
}
}
```
## 🖥️ 桌面应用
### Python
使用 tkinter 的 Python 桌面应用:
```python
import tkinter as tk
from tkinter import scrolledtext, messagebox
import requests
import json
import threading
from typing import List, Dict
class AIProxyClient:
def __init__(self, proxy_url: str, proxy_key: str):
self.proxy_url = proxy_url
self.proxy_key = proxy_key
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {proxy_key}',
'Content-Type': 'application/json'
})
def send_message(self, messages: List[Dict[str, str]]) -> str:
try:
response = self.session.post(
f'{self.proxy_url}/chat',
json={
'model': 'deepseek-chat',
'messages': messages
},
timeout=30
)
response.raise_for_status()
data = response.json()
return data['choices'][0]['message']['content']
except requests.exceptions.RequestException as e:
raise Exception(f"请求失败: {str(e)}")
except (KeyError, IndexError) as e:
raise Exception(f"无效的响应格式: {str(e)}")
class ChatGUI:
def __init__(self):
self.root = tk.Tk()
self.root.title("AI 聊天助手")
self.root.geometry("600x500")
# 初始化 AI 客户端
self.ai_client = AIProxyClient(
proxy_url="https://your-worker.workers.dev",
proxy_key="your-proxy-key"
)
self.messages = []
self.setup_ui()
def setup_ui(self):
# 聊天显示区域
self.chat_display = scrolledtext.ScrolledText(
self.root,
wrap=tk.WORD,
state=tk.DISABLED,
height=20,
font=('微软雅黑', 10)
)
self.chat_display.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 输入框架
input_frame = tk.Frame(self.root)
input_frame.pack(fill=tk.X, padx=10, pady=5)
# 消息输入
self.message_entry = tk.Entry(input_frame, font=('微软雅黑', 10))
self.message_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
self.message_entry.bind('<Return>', self.send_message)
# 发送按钮
self.send_button = tk.Button(
input_frame,
text="发送",
command=self.send_message,
font=('微软雅黑', 10),
bg='#2196f3',
fg='white',
relief=tk.FLAT,
padx=20
)
self.send_button.pack(side=tk.RIGHT)
# 状态栏
self.status_var = tk.StringVar()
self.status_var.set("就绪")
status_bar = tk.Label(
self.root,
textvariable=self.status_var,
relief=tk.SUNKEN,
anchor=tk.W
)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# 添加欢迎消息
self.add_message("assistant", "你好!我是你的 AI 助手。今天我能为你做些什么?")
def add_message(self, role: str, content: str):
self.chat_display.config(state=tk.NORMAL)
# 添加角色标签
role_text = {"user": "用户", "assistant": "助手", "error": "错误"}.get(role, role)
self.chat_display.insert(tk.END, f"{role_text}: ", f"{role}_label")
# 添加消息内容
self.chat_display.insert(tk.END, f"{content}\n\n")
# 配置标签样式
self.chat_display.tag_config("user_label", foreground="blue", font=('微软雅黑', 10, 'bold'))
self.chat_display.tag_config("assistant_label", foreground="purple", font=('微软雅黑', 10, 'bold'))
self.chat_display.tag_config("error_label", foreground="red", font=('微软雅黑', 10, 'bold'))
self.chat_display.config(state=tk.DISABLED)
self.chat_display.see(tk.END)
def send_message(self, event=None):
message = self.message_entry.get().strip()
if not message:
return
# 添加用户消息
self.add_message("user", message)
self.messages.append({"role": "user", "content": message})
# 清空输入
self.message_entry.delete(0, tk.END)
# 禁用发送按钮并显示加载状态
self.send_button.config(state=tk.DISABLED)
self.status_var.set("AI 正在思考...")
# 在后台线程中发送请求
threading.Thread(target=self.get_ai_response, daemon=True).start()
def get_ai_response(self):
try:
response = self.ai_client.send_message(self.messages)
# 在主线程中更新 UI
self.root.after(0, lambda: self.handle_ai_response(response))
except Exception as e:
error_msg = f"错误: {str(e)}"
self.root.after(0, lambda: self.handle_error(error_msg))
def handle_ai_response(self, response: str):
self.add_message("assistant", response)
self.messages.append({"role": "assistant", "content": response})
# 重新启用发送按钮
self.send_button.config(state=tk.NORMAL)
self.status_var.set("就绪")
self.message_entry.focus()
def handle_error(self, error_msg: str):
self.add_message("error", error_msg)
# 重新启用发送按钮
self.send_button.config(state=tk.NORMAL)
self.status_var.set("就绪")
self.message_entry.focus()
def run(self):
self.root.mainloop()
if __name__ == "__main__":
app = ChatGUI()
app.run()
```
## 🔧 后端集成
### Node.js/Express
用于代理 AI 请求的 Express 中间件:
```javascript
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
const PROXY_URL = 'https://your-worker.workers.dev';
const PROXY_KEY = 'your-proxy-key';
// 代理 AI 请求的中间件
app.post('/api/chat', async (req, res) => {
try {
const response = await fetch(`${PROXY_URL}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${PROXY_KEY}`
},
body: JSON.stringify(req.body)
});
if (!response.ok) {
throw new Error(`代理错误: ${response.status} ${response.statusText}`);
}
const data = await response.json();
res.json(data);
} catch (error) {
console.error('AI 代理错误:', error);
res.status(500).json({
error: 'ai_proxy_error',
message: '获取 AI 响应失败'
});
}
});
// 健康检查端点
app.get('/api/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
```
---
**准备好集成了吗?** 🚀
选择最适合你技术栈的示例,并根据你的具体需求进行定制。所有示例都包含适当的错误处理,可以扩展更多功能,如对话历史、用户认证等。

137
docs/Home.en.md Normal file
View File

@@ -0,0 +1,137 @@
# AI Proxy Worker Wiki
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Home.en.md) | [🇨🇳 中文](./Home.md)
</div>
Welcome to the complete documentation for AI Proxy Worker! Here you'll find detailed information from installation to advanced usage.
## 📚 Documentation Navigation
### 🚀 Quick Start
- **[Installation Guide](./Installation.en)** - Detailed system installation steps
- **[Deployment Tutorial](./Deployment.en)** - Multiple deployment methods comparison
- **[Quick Setup](./Quick-Setup.en)** - 5-minute quick configuration guide
### 📖 Usage Guide
- **[API Documentation](./API-Reference.en)** - Complete API reference
- **[Configuration Guide](./Configuration.en)** - Environment variables and code configuration details
- **[Usage Examples](./Examples.en)** - Examples in various programming languages
### 🔧 Operations Guide
- **[Troubleshooting](./Troubleshooting.en)** - Common issues and solutions
- **[Monitoring Guide](./Monitoring.en)** - Logging and performance monitoring
- **[Security Configuration](./Security.en)** - Production environment security best practices
### 🏗️ Development Guide
- **[Contributing Guide](./Contributing.en)** - How to participate in project development
- **[Code Style](./Code-Style.en)** - Code style and best practices
- **[Testing Guide](./Testing.en)** - Testing writing and execution
### 🔮 Advanced Features
- **[Multi-AI Support](./Multi-AI-Support.en)** - Extending support for other AI services
- **[Custom Middleware](./Custom-Middleware.en)** - Adding custom functionality
- **[Performance Optimization](./Performance.en)** - Performance tuning guide
## 🎯 Project Overview
AI Proxy Worker is a universal AI API proxy service based on Cloudflare Workers, designed to solve the following problems:
### 🔐 Security Issues
- **Problem**: Client applications calling AI APIs directly need to expose API keys
- **Solution**: Proxy service stores keys securely on the server side, clients only need proxy access keys
### ⚡ Performance Issues
- **Problem**: Direct calls may be affected by network restrictions
- **Solution**: Based on Cloudflare's global edge network for nearby access
### 🔄 Compatibility Issues
- **Problem**: Different AI service providers have inconsistent API formats
- **Solution**: Provides unified proxy interface, future support for multiple AI service providers
## 🏗️ Architecture Design
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Client Apps │────│ AI Proxy │────│ AI Service │
│ (iOS/Web) │ │ Worker │ │ (DeepSeek) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ PROXY_KEY │ DEEPSEEK_API_KEY │
│ (Public Safe) │ (Server Secret) │
└────────────────────────┴───────────────────────┘
```
### Core Components
1. **Authentication Layer**: Handles client access control
2. **Routing Layer**: Distributes requests to different AI services
3. **Proxy Layer**: Communicates with upstream AI APIs
4. **Security Layer**: Request validation, rate limiting, logging
## 📊 Supported AI Services
### Currently Supported (v1.0)
-**DeepSeek API** - Complete support including conversation and reasoning models
- `deepseek-chat` - General conversation model
- `deepseek-reasoner` - Complex reasoning model
### Planned Support (Future Versions)
- 🔄 **OpenAI API** - GPT series models (planned for v2.0)
- 🔄 **Claude API** - Anthropic Claude models (planned for v2.0)
- 🔄 **Gemini API** - Google Gemini models (planned for v2.0)
- 🔄 **Unified Routing** - One interface to call all AI service providers (planned for v2.0)
> **Note**: The current version focuses on providing stable and reliable DeepSeek API proxy service. Multi-AI support is under development, stay tuned!
## 🎯 Use Cases
### Mobile Applications
- iOS/Android apps securely call AI APIs
- Avoid storing sensitive keys in clients
- Provide unified AI service interface
### Web Applications
- Frontend direct calls without backend relay
- Support streaming responses for real-time conversation experience
- Global CDN accelerated access
### Microservice Architecture
- Serve as AI service gateway
- Unified AI API access layer
- Support multi-tenancy and access control
## 🔄 Version History
### v1.0.0 (Current Version)
- ✅ Support DeepSeek API
- ✅ Complete error handling
- ✅ Streaming response support
- ✅ Security protection mechanisms
### Future Versions
- 🔮 Multi-AI service provider support
- 🔮 User-level access control
- 🔮 Request rate limiting and quotas
- 🔮 Detailed usage statistics
## 🤝 Community
### Ways to Participate
- 🐛 [Report Bugs](../../issues/new?template=bug_report.md)
- 💡 [Feature Suggestions](../../issues/new?template=feature_request.md)
- 📝 [Improve Documentation](../../issues/new?template=documentation.md)
- 🔧 [Submit Code](./Contributing.en)
### Community Resources
- [GitHub Discussions](../../discussions) - Discussion and communication
- [Example Projects](./Examples.en) - Real-world use cases
- [Best Practices](./Best-Practices.en) - Experience sharing
---
**Ready to Start?** 👉 [Installation Guide](./Installation.en)

137
docs/Home.md Normal file
View File

@@ -0,0 +1,137 @@
# AI Proxy Worker Wiki
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Home.en.md) | [🇨🇳 中文](./Home.md)
</div>
欢迎来到 AI Proxy Worker 的完整文档!这里包含了从安装到高级使用的所有详细信息。
## 📚 文档导航
### 🚀 快速开始
- **[安装指南](./Installation)** - 详细的系统安装步骤
- **[部署教程](./Deployment)** - 多种部署方式对比
- **[快速配置](./Quick-Setup)** - 5分钟快速配置指南
### 📖 使用指南
- **[API 文档](./API-Reference)** - 完整的 API 参考
- **[配置指南](./Configuration)** - 环境变量和代码配置详解
- **[使用示例](./Examples)** - 各种编程语言示例
### 🔧 运维指南
- **[故障排除](./Troubleshooting)** - 常见问题解决方案
- **[监控指南](./Monitoring)** - 日志和性能监控
- **[安全配置](./Security)** - 生产环境安全最佳实践
### 🏗️ 开发指南
- **[贡献指南](./Contributing)** - 如何参与项目开发
- **[代码规范](./Code-Style)** - 代码风格和最佳实践
- **[测试指南](./Testing)** - 测试编写和执行
### 🔮 高级功能
- **[多 AI 支持](./Multi-AI-Support)** - 扩展支持其他 AI 服务
- **[自定义中间件](./Custom-Middleware)** - 添加自定义功能
- **[性能优化](./Performance)** - 性能调优指南
## 🎯 项目概览
AI Proxy Worker 是一个基于 Cloudflare Workers 的通用 AI API 代理服务,旨在解决以下问题:
### 🔐 安全性问题
- **问题**:客户端应用直接调用 AI API 需要暴露 API 密钥
- **解决**:代理服务将密钥安全存储在服务端,客户端只需要代理访问密钥
### ⚡ 性能问题
- **问题**:直接调用可能受网络限制影响速度
- **解决**:基于 Cloudflare 全球边缘网络,就近访问
### 🔄 兼容性问题
- **问题**:不同 AI 服务商 API 格式不统一
- **解决**:提供统一的代理接口,未来支持多个 AI 服务商
## 🏗️ 架构设计
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 客户端应用 │────│ AI Proxy │────│ AI 服务商 │
│ (iOS/Web) │ │ Worker │ │ (DeepSeek) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ PROXY_KEY │ DEEPSEEK_API_KEY │
│ (公开安全) │ (服务端机密) │
└────────────────────────┴───────────────────────┘
```
### 核心组件
1. **认证层**:处理客户端访问控制
2. **路由层**:分发请求到不同的 AI 服务
3. **代理层**:与上游 AI API 通信
4. **安全层**:请求验证、限流、日志记录
## 📊 支持的 AI 服务
### 当前支持 (v1.0)
-**DeepSeek API** - 完整支持,包含对话和推理模型
- `deepseek-chat` - 通用对话模型
- `deepseek-reasoner` - 复杂推理模型
### 计划支持 (未来版本)
- 🔄 **OpenAI API** - GPT 系列模型 (v2.0 计划)
- 🔄 **Claude API** - Anthropic Claude 模型 (v2.0 计划)
- 🔄 **Gemini API** - Google Gemini 模型 (v2.0 计划)
- 🔄 **统一路由** - 一套接口调用所有 AI 服务商 (v2.0 计划)
> **注意**: 当前版本专注于提供稳定可靠的 DeepSeek API 代理服务。多 AI 支持正在开发中,敬请期待!
## 🎯 使用场景
### 移动应用
- iOS/Android 应用安全调用 AI API
- 避免在客户端存储敏感密钥
- 提供统一的 AI 服务接口
### Web 应用
- 前端直接调用,无需后端中转
- 支持流式响应,实时对话体验
- 全球 CDN 加速访问
### 微服务架构
- 作为 AI 服务网关
- 统一的 AI API 访问层
- 支持多租户和访问控制
## 🔄 版本历史
### v1.0.0 (当前版本)
- ✅ 支持 DeepSeek API
- ✅ 完整的错误处理
- ✅ 流式响应支持
- ✅ 安全防护机制
### 未来版本
- 🔮 多 AI 服务商支持
- 🔮 用户级访问控制
- 🔮 请求限流和配额
- 🔮 详细的使用统计
## 🤝 社区
### 参与方式
- 🐛 [报告 Bug](../../issues/new?template=bug_report.md)
- 💡 [功能建议](../../issues/new?template=feature_request.md)
- 📝 [改进文档](../../issues/new?template=documentation.md)
- 🔧 [提交代码](./Contributing)
### 社区资源
- [GitHub Discussions](../../discussions) - 讨论和交流
- [示例项目](./Examples) - 实际使用案例
- [最佳实践](./Best-Practices) - 经验分享
---
**开始使用?** 👉 [安装指南](./Installation)

329
docs/Installation.en.md Normal file
View File

@@ -0,0 +1,329 @@
# Installation Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Installation.en.md) | [🇨🇳 中文](./Installation.md)
</div>
This guide provides detailed instructions on how to install the AI Proxy Worker development and deployment environment on different operating systems.
## 📋 System Requirements
### Minimum Requirements
- **Node.js**: 18.x or higher
- **npm**: 9.x or higher
- **Git**: 2.x or higher
- **Cloudflare Account**: Free account is sufficient
### Recommended Configuration
- **Node.js**: 20.x LTS
- **Operating System**: Windows 10+, macOS 12+, Ubuntu 20.04+
- **Memory**: 4GB+ (for development)
## 🖥️ Windows Installation
### Method 1: Using Official Node.js Installer (Recommended)
#### 1. Download and Install Node.js
1. Visit [Node.js official website](https://nodejs.org/)
2. Download **LTS version** (recommended 20.x)
3. Run the `.msi` installer
4. Keep all default options during installation
5. Ensure **"Add to PATH"** option is checked
#### 2. Verify Installation
Open **Command Prompt** (CMD) or **PowerShell**:
```cmd
# Check Node.js version
node --version
# Should display something like: v20.10.0
# Check npm version
npm --version
# Should display something like: 10.2.3
```
#### 3. Install Git
1. Visit [Git official website](https://git-scm.com/)
2. Download Windows version
3. Run installer with default settings
4. Verify installation:
```cmd
git --version
# Should display something like: git version 2.43.0
```
### Method 2: Using Package Managers
#### Using Chocolatey
1. Open PowerShell as Administrator
2. Install Chocolatey:
```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
```
3. Install dependencies:
```powershell
choco install nodejs git -y
```
#### Using Scoop
```powershell
# Install Scoop
iwr -useb get.scoop.sh | iex
# Install dependencies
scoop install nodejs git
```
### Method 3: Using WSL2 (Recommended for Developers)
1. Enable WSL2:
```powershell
# Run as Administrator
wsl --install
```
2. Install Ubuntu:
```bash
wsl --install -d Ubuntu
```
3. Follow Linux installation steps in WSL2
## 🍎 macOS Installation
### Method 1: Using Homebrew (Strongly Recommended)
#### 1. Install Homebrew
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
#### 2. Install Dependencies
```bash
# Install Node.js (includes npm)
brew install node
# Install Git
brew install git
# Verify installation
node --version && npm --version && git --version
```
### Method 2: Using Official Installers
#### 1. Install Node.js
1. Visit [Node.js official website](https://nodejs.org/)
2. Download macOS version of LTS release
3. Run the `.pkg` installer
4. Follow installation wizard
#### 2. Install Git
```bash
# Git is usually pre-installed, check version
git --version
# If not installed, download official installer
# or use Xcode Command Line Tools
xcode-select --install
```
### Method 3: Using MacPorts
```bash
# After installing MacPorts
sudo port install nodejs20 +universal
sudo port install git
```
## 🐧 Linux Installation
### Ubuntu/Debian
#### Using apt (Recommended)
```bash
# Update package list
sudo apt update
# Install Node.js 20.x
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install Git
sudo apt install git -y
# Verify installation
node --version && npm --version && git --version
```
#### Using snap
```bash
sudo snap install node --classic
sudo apt install git -y
```
### CentOS/RHEL/Fedora
#### Using dnf/yum
```bash
# Fedora
sudo dnf install nodejs npm git -y
# CentOS/RHEL (requires EPEL)
sudo yum install epel-release -y
sudo yum install nodejs npm git -y
```
#### Using NodeSource
```bash
# Install Node.js 20.x
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo yum install nodejs git -y
```
### Arch Linux
```bash
sudo pacman -S nodejs npm git
```
## 🔧 Install Wrangler CLI
After installing Node.js, globally install Cloudflare Wrangler:
```bash
# Globally install Wrangler
npm install -g wrangler
# Verify installation
wrangler --version
# Should display something like: ⛅️ wrangler 3.15.0
```
### Common Issues Resolution
#### Permission Issues (Linux/macOS)
```bash
# If encountering permission errors, configure npm global directory
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
# Add to PATH (add to ~/.bashrc or ~/.zshrc)
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# Reinstall Wrangler
npm install -g wrangler
```
#### Windows Execution Policy Issues
```powershell
# If encountering execution policy errors
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
## 📦 Get Project Code
### Method 1: Git Clone (Recommended)
```bash
# Clone project
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
# Enter project directory
cd ai-proxy-worker
# View project structure
ls -la
```
### Method 2: Download ZIP
1. Visit project GitHub page
2. Click green **"Code"** button
3. Select **"Download ZIP"**
4. Extract to local directory
5. Open terminal and navigate to project directory
## ✅ Verify Installation
Run the following commands to verify all dependencies are correctly installed:
```bash
# Check Node.js
node --version
# ✅ Should display v18.0.0 or higher
# Check npm
npm --version
# ✅ Should display 9.0.0 or higher
# Check Git
git --version
# ✅ Should display git version 2.x.x
# Check Wrangler
wrangler --version
# ✅ Should display wrangler 3.x.x
# Check project files
ls worker.js wrangler.toml
# ✅ Should show these two files exist
```
## 🚀 Next Steps
After installation, you can:
1. **[Configure deployment environment](./Deployment.en)** - Set up Cloudflare account and keys
2. **[Quick deployment](./Quick-Setup.en)** - Deploy to production in 5 minutes
3. **[Local development](./Development.en)** - Set up local development environment
## 🔧 Troubleshooting
### Common Installation Issues
#### Node.js Version Too Old
```bash
# Uninstall old version
sudo apt remove nodejs npm # Ubuntu
brew uninstall node # macOS
# Reinstall latest version following steps above
```
#### npm Permission Issues
```bash
# Linux/macOS solution
sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/local/lib/node_modules
```
#### Wrangler Installation Failed
```bash
# Clear npm cache
npm cache clean --force
# Reinstall
npm install -g wrangler
```
#### Network Issues (China Users)
```bash
# Use Taobao mirror
npm config set registry https://registry.npmmirror.com/
# Reinstall
npm install -g wrangler
```
### Get Help
If you encounter installation issues:
1. Check [Troubleshooting Guide](./Troubleshooting.en)
2. Search [GitHub Issues](../../issues)
3. Ask questions in [Discussions](../../discussions)
4. Check [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)
---
**Installation Complete?** 👉 [Start Deployment](./Deployment.en)

329
docs/Installation.md Normal file
View File

@@ -0,0 +1,329 @@
# 安装指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Installation.en.md) | [🇨🇳 中文](./Installation.md)
</div>
本指南将详细介绍如何在不同操作系统上安装 AI Proxy Worker 的开发和部署环境。
## 📋 系统要求
### 最低要求
- **Node.js**: 18.x 或更高版本
- **npm**: 9.x 或更高版本
- **Git**: 2.x 或更高版本
- **Cloudflare 账户**: 免费账户即可
### 推荐配置
- **Node.js**: 20.x LTS
- **操作系统**: Windows 10+, macOS 12+, Ubuntu 20.04+
- **内存**: 4GB+(开发时)
## 🖥️ Windows 系统安装
### 方法一:使用 Node.js 官方安装包(推荐)
#### 1. 下载并安装 Node.js
1. 访问 [Node.js 官网](https://nodejs.org/)
2. 下载 **LTS 版本**(推荐 20.x
3. 运行 `.msi` 安装文件
4. 安装过程中保持所有默认选项
5. 确保勾选 **"Add to PATH"** 选项
#### 2. 验证安装
打开 **命令提示符** (CMD) 或 **PowerShell**
```cmd
# 检查 Node.js 版本
node --version
# 应该显示类似v20.10.0
# 检查 npm 版本
npm --version
# 应该显示类似10.2.3
```
#### 3. 安装 Git
1. 访问 [Git 官网](https://git-scm.com/)
2. 下载 Windows 版本
3. 运行安装程序,保持默认设置
4. 验证安装:
```cmd
git --version
# 应该显示类似git version 2.43.0
```
### 方法二:使用包管理器
#### 使用 Chocolatey
1. 以管理员身份打开 PowerShell
2. 安装 Chocolatey
```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
```
3. 安装依赖:
```powershell
choco install nodejs git -y
```
#### 使用 Scoop
```powershell
# 安装 Scoop
iwr -useb get.scoop.sh | iex
# 安装依赖
scoop install nodejs git
```
### 方法三:使用 WSL2推荐开发者
1. 启用 WSL2
```powershell
# 以管理员身份运行
wsl --install
```
2. 安装 Ubuntu
```bash
wsl --install -d Ubuntu
```
3. 在 WSL2 中按照 Linux 安装步骤进行
## 🍎 macOS 系统安装
### 方法一:使用 Homebrew强烈推荐
#### 1. 安装 Homebrew
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
#### 2. 安装依赖
```bash
# 安装 Node.js包含 npm
brew install node
# 安装 Git
brew install git
# 验证安装
node --version && npm --version && git --version
```
### 方法二:使用官方安装包
#### 1. 安装 Node.js
1. 访问 [Node.js 官网](https://nodejs.org/)
2. 下载 macOS 版本的 LTS 版本
3. 运行 `.pkg` 安装文件
4. 按照安装向导完成安装
#### 2. 安装 Git
```bash
# Git 通常已预装,检查版本
git --version
# 如果没有安装,下载官方安装包
# 或使用 Xcode Command Line Tools
xcode-select --install
```
### 方法三:使用 MacPorts
```bash
# 安装 MacPorts 后
sudo port install nodejs20 +universal
sudo port install git
```
## 🐧 Linux 系统安装
### Ubuntu/Debian
#### 使用 apt推荐
```bash
# 更新包列表
sudo apt update
# 安装 Node.js 20.x
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# 安装 Git
sudo apt install git -y
# 验证安装
node --version && npm --version && git --version
```
#### 使用 snap
```bash
sudo snap install node --classic
sudo apt install git -y
```
### CentOS/RHEL/Fedora
#### 使用 dnf/yum
```bash
# Fedora
sudo dnf install nodejs npm git -y
# CentOS/RHEL (需要 EPEL)
sudo yum install epel-release -y
sudo yum install nodejs npm git -y
```
#### 使用 NodeSource
```bash
# 安装 Node.js 20.x
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo yum install nodejs git -y
```
### Arch Linux
```bash
sudo pacman -S nodejs npm git
```
## 🔧 安装 Wrangler CLI
安装完 Node.js 后,全局安装 Cloudflare Wrangler
```bash
# 全局安装 Wrangler
npm install -g wrangler
# 验证安装
wrangler --version
# 应该显示类似:⛅️ wrangler 3.15.0
```
### 常见问题解决
#### 权限问题Linux/macOS
```bash
# 如果遇到权限错误,配置 npm 全局目录
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
# 添加到 PATH添加到 ~/.bashrc 或 ~/.zshrc
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# 重新安装 Wrangler
npm install -g wrangler
```
#### Windows 执行策略问题
```powershell
# 如果遇到执行策略错误
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
## 📦 获取项目代码
### 方法一Git 克隆(推荐)
```bash
# 克隆项目
git clone https://github.com/qinfuyao/AI-Proxy-Worker.git
# 进入项目目录
cd ai-proxy-worker
# 查看项目结构
ls -la
```
### 方法二:下载 ZIP
1. 访问项目 GitHub 页面
2. 点击绿色的 **"Code"** 按钮
3. 选择 **"Download ZIP"**
4. 解压到本地目录
5. 打开终端,进入项目目录
## ✅ 验证安装
运行以下命令验证所有依赖都已正确安装:
```bash
# 检查 Node.js
node --version
# ✅ 应该显示 v18.0.0 或更高版本
# 检查 npm
npm --version
# ✅ 应该显示 9.0.0 或更高版本
# 检查 Git
git --version
# ✅ 应该显示 git version 2.x.x
# 检查 Wrangler
wrangler --version
# ✅ 应该显示 wrangler 3.x.x
# 检查项目文件
ls worker.js wrangler.toml
# ✅ 应该显示这两个文件存在
```
## 🚀 下一步
安装完成后,你可以:
1. **[配置部署环境](./Deployment)** - 设置 Cloudflare 账户和密钥
2. **[快速部署](./Quick-Setup)** - 5分钟部署到生产环境
3. **[本地开发](./Development)** - 设置本地开发环境
## 🔧 故障排除
### 常见安装问题
#### Node.js 版本过低
```bash
# 卸载旧版本
sudo apt remove nodejs npm # Ubuntu
brew uninstall node # macOS
# 按照上述步骤重新安装最新版本
```
#### npm 权限问题
```bash
# Linux/macOS 解决方案
sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/local/lib/node_modules
```
#### Wrangler 安装失败
```bash
# 清理 npm 缓存
npm cache clean --force
# 重新安装
npm install -g wrangler
```
#### 网络问题(中国用户)
```bash
# 使用淘宝镜像
npm config set registry https://registry.npmmirror.com/
# 重新安装
npm install -g wrangler
```
### 获取帮助
如果遇到安装问题:
1. 查看 [故障排除指南](./Troubleshooting)
2. 搜索 [GitHub Issues](../../issues)
3. 在 [Discussions](../../discussions) 中提问
4. 查看 [Cloudflare Workers 文档](https://developers.cloudflare.com/workers/)
---
**安装完成?** 👉 [开始部署](./Deployment)

398
docs/Monitoring.en.md Normal file
View File

@@ -0,0 +1,398 @@
# Monitoring Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Monitoring.en.md) | [🇨🇳 中文](./Monitoring.md)
</div>
Learn how to monitor your AI Proxy Worker deployment, track performance, and troubleshoot issues using Cloudflare's built-in monitoring tools.
## 📊 Cloudflare Dashboard Monitoring
### Worker Analytics
Access real-time metrics in your Cloudflare dashboard:
1. **Navigate to Workers & Pages**
2. **Select your AI Proxy Worker**
3. **View Analytics tab**
### Key Metrics to Monitor
#### Request Metrics
- **Requests per second** - Traffic volume
- **Success rate** - Percentage of successful requests
- **Error rate** - Failed requests requiring attention
- **Response time** - Average latency
#### Resource Usage
- **CPU usage** - Worker execution time
- **Memory usage** - Memory consumption per request
- **Duration** - Request processing time
#### Error Analysis
- **4xx errors** - Client-side issues (authentication, validation)
- **5xx errors** - Server-side problems (upstream API issues)
- **Timeout errors** - Requests exceeding time limits
## 🔍 Log Analysis
### Accessing Logs
```bash
# View real-time logs
wrangler tail
# Filter by specific log level
wrangler tail --format json | jq 'select(.level == "error")'
# Save logs to file
wrangler tail --format json > worker-logs.json
```
### Log Levels and Meanings
#### INFO Level
```javascript
console.log('Request received:', {
method: request.method,
url: request.url,
timestamp: new Date().toISOString()
});
```
#### WARN Level
```javascript
console.warn('Rate limit approaching:', {
clientId: 'user123',
requestCount: 95,
limit: 100
});
```
#### ERROR Level
```javascript
console.error('API request failed:', {
error: error.message,
statusCode: response.status,
timestamp: new Date().toISOString()
});
```
## 📈 Performance Monitoring
### Response Time Tracking
Monitor these key performance indicators:
```javascript
// Custom timing logs
const start = Date.now();
const response = await fetch(upstreamAPI);
const duration = Date.now() - start;
console.log('Upstream API timing:', {
duration: duration,
endpoint: 'deepseek-api',
status: response.status
});
```
### Recommended Response Time Targets
- **Chat requests**: < 2 seconds
- **Streaming responses**: First token < 1 second
- **Health checks**: < 500ms
### Performance Optimization Monitoring
Track these metrics to identify optimization opportunities:
1. **Cold start frequency** - Worker initialization time
2. **Memory usage patterns** - Identify memory leaks
3. **CPU utilization** - Optimize heavy computations
4. **Network latency** - Upstream API response times
## 🚨 Alert Configuration
### Cloudflare Alerts
Set up alerts for critical issues:
#### Error Rate Alert
```yaml
Alert Type: Worker Error Rate
Threshold: > 5% error rate
Time Period: 5 minutes
Notification: Email, Webhook
```
#### Response Time Alert
```yaml
Alert Type: Worker Response Time
Threshold: > 3 seconds average
Time Period: 5 minutes
Notification: Email, Slack
```
#### Request Volume Alert
```yaml
Alert Type: Request Volume
Threshold: > 1000 requests/minute
Time Period: 1 minute
Notification: Email
```
### Custom Alert Implementation
```javascript
// In your worker code
const ALERT_THRESHOLDS = {
ERROR_RATE: 0.05, // 5%
RESPONSE_TIME: 3000, // 3 seconds
REQUEST_RATE: 1000 // 1000/minute
};
async function checkAlerts(metrics) {
if (metrics.errorRate > ALERT_THRESHOLDS.ERROR_RATE) {
await sendAlert('High error rate detected', metrics);
}
}
```
## 📋 Health Checks
### Endpoint Monitoring
Create a health check endpoint:
```javascript
// Add to your worker
if (url.pathname === '/health') {
const healthStatus = await checkSystemHealth();
return new Response(JSON.stringify(healthStatus), {
headers: { 'Content-Type': 'application/json' }
});
}
async function checkSystemHealth() {
return {
status: 'healthy',
timestamp: new Date().toISOString(),
version: '1.0.0',
upstreamAPIs: {
deepseek: await checkDeepSeekAPI()
}
};
}
```
### External Monitoring Services
Integrate with external monitoring tools:
#### Uptime Robot
```bash
# Monitor endpoint
https://your-worker.workers.dev/health
# Check every 5 minutes
# Alert on 3 consecutive failures
```
#### Pingdom
```bash
# HTTP check configuration
URL: https://your-worker.workers.dev/health
Interval: 1 minute
Timeout: 30 seconds
```
## 🔧 Debugging Tools
### Debug Mode
Enable detailed logging for troubleshooting:
```javascript
const DEBUG = env.DEBUG_MODE === 'true';
if (DEBUG) {
console.log('Debug: Request details:', {
headers: Object.fromEntries(request.headers),
body: await request.clone().text(),
timestamp: new Date().toISOString()
});
}
```
### Request Tracing
Track requests through the system:
```javascript
function generateTraceId() {
return Math.random().toString(36).substring(2, 15);
}
const traceId = generateTraceId();
console.log('Request started:', { traceId, url: request.url });
// Pass traceId through all function calls
const result = await processRequest(request, { traceId });
console.log('Request completed:', { traceId, duration });
```
## 📊 Custom Metrics
### Business Metrics
Track application-specific metrics:
```javascript
// Track model usage
const modelUsage = {
'deepseek-chat': 0,
'deepseek-reasoner': 0
};
// Track user activity
const userMetrics = {
activeUsers: new Set(),
totalRequests: 0,
streamingRequests: 0
};
// Log metrics periodically
setInterval(() => {
console.log('Business metrics:', {
modelUsage,
userMetrics: {
activeUsers: userMetrics.activeUsers.size,
totalRequests: userMetrics.totalRequests,
streamingRequests: userMetrics.streamingRequests
}
});
}, 60000); // Every minute
```
### Cost Tracking
Monitor usage costs:
```javascript
// Track request costs
const costTracking = {
totalRequests: 0,
cpuTime: 0,
bandwidthUsed: 0
};
// Calculate estimated costs
function calculateCosts(metrics) {
const workerCost = metrics.totalRequests * 0.0000005; // $0.50 per million
const cpuCost = metrics.cpuTime * 0.000002; // $2 per million CPU seconds
return {
workerCost,
cpuCost,
totalCost: workerCost + cpuCost
};
}
```
## 🔍 Log Analysis Best Practices
### Structured Logging
Use consistent log formats:
```javascript
function logEvent(level, event, data) {
const logEntry = {
level,
event,
timestamp: new Date().toISOString(),
workerId: env.WORKER_ID || 'unknown',
...data
};
console[level](JSON.stringify(logEntry));
}
// Usage
logEvent('info', 'request_received', { method, url });
logEvent('error', 'api_error', { error: err.message, statusCode });
```
### Log Retention
Understand Cloudflare's log retention:
- **Real-time logs**: Available during development
- **Analytics data**: Retained for 30 days
- **Custom logging**: Use external services for long-term storage
### External Log Aggregation
Send logs to external services:
```javascript
async function sendToLogService(logData) {
if (env.LOG_SERVICE_URL) {
await fetch(env.LOG_SERVICE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(logData)
});
}
}
```
## 📱 Monitoring Dashboard
### Creating Custom Dashboards
Use tools like Grafana or Datadog:
```javascript
// Send metrics to external service
async function sendMetrics(metrics) {
if (env.METRICS_ENDPOINT) {
await fetch(env.METRICS_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.METRICS_API_KEY}`
},
body: JSON.stringify({
service: 'ai-proxy-worker',
timestamp: Date.now(),
metrics
})
});
}
}
```
### Key Dashboard Widgets
1. **Request Volume** - Line chart showing requests over time
2. **Error Rate** - Percentage gauge with threshold alerts
3. **Response Time** - Histogram showing latency distribution
4. **Model Usage** - Pie chart showing model usage breakdown
5. **Geographic Distribution** - Map showing request origins
## 🚨 Incident Response
### Incident Detection
Automated monitoring should detect:
- High error rates (>5%)
- Slow response times (>3s average)
- Service unavailability
- Unusual traffic patterns
### Response Procedures
1. **Immediate**: Check Cloudflare status page
2. **Investigate**: Review recent deployments and logs
3. **Mitigate**: Roll back if necessary
4. **Communicate**: Update status page and notify users
5. **Resolve**: Fix root cause
6. **Post-mortem**: Document lessons learned
### Emergency Contacts
Maintain an escalation list:
- Primary: On-call engineer
- Secondary: Team lead
- Escalation: Infrastructure team
---
**Effective monitoring ensures reliable service** 📊
Regular monitoring helps you maintain high availability and quickly resolve issues.

398
docs/Monitoring.md Normal file
View File

@@ -0,0 +1,398 @@
# 监控指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Monitoring.en.md) | [🇨🇳 中文](./Monitoring.md)
</div>
学习如何监控你的 AI Proxy Worker 部署,跟踪性能,并使用 Cloudflare 内置监控工具排查问题。
## 📊 Cloudflare 控制台监控
### Worker 分析
在 Cloudflare 控制台中访问实时指标:
1. **导航到 Workers & Pages**
2. **选择你的 AI Proxy Worker**
3. **查看分析标签**
### 需要监控的关键指标
#### 请求指标
- **每秒请求数** - 流量大小
- **成功率** - 成功请求的百分比
- **错误率** - 需要关注的失败请求
- **响应时间** - 平均延迟
#### 资源使用
- **CPU 使用率** - Worker 执行时间
- **内存使用** - 每个请求的内存消耗
- **持续时间** - 请求处理时间
#### 错误分析
- **4xx 错误** - 客户端问题(认证、验证)
- **5xx 错误** - 服务器端问题(上游 API 问题)
- **超时错误** - 超过时间限制的请求
## 🔍 日志分析
### 访问日志
```bash
# 查看实时日志
wrangler tail
# 按特定日志级别过滤
wrangler tail --format json | jq 'select(.level == "error")'
# 保存日志到文件
wrangler tail --format json > worker-logs.json
```
### 日志级别和含义
#### INFO 级别
```javascript
console.log('收到请求:', {
method: request.method,
url: request.url,
timestamp: new Date().toISOString()
});
```
#### WARN 级别
```javascript
console.warn('接近速率限制:', {
clientId: 'user123',
requestCount: 95,
limit: 100
});
```
#### ERROR 级别
```javascript
console.error('API 请求失败:', {
error: error.message,
statusCode: response.status,
timestamp: new Date().toISOString()
});
```
## 📈 性能监控
### 响应时间跟踪
监控这些关键性能指标:
```javascript
// 自定义计时日志
const start = Date.now();
const response = await fetch(upstreamAPI);
const duration = Date.now() - start;
console.log('上游 API 计时:', {
duration: duration,
endpoint: 'deepseek-api',
status: response.status
});
```
### 推荐的响应时间目标
- **聊天请求**: < 2
- **流式响应**: 首个令牌 < 1
- **健康检查**: < 500ms
### 性能优化监控
跟踪这些指标以识别优化机会
1. **冷启动频率** - Worker 初始化时间
2. **内存使用模式** - 识别内存泄漏
3. **CPU 利用率** - 优化重计算
4. **网络延迟** - 上游 API 响应时间
## 🚨 告警配置
### Cloudflare 告警
为关键问题设置告警
#### 错误率告警
```yaml
告警类型: Worker 错误率
阈值: > 5% 错误率
时间段: 5 分钟
通知方式: 邮件, Webhook
```
#### 响应时间告警
```yaml
告警类型: Worker 响应时间
阈值: > 3 秒平均值
时间段: 5 分钟
通知方式: 邮件, Slack
```
#### 请求量告警
```yaml
告警类型: 请求量
阈值: > 1000 请求/分钟
时间段: 1 分钟
通知方式: 邮件
```
### 自定义告警实现
```javascript
// 在你的 worker 代码中
const ALERT_THRESHOLDS = {
ERROR_RATE: 0.05, // 5%
RESPONSE_TIME: 3000, // 3 秒
REQUEST_RATE: 1000 // 1000/分钟
};
async function checkAlerts(metrics) {
if (metrics.errorRate > ALERT_THRESHOLDS.ERROR_RATE) {
await sendAlert('检测到高错误率', metrics);
}
}
```
## 📋 健康检查
### 端点监控
创建健康检查端点
```javascript
// 添加到你的 worker
if (url.pathname === '/health') {
const healthStatus = await checkSystemHealth();
return new Response(JSON.stringify(healthStatus), {
headers: { 'Content-Type': 'application/json' }
});
}
async function checkSystemHealth() {
return {
status: 'healthy',
timestamp: new Date().toISOString(),
version: '1.0.0',
upstreamAPIs: {
deepseek: await checkDeepSeekAPI()
}
};
}
```
### 外部监控服务
与外部监控工具集成
#### Uptime Robot
```bash
# 监控端点
https://your-worker.workers.dev/health
# 每 5 分钟检查一次
# 连续 3 次失败时告警
```
#### Pingdom
```bash
# HTTP 检查配置
URL: https://your-worker.workers.dev/health
间隔: 1 分钟
超时: 30
```
## 🔧 调试工具
### 调试模式
启用详细日志进行故障排除
```javascript
const DEBUG = env.DEBUG_MODE === 'true';
if (DEBUG) {
console.log('调试: 请求详情:', {
headers: Object.fromEntries(request.headers),
body: await request.clone().text(),
timestamp: new Date().toISOString()
});
}
```
### 请求追踪
跟踪请求在系统中的流转
```javascript
function generateTraceId() {
return Math.random().toString(36).substring(2, 15);
}
const traceId = generateTraceId();
console.log('请求开始:', { traceId, url: request.url });
// 在所有函数调用中传递 traceId
const result = await processRequest(request, { traceId });
console.log('请求完成:', { traceId, duration });
```
## 📊 自定义指标
### 业务指标
跟踪应用程序特定指标
```javascript
// 跟踪模型使用情况
const modelUsage = {
'deepseek-chat': 0,
'deepseek-reasoner': 0
};
// 跟踪用户活动
const userMetrics = {
activeUsers: new Set(),
totalRequests: 0,
streamingRequests: 0
};
// 定期记录指标
setInterval(() => {
console.log('业务指标:', {
modelUsage,
userMetrics: {
activeUsers: userMetrics.activeUsers.size,
totalRequests: userMetrics.totalRequests,
streamingRequests: userMetrics.streamingRequests
}
});
}, 60000); // 每分钟
```
### 成本跟踪
监控使用成本
```javascript
// 跟踪请求成本
const costTracking = {
totalRequests: 0,
cpuTime: 0,
bandwidthUsed: 0
};
// 计算预估成本
function calculateCosts(metrics) {
const workerCost = metrics.totalRequests * 0.0000005; // 每百万 $0.50
const cpuCost = metrics.cpuTime * 0.000002; // 每百万 CPU 秒 $2
return {
workerCost,
cpuCost,
totalCost: workerCost + cpuCost
};
}
```
## 🔍 日志分析最佳实践
### 结构化日志
使用一致的日志格式
```javascript
function logEvent(level, event, data) {
const logEntry = {
level,
event,
timestamp: new Date().toISOString(),
workerId: env.WORKER_ID || 'unknown',
...data
};
console[level](JSON.stringify(logEntry));
}
// 使用方法
logEvent('info', 'request_received', { method, url });
logEvent('error', 'api_error', { error: err.message, statusCode });
```
### 日志保留
了解 Cloudflare 的日志保留策略
- **实时日志**: 开发期间可用
- **分析数据**: 保留 30
- **自定义日志**: 使用外部服务进行长期存储
### 外部日志聚合
发送日志到外部服务
```javascript
async function sendToLogService(logData) {
if (env.LOG_SERVICE_URL) {
await fetch(env.LOG_SERVICE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(logData)
});
}
}
```
## 📱 监控仪表板
### 创建自定义仪表板
使用 Grafana Datadog 等工具
```javascript
// 发送指标到外部服务
async function sendMetrics(metrics) {
if (env.METRICS_ENDPOINT) {
await fetch(env.METRICS_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.METRICS_API_KEY}`
},
body: JSON.stringify({
service: 'ai-proxy-worker',
timestamp: Date.now(),
metrics
})
});
}
}
```
### 关键仪表板组件
1. **请求量** - 显示随时间变化的请求的折线图
2. **错误率** - 带有阈值告警的百分比仪表
3. **响应时间** - 显示延迟分布的直方图
4. **模型使用** - 显示模型使用情况的饼图
5. **地理分布** - 显示请求来源的地图
## 🚨 事故响应
### 事故检测
自动化监控应检测
- 高错误率 (>5%)
- 慢响应时间 (>3s 平均)
- 服务不可用
- 异常流量模式
### 响应程序
1. **立即**: 检查 Cloudflare 状态页面
2. **调查**: 查看最近的部署和日志
3. **缓解**: 必要时回滚
4. **沟通**: 更新状态页面并通知用户
5. **解决**: 修复根本原因
6. **事后分析**: 记录经验教训
### 紧急联系人
维护升级列表:
- 主要: 值班工程师
- 次要: 团队负责人
- 升级: 基础设施团队
---
**有效的监控确保可靠的服务** 📊
定期监控帮助你维护高可用性并快速解决问题。

514
docs/Security.en.md Normal file
View File

@@ -0,0 +1,514 @@
# Security Configuration
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Security.en.md) | [🇨🇳 中文](./Security.md)
</div>
Comprehensive security guide for deploying AI Proxy Worker in production environments. Follow these best practices to ensure your deployment is secure and protected against common threats.
## 🔐 Authentication & Authorization
### API Key Security
Protect your AI service API keys:
```javascript
// ✅ Good: Store in Cloudflare secrets
wrangler secret put DEEPSEEK_API_KEY
// ❌ Bad: Never hardcode in source code
const API_KEY = "sk-1234567890abcdef"; // NEVER DO THIS
```
### Proxy Key Configuration
Set up secure proxy access:
```javascript
// Strong proxy key generation
const PROXY_KEY = crypto.randomUUID() + crypto.randomUUID();
// Set as Cloudflare secret
wrangler secret put PROXY_KEY
```
### Multi-tier Authentication
Implement layered security:
```javascript
async function authenticateRequest(request, env) {
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return { valid: false, error: 'Missing or invalid authorization header' };
}
const token = authHeader.substring(7);
// Verify proxy key
if (token !== env.PROXY_KEY) {
return { valid: false, error: 'Invalid proxy key' };
}
return { valid: true };
}
```
## 🛡️ Input Validation & Sanitization
### Request Validation
Validate all incoming requests:
```javascript
function validateChatRequest(data) {
const errors = [];
// Required fields
if (!data.messages || !Array.isArray(data.messages)) {
errors.push('messages must be an array');
}
if (!data.model || typeof data.model !== 'string') {
errors.push('model must be a string');
}
// Message validation
data.messages?.forEach((msg, index) => {
if (!msg.role || !['user', 'assistant', 'system'].includes(msg.role)) {
errors.push(`messages[${index}].role must be user, assistant, or system`);
}
if (!msg.content || typeof msg.content !== 'string') {
errors.push(`messages[${index}].content must be a non-empty string`);
}
// Content length limits
if (msg.content.length > 100000) {
errors.push(`messages[${index}].content exceeds maximum length`);
}
});
// Parameter validation
if (data.temperature !== undefined) {
if (typeof data.temperature !== 'number' || data.temperature < 0 || data.temperature > 2) {
errors.push('temperature must be a number between 0 and 2');
}
}
return {
valid: errors.length === 0,
errors
};
}
```
### Content Filtering
Implement content safety checks:
```javascript
function containsUnsafeContent(text) {
const unsafePatterns = [
/\b(password|secret|key|token)\s*[:=]\s*\w+/i,
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/, // Credit card patterns
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/ // Email patterns (if needed)
];
return unsafePatterns.some(pattern => pattern.test(text));
}
// Usage in request handler
if (containsUnsafeContent(message.content)) {
return new Response(JSON.stringify({
error: 'content_rejected',
message: 'Request contains potentially unsafe content'
}), { status: 400 });
}
```
## 🚫 Rate Limiting & DDoS Protection
### Request Rate Limiting
Implement rate limiting to prevent abuse:
```javascript
class RateLimiter {
constructor(maxRequests = 100, windowMs = 60000) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.requests = new Map();
}
isAllowed(clientId) {
const now = Date.now();
const windowStart = now - this.windowMs;
// Clean old entries
for (const [id, timestamps] of this.requests.entries()) {
const validTimestamps = timestamps.filter(ts => ts > windowStart);
if (validTimestamps.length === 0) {
this.requests.delete(id);
} else {
this.requests.set(id, validTimestamps);
}
}
// Check current client
const clientRequests = this.requests.get(clientId) || [];
const recentRequests = clientRequests.filter(ts => ts > windowStart);
if (recentRequests.length >= this.maxRequests) {
return false;
}
// Add current request
recentRequests.push(now);
this.requests.set(clientId, recentRequests);
return true;
}
getRemainingRequests(clientId) {
const clientRequests = this.requests.get(clientId) || [];
const windowStart = Date.now() - this.windowMs;
const recentRequests = clientRequests.filter(ts => ts > windowStart);
return Math.max(0, this.maxRequests - recentRequests.length);
}
}
// Usage
const rateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
async function handleRequest(request, env) {
const clientId = getClientId(request);
if (!rateLimiter.isAllowed(clientId)) {
return new Response(JSON.stringify({
error: 'rate_limit_exceeded',
message: 'Too many requests. Please try again later.',
retryAfter: 60
}), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': '60'
}
});
}
// Process request...
}
```
### IP-based Protection
Implement IP-based security measures:
```javascript
function getClientIP(request) {
return request.headers.get('CF-Connecting-IP') ||
request.headers.get('X-Forwarded-For') ||
'unknown';
}
const BLOCKED_IPS = new Set([
'192.168.1.100',
'10.0.0.50'
]);
function isBlockedIP(ip) {
return BLOCKED_IPS.has(ip);
}
// Usage in request handler
const clientIP = getClientIP(request);
if (isBlockedIP(clientIP)) {
return new Response('Access denied', { status: 403 });
}
```
## 🔒 Data Protection
### Sensitive Data Handling
Never log sensitive information:
```javascript
function sanitizeForLogging(data) {
const sanitized = { ...data };
// Remove sensitive fields
delete sanitized.api_key;
delete sanitized.authorization;
delete sanitized.password;
// Truncate long content
if (sanitized.messages) {
sanitized.messages = sanitized.messages.map(msg => ({
...msg,
content: msg.content.length > 100 ?
msg.content.substring(0, 100) + '...[truncated]' :
msg.content
}));
}
return sanitized;
}
// Usage
console.log('Request received:', sanitizeForLogging(requestData));
```
### Response Sanitization
Clean responses before sending:
```javascript
function sanitizeResponse(response) {
// Remove internal fields
const sanitized = { ...response };
delete sanitized.internal_id;
delete sanitized.debug_info;
delete sanitized.upstream_headers;
return sanitized;
}
```
## 🌐 CORS Configuration
### Secure CORS Setup
Configure CORS properly:
```javascript
function setCORSHeaders(response, origin) {
const allowedOrigins = [
'https://yourdomain.com',
'https://app.yourdomain.com',
'https://localhost:3000' // Development only
];
if (allowedOrigins.includes(origin)) {
response.headers.set('Access-Control-Allow-Origin', origin);
}
response.headers.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
response.headers.set('Access-Control-Max-Age', '86400');
return response;
}
// Handle preflight requests
if (request.method === 'OPTIONS') {
const origin = request.headers.get('Origin');
const response = new Response(null, { status: 204 });
return setCORSHeaders(response, origin);
}
```
## 📝 Security Headers
### Essential Security Headers
Add security headers to all responses:
```javascript
function addSecurityHeaders(response) {
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-XSS-Protection', '1; mode=block');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Content-Security-Policy', "default-src 'none'");
// Remove server information
response.headers.delete('Server');
return response;
}
```
## 🔍 Security Monitoring
### Security Event Logging
Log security-related events:
```javascript
function logSecurityEvent(event, details) {
console.warn('Security event:', {
event,
timestamp: new Date().toISOString(),
...details
});
// Send to security monitoring service
if (env.SECURITY_WEBHOOK) {
fetch(env.SECURITY_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
service: 'ai-proxy-worker',
event,
timestamp: new Date().toISOString(),
...details
})
}).catch(err => console.error('Failed to send security alert:', err));
}
}
// Usage
logSecurityEvent('authentication_failure', {
ip: getClientIP(request),
userAgent: request.headers.get('User-Agent'),
path: new URL(request.url).pathname
});
```
### Anomaly Detection
Detect unusual patterns:
```javascript
class AnomalyDetector {
constructor() {
this.requestPatterns = new Map();
}
recordRequest(clientId, endpoint) {
const key = `${clientId}:${endpoint}`;
const now = Date.now();
if (!this.requestPatterns.has(key)) {
this.requestPatterns.set(key, []);
}
const requests = this.requestPatterns.get(key);
requests.push(now);
// Keep only recent requests (last hour)
const oneHourAgo = now - 3600000;
const recentRequests = requests.filter(ts => ts > oneHourAgo);
this.requestPatterns.set(key, recentRequests);
// Detect anomalies
if (recentRequests.length > 1000) { // More than 1000 requests per hour
logSecurityEvent('high_frequency_requests', {
clientId,
endpoint,
requestCount: recentRequests.length
});
}
}
}
```
## 🛠️ Environment Configuration
### Production Environment Variables
Secure environment setup:
```bash
# Required secrets
wrangler secret put DEEPSEEK_API_KEY
wrangler secret put PROXY_KEY
# Optional security settings
wrangler secret put SECURITY_WEBHOOK
wrangler secret put RATE_LIMIT_MAX_REQUESTS
wrangler secret put ALLOWED_ORIGINS
```
### Configuration Validation
Validate environment on startup:
```javascript
function validateEnvironment(env) {
const required = ['DEEPSEEK_API_KEY'];
const missing = required.filter(key => !env[key]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
// Validate API key format
if (!env.DEEPSEEK_API_KEY.startsWith('sk-')) {
console.warn('DEEPSEEK_API_KEY may be invalid - should start with sk-');
}
return true;
}
```
## 🚨 Incident Response
### Security Incident Checklist
When a security incident is detected:
1. **Immediate Response**
- [ ] Identify the scope of the incident
- [ ] Block malicious traffic if possible
- [ ] Preserve logs and evidence
2. **Assessment**
- [ ] Determine what data may have been accessed
- [ ] Assess the impact on users and services
- [ ] Check for ongoing threats
3. **Containment**
- [ ] Rotate compromised API keys
- [ ] Update security rules
- [ ] Deploy patches if needed
4. **Recovery**
- [ ] Restore normal operations
- [ ] Monitor for continued threats
- [ ] Validate security measures
5. **Post-Incident**
- [ ] Document lessons learned
- [ ] Update security procedures
- [ ] Notify stakeholders if required
### Emergency Procedures
```javascript
// Emergency shutdown capability
async function emergencyShutdown(reason) {
logSecurityEvent('emergency_shutdown', { reason });
// Return maintenance mode response
return new Response(JSON.stringify({
error: 'service_unavailable',
message: 'Service temporarily unavailable for maintenance',
timestamp: new Date().toISOString()
}), {
status: 503,
headers: {
'Content-Type': 'application/json',
'Retry-After': '3600'
}
});
}
```
## 📋 Security Checklist
### Pre-deployment Security Review
- [ ] All API keys stored as secrets
- [ ] Input validation implemented
- [ ] Rate limiting configured
- [ ] CORS properly configured
- [ ] Security headers added
- [ ] Logging sanitized
- [ ] Error messages don't leak information
- [ ] Dependencies updated
- [ ] Security monitoring enabled
### Regular Security Maintenance
- [ ] Review access logs monthly
- [ ] Update dependencies quarterly
- [ ] Rotate API keys annually
- [ ] Test incident response procedures
- [ ] Review and update security policies
---
**Security is an ongoing process** 🔒
Regular reviews and updates ensure your AI Proxy Worker remains secure against evolving threats.

514
docs/Security.md Normal file
View File

@@ -0,0 +1,514 @@
# 安全配置
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Security.en.md) | [🇨🇳 中文](./Security.md)
</div>
在生产环境中部署 AI Proxy Worker 的综合安全指南。遵循这些最佳实践,确保你的部署安全并防范常见威胁。
## 🔐 认证与授权
### API 密钥安全
保护你的 AI 服务 API 密钥:
```javascript
// ✅ 正确: 存储在 Cloudflare 密钥中
wrangler secret put DEEPSEEK_API_KEY
// ❌ 错误: 永远不要在源代码中硬编码
const API_KEY = "sk-1234567890abcdef"; // 永远不要这样做
```
### 代理密钥配置
设置安全的代理访问:
```javascript
// 强代理密钥生成
const PROXY_KEY = crypto.randomUUID() + crypto.randomUUID();
// 设置为 Cloudflare 密钥
wrangler secret put PROXY_KEY
```
### 多层认证
实施分层安全:
```javascript
async function authenticateRequest(request, env) {
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return { valid: false, error: '缺少或无效的授权头' };
}
const token = authHeader.substring(7);
// 验证代理密钥
if (token !== env.PROXY_KEY) {
return { valid: false, error: '无效的代理密钥' };
}
return { valid: true };
}
```
## 🛡️ 输入验证与清理
### 请求验证
验证所有传入请求:
```javascript
function validateChatRequest(data) {
const errors = [];
// 必需字段
if (!data.messages || !Array.isArray(data.messages)) {
errors.push('messages 必须是数组');
}
if (!data.model || typeof data.model !== 'string') {
errors.push('model 必须是字符串');
}
// 消息验证
data.messages?.forEach((msg, index) => {
if (!msg.role || !['user', 'assistant', 'system'].includes(msg.role)) {
errors.push(`messages[${index}].role 必须是 user、assistant 或 system`);
}
if (!msg.content || typeof msg.content !== 'string') {
errors.push(`messages[${index}].content 必须是非空字符串`);
}
// 内容长度限制
if (msg.content.length > 100000) {
errors.push(`messages[${index}].content 超过最大长度`);
}
});
// 参数验证
if (data.temperature !== undefined) {
if (typeof data.temperature !== 'number' || data.temperature < 0 || data.temperature > 2) {
errors.push('temperature 必须是 0 到 2 之间的数字');
}
}
return {
valid: errors.length === 0,
errors
};
}
```
### 内容过滤
实施内容安全检查:
```javascript
function containsUnsafeContent(text) {
const unsafePatterns = [
/\b(password|secret|key|token|密码|密钥|令牌)\s*[:=]\s*\w+/i,
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/, // 信用卡模式
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/ // 邮箱模式(如需要)
];
return unsafePatterns.some(pattern => pattern.test(text));
}
// 在请求处理器中使用
if (containsUnsafeContent(message.content)) {
return new Response(JSON.stringify({
error: 'content_rejected',
message: '请求包含潜在不安全内容'
}), { status: 400 });
}
```
## 🚫 速率限制与 DDoS 防护
### 请求速率限制
实施速率限制以防止滥用:
```javascript
class RateLimiter {
constructor(maxRequests = 100, windowMs = 60000) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.requests = new Map();
}
isAllowed(clientId) {
const now = Date.now();
const windowStart = now - this.windowMs;
// 清理旧条目
for (const [id, timestamps] of this.requests.entries()) {
const validTimestamps = timestamps.filter(ts => ts > windowStart);
if (validTimestamps.length === 0) {
this.requests.delete(id);
} else {
this.requests.set(id, validTimestamps);
}
}
// 检查当前客户端
const clientRequests = this.requests.get(clientId) || [];
const recentRequests = clientRequests.filter(ts => ts > windowStart);
if (recentRequests.length >= this.maxRequests) {
return false;
}
// 添加当前请求
recentRequests.push(now);
this.requests.set(clientId, recentRequests);
return true;
}
getRemainingRequests(clientId) {
const clientRequests = this.requests.get(clientId) || [];
const windowStart = Date.now() - this.windowMs;
const recentRequests = clientRequests.filter(ts => ts > windowStart);
return Math.max(0, this.maxRequests - recentRequests.length);
}
}
// 使用方法
const rateLimiter = new RateLimiter(100, 60000); // 每分钟 100 个请求
async function handleRequest(request, env) {
const clientId = getClientId(request);
if (!rateLimiter.isAllowed(clientId)) {
return new Response(JSON.stringify({
error: 'rate_limit_exceeded',
message: '请求过多。请稍后再试。',
retryAfter: 60
}), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': '60'
}
});
}
// 处理请求...
}
```
### 基于 IP 的防护
实施基于 IP 的安全措施:
```javascript
function getClientIP(request) {
return request.headers.get('CF-Connecting-IP') ||
request.headers.get('X-Forwarded-For') ||
'unknown';
}
const BLOCKED_IPS = new Set([
'192.168.1.100',
'10.0.0.50'
]);
function isBlockedIP(ip) {
return BLOCKED_IPS.has(ip);
}
// 在请求处理器中使用
const clientIP = getClientIP(request);
if (isBlockedIP(clientIP)) {
return new Response('访问被拒绝', { status: 403 });
}
```
## 🔒 数据保护
### 敏感数据处理
永远不要记录敏感信息:
```javascript
function sanitizeForLogging(data) {
const sanitized = { ...data };
// 移除敏感字段
delete sanitized.api_key;
delete sanitized.authorization;
delete sanitized.password;
// 截断长内容
if (sanitized.messages) {
sanitized.messages = sanitized.messages.map(msg => ({
...msg,
content: msg.content.length > 100 ?
msg.content.substring(0, 100) + '...[已截断]' :
msg.content
}));
}
return sanitized;
}
// 使用方法
console.log('收到请求:', sanitizeForLogging(requestData));
```
### 响应清理
发送前清理响应:
```javascript
function sanitizeResponse(response) {
// 移除内部字段
const sanitized = { ...response };
delete sanitized.internal_id;
delete sanitized.debug_info;
delete sanitized.upstream_headers;
return sanitized;
}
```
## 🌐 CORS 配置
### 安全 CORS 设置
正确配置 CORS
```javascript
function setCORSHeaders(response, origin) {
const allowedOrigins = [
'https://yourdomain.com',
'https://app.yourdomain.com',
'https://localhost:3000' // 仅开发环境
];
if (allowedOrigins.includes(origin)) {
response.headers.set('Access-Control-Allow-Origin', origin);
}
response.headers.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
response.headers.set('Access-Control-Max-Age', '86400');
return response;
}
// 处理预检请求
if (request.method === 'OPTIONS') {
const origin = request.headers.get('Origin');
const response = new Response(null, { status: 204 });
return setCORSHeaders(response, origin);
}
```
## 📝 安全头
### 必要的安全头
为所有响应添加安全头:
```javascript
function addSecurityHeaders(response) {
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-XSS-Protection', '1; mode=block');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Content-Security-Policy', "default-src 'none'");
// 移除服务器信息
response.headers.delete('Server');
return response;
}
```
## 🔍 安全监控
### 安全事件日志
记录安全相关事件:
```javascript
function logSecurityEvent(event, details) {
console.warn('安全事件:', {
event,
timestamp: new Date().toISOString(),
...details
});
// 发送到安全监控服务
if (env.SECURITY_WEBHOOK) {
fetch(env.SECURITY_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
service: 'ai-proxy-worker',
event,
timestamp: new Date().toISOString(),
...details
})
}).catch(err => console.error('发送安全告警失败:', err));
}
}
// 使用方法
logSecurityEvent('authentication_failure', {
ip: getClientIP(request),
userAgent: request.headers.get('User-Agent'),
path: new URL(request.url).pathname
});
```
### 异常检测
检测异常模式:
```javascript
class AnomalyDetector {
constructor() {
this.requestPatterns = new Map();
}
recordRequest(clientId, endpoint) {
const key = `${clientId}:${endpoint}`;
const now = Date.now();
if (!this.requestPatterns.has(key)) {
this.requestPatterns.set(key, []);
}
const requests = this.requestPatterns.get(key);
requests.push(now);
// 只保留最近的请求(最后一小时)
const oneHourAgo = now - 3600000;
const recentRequests = requests.filter(ts => ts > oneHourAgo);
this.requestPatterns.set(key, recentRequests);
// 检测异常
if (recentRequests.length > 1000) { // 每小时超过 1000 个请求
logSecurityEvent('high_frequency_requests', {
clientId,
endpoint,
requestCount: recentRequests.length
});
}
}
}
```
## 🛠️ 环境配置
### 生产环境变量
安全环境设置:
```bash
# 必需的密钥
wrangler secret put DEEPSEEK_API_KEY
wrangler secret put PROXY_KEY
# 可选的安全设置
wrangler secret put SECURITY_WEBHOOK
wrangler secret put RATE_LIMIT_MAX_REQUESTS
wrangler secret put ALLOWED_ORIGINS
```
### 配置验证
启动时验证环境:
```javascript
function validateEnvironment(env) {
const required = ['DEEPSEEK_API_KEY'];
const missing = required.filter(key => !env[key]);
if (missing.length > 0) {
throw new Error(`缺少必需的环境变量: ${missing.join(', ')}`);
}
// 验证 API 密钥格式
if (!env.DEEPSEEK_API_KEY.startsWith('sk-')) {
console.warn('DEEPSEEK_API_KEY 可能无效 - 应以 sk- 开头');
}
return true;
}
```
## 🚨 事故响应
### 安全事故清单
检测到安全事故时:
1. **立即响应**
- [ ] 识别事故范围
- [ ] 如可能阻止恶意流量
- [ ] 保存日志和证据
2. **评估**
- [ ] 确定可能被访问的数据
- [ ] 评估对用户和服务的影响
- [ ] 检查持续威胁
3. **控制**
- [ ] 轮换受损的 API 密钥
- [ ] 更新安全规则
- [ ] 如需要部署补丁
4. **恢复**
- [ ] 恢复正常操作
- [ ] 监控持续威胁
- [ ] 验证安全措施
5. **事后处理**
- [ ] 记录经验教训
- [ ] 更新安全程序
- [ ] 如需要通知利益相关者
### 紧急程序
```javascript
// 紧急关闭能力
async function emergencyShutdown(reason) {
logSecurityEvent('emergency_shutdown', { reason });
// 返回维护模式响应
return new Response(JSON.stringify({
error: 'service_unavailable',
message: '服务因维护暂时不可用',
timestamp: new Date().toISOString()
}), {
status: 503,
headers: {
'Content-Type': 'application/json',
'Retry-After': '3600'
}
});
}
```
## 📋 安全清单
### 部署前安全审查
- [ ] 所有 API 密钥存储为密钥
- [ ] 实施输入验证
- [ ] 配置速率限制
- [ ] 正确配置 CORS
- [ ] 添加安全头
- [ ] 清理日志
- [ ] 错误消息不泄露信息
- [ ] 更新依赖项
- [ ] 启用安全监控
### 定期安全维护
- [ ] 每月审查访问日志
- [ ] 每季度更新依赖项
- [ ] 每年轮换 API 密钥
- [ ] 测试事故响应程序
- [ ] 审查和更新安全策略
---
**安全是一个持续的过程** 🔒
定期审查和更新确保你的 AI Proxy Worker 对不断演变的威胁保持安全。

731
docs/Testing.en.md Normal file
View File

@@ -0,0 +1,731 @@
# Testing Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Testing.en.md) | [🇨🇳 中文](./Testing.md)
</div>
Comprehensive testing guide for AI Proxy Worker. This document covers testing strategies, tools, and best practices for ensuring code quality and reliability.
## 📋 Testing Overview
### Testing Philosophy
- **Test Early, Test Often**: Write tests as you develop features
- **Quality Over Quantity**: Focus on meaningful tests rather than coverage numbers
- **Real-World Scenarios**: Test actual use cases and edge conditions
- **Maintainable Tests**: Write tests that are easy to understand and maintain
### Testing Pyramid
```
/\
/ \ Unit Tests (70%)
/____\ - Fast, isolated, focused
/ \
/________\ Integration Tests (20%)
- API endpoints, data flow
E2E Tests (10%)
- Full user scenarios
```
## 🧪 Testing Setup
### Prerequisites
- Node.js 18+ and npm
- Wrangler CLI for Cloudflare Workers
- Testing framework (Jest recommended)
### Installation
```bash
# Install testing dependencies
npm install --save-dev jest @types/jest
npm install --save-dev @cloudflare/workers-types
# For API testing
npm install --save-dev supertest
npm install --save-dev node-fetch
# For mocking
npm install --save-dev jest-environment-miniflare
```
### Configuration
Create `jest.config.js`:
```javascript
module.exports = {
testEnvironment: 'miniflare',
testEnvironmentOptions: {
scriptPath: './worker.js',
modules: true,
},
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
collectCoverageFrom: [
'worker.js',
'!**/node_modules/**',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
}
```
## 🔧 Unit Testing
### Testing Worker Functions
Test individual functions in isolation:
```javascript
// tests/validation.test.js
import { validateChatRequest } from '../worker.js'
describe('validateChatRequest', () => {
it('should validate correct chat request', () => {
const validRequest = {
messages: [
{ role: 'user', content: 'Hello, AI!' }
],
model: 'deepseek-chat'
}
const result = validateChatRequest(validRequest)
expect(result.valid).toBe(true)
expect(result.errors).toHaveLength(0)
})
it('should reject request without messages', () => {
const invalidRequest = {
model: 'deepseek-chat'
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages must be an array')
})
it('should reject invalid message format', () => {
const invalidRequest = {
messages: [
{ role: 'invalid', content: 'Hello' }
],
model: 'deepseek-chat'
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages[0].role must be user, assistant, or system')
})
it('should reject messages that are too long', () => {
const longContent = 'a'.repeat(100001)
const invalidRequest = {
messages: [
{ role: 'user', content: longContent }
],
model: 'deepseek-chat'
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages[0].content exceeds maximum length')
})
it('should validate optional parameters', () => {
const requestWithParams = {
messages: [{ role: 'user', content: 'Hello' }],
model: 'deepseek-chat',
temperature: 0.7,
max_tokens: 1000
}
const result = validateChatRequest(requestWithParams)
expect(result.valid).toBe(true)
})
it('should reject invalid temperature', () => {
const invalidRequest = {
messages: [{ role: 'user', content: 'Hello' }],
model: 'deepseek-chat',
temperature: 3.0 // Invalid: > 2.0
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('temperature must be a number between 0 and 2')
})
})
```
### Testing Error Handling
Test error scenarios thoroughly:
```javascript
// tests/error-handling.test.js
import { createErrorResponse, ApiError } from '../worker.js'
describe('Error Handling', () => {
describe('createErrorResponse', () => {
it('should create standard error response', () => {
const error = new Error('Test error')
const response = createErrorResponse(error, 400)
expect(response.status).toBe(400)
expect(response.headers.get('Content-Type')).toBe('application/json')
})
it('should include error details in debug mode', () => {
const error = new Error('Test error')
const env = { DEBUG_MODE: 'true' }
const details = { operation: 'test' }
const response = createErrorResponse(error, 400, details, env)
const body = JSON.parse(response.body)
expect(body.details).toEqual(details)
})
it('should not include details in production', () => {
const error = new Error('Test error')
const env = { DEBUG_MODE: 'false' }
const details = { operation: 'test' }
const response = createErrorResponse(error, 400, details, env)
const body = JSON.parse(response.body)
expect(body.details).toBeUndefined()
})
})
describe('ApiError', () => {
it('should create API error with status code', () => {
const apiError = new ApiError('Upstream API failed', 502)
expect(apiError.message).toBe('Upstream API failed')
expect(apiError.statusCode).toBe(502)
expect(apiError.name).toBe('ApiError')
})
})
})
```
### Testing Utilities
Test helper functions:
```javascript
// tests/utils.test.js
import { sanitizeForLogging, getClientIP } from '../worker.js'
describe('Utility Functions', () => {
describe('sanitizeForLogging', () => {
it('should remove sensitive fields', () => {
const data = {
messages: [{ role: 'user', content: 'Hello' }],
api_key: 'sk-secret',
authorization: 'Bearer token'
}
const sanitized = sanitizeForLogging(data)
expect(sanitized.api_key).toBeUndefined()
expect(sanitized.authorization).toBeUndefined()
expect(sanitized.messages).toBeDefined()
})
it('should truncate long content', () => {
const longContent = 'a'.repeat(200)
const data = {
messages: [{ role: 'user', content: longContent }]
}
const sanitized = sanitizeForLogging(data)
expect(sanitized.messages[0].content).toHaveLength(103) // 100 + "..."
expect(sanitized.messages[0].content).toContain('...[truncated]')
})
})
describe('getClientIP', () => {
it('should extract IP from CF-Connecting-IP header', () => {
const request = new Request('https://example.com', {
headers: { 'CF-Connecting-IP': '192.168.1.1' }
})
const ip = getClientIP(request)
expect(ip).toBe('192.168.1.1')
})
it('should fallback to X-Forwarded-For', () => {
const request = new Request('https://example.com', {
headers: { 'X-Forwarded-For': '192.168.1.2' }
})
const ip = getClientIP(request)
expect(ip).toBe('192.168.1.2')
})
it('should return unknown for missing headers', () => {
const request = new Request('https://example.com')
const ip = getClientIP(request)
expect(ip).toBe('unknown')
})
})
})
```
## 🌐 Integration Testing
### Testing HTTP Endpoints
Test complete request/response cycles:
```javascript
// tests/integration/chat.test.js
import { unstable_dev } from 'wrangler'
describe('Chat Endpoint Integration', () => {
let worker
beforeAll(async () => {
worker = await unstable_dev('worker.js', {
experimental: { disableExperimentalWarning: true },
env: {
DEEPSEEK_API_KEY: 'test-key',
PROXY_KEY: 'test-proxy-key'
}
})
})
afterAll(async () => {
await worker.stop()
})
it('should handle valid chat request', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'user', content: 'Hello, AI!' }
]
})
})
expect(response.status).toBe(200)
const data = await response.json()
expect(data.choices).toBeDefined()
expect(data.choices[0].message).toBeDefined()
})
it('should reject request without authorization', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello' }]
})
})
expect(response.status).toBe(401)
})
it('should handle malformed JSON', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json'
},
body: 'invalid json'
})
expect(response.status).toBe(400)
const data = await response.json()
expect(data.error).toBe('invalid_json')
})
it('should handle streaming requests', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello' }],
stream: true
})
})
expect(response.status).toBe(200)
expect(response.headers.get('Content-Type')).toContain('text/event-stream')
})
})
```
### Testing Rate Limiting
Test rate limiting functionality:
```javascript
// tests/integration/rate-limiting.test.js
describe('Rate Limiting', () => {
let worker
beforeAll(async () => {
worker = await unstable_dev('worker.js', {
env: {
RATE_LIMIT_MAX_REQUESTS: '5',
RATE_LIMIT_WINDOW_MS: '60000'
}
})
})
afterAll(async () => {
await worker.stop()
})
it('should allow requests within limit', async () => {
const requests = Array(3).fill().map(() =>
worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json',
'CF-Connecting-IP': '192.168.1.100'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello' }]
})
})
)
const responses = await Promise.all(requests)
responses.forEach(response => {
expect(response.status).toBe(200)
})
})
it('should block requests exceeding limit', async () => {
// Make requests up to the limit
const requests = Array(6).fill().map(() =>
worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json',
'CF-Connecting-IP': '192.168.1.101'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello' }]
})
})
)
const responses = await Promise.all(requests)
// First 5 should succeed
responses.slice(0, 5).forEach(response => {
expect(response.status).toBe(200)
})
// 6th should be rate limited
expect(responses[5].status).toBe(429)
})
})
```
## 🎭 Mocking External APIs
### Mock DeepSeek API Responses
Create mocks for external API calls:
```javascript
// tests/mocks/deepseek-api.js
export const mockDeepSeekResponse = {
id: 'chatcmpl-test123',
object: 'chat.completion',
created: Date.now(),
model: 'deepseek-chat',
choices: [{
index: 0,
message: {
role: 'assistant',
content: 'Hello! How can I help you today?'
},
finish_reason: 'stop'
}],
usage: {
prompt_tokens: 10,
completion_tokens: 9,
total_tokens: 19
}
}
export const mockStreamingResponse = [
'data: {"choices":[{"delta":{"content":"Hello"}}]}\n\n',
'data: {"choices":[{"delta":{"content":"! How"}}]}\n\n',
'data: {"choices":[{"delta":{"content":" can I help you?"}}]}\n\n',
'data: [DONE]\n\n'
]
// Mock fetch for testing
export function mockFetch(url, options) {
if (url.includes('api.deepseek.com')) {
if (options.headers.Accept?.includes('text/event-stream')) {
return Promise.resolve({
ok: true,
status: 200,
headers: new Map([['content-type', 'text/event-stream']]),
body: {
getReader: () => ({
read: mockStreamingResponse.shift()
? () => Promise.resolve({
done: false,
value: new TextEncoder().encode(mockStreamingResponse.shift())
})
: () => Promise.resolve({ done: true })
})
}
})
}
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockDeepSeekResponse)
})
}
return Promise.reject(new Error('Unmocked URL'))
}
```
### Using Mocks in Tests
```javascript
// tests/integration/api-mocking.test.js
import { mockFetch, mockDeepSeekResponse } from '../mocks/deepseek-api.js'
describe('API Integration with Mocks', () => {
beforeEach(() => {
global.fetch = jest.fn(mockFetch)
})
afterEach(() => {
jest.restoreAllMocks()
})
it('should handle successful API response', async () => {
const worker = await unstable_dev('worker.js')
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Hello' }]
})
})
expect(response.status).toBe(200)
const data = await response.json()
expect(data.choices[0].message.content).toBe(mockDeepSeekResponse.choices[0].message.content)
await worker.stop()
})
})
```
## 🔍 Performance Testing
### Load Testing
Test performance under load:
```javascript
// tests/performance/load.test.js
describe('Performance Tests', () => {
it('should handle concurrent requests', async () => {
const worker = await unstable_dev('worker.js')
const concurrentRequests = 50
const startTime = Date.now()
const requests = Array(concurrentRequests).fill().map(() =>
worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Performance test' }]
})
})
)
const responses = await Promise.all(requests)
const endTime = Date.now()
// All requests should succeed
responses.forEach(response => {
expect(response.status).toBe(200)
})
// Should complete within reasonable time
const duration = endTime - startTime
expect(duration).toBeLessThan(10000) // 10 seconds
console.log(`${concurrentRequests} concurrent requests completed in ${duration}ms`)
await worker.stop()
})
})
```
## 📊 Test Coverage
### Coverage Configuration
Ensure good test coverage:
```javascript
// jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
collectCoverageFrom: [
'worker.js',
'src/**/*.js',
'!**/node_modules/**',
'!coverage/**',
'!tests/**'
],
coverageThreshold: {
global: {
branches: 80,
functions: 85,
lines: 85,
statements: 85
}
}
}
```
### Running Coverage
```bash
# Generate coverage report
npm run test:coverage
# View HTML coverage report
open coverage/lcov-report/index.html
```
## 🚀 Continuous Integration
### GitHub Actions Workflow
Create `.github/workflows/test.yml`:
```yaml
name: Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm run test:coverage
env:
DEEPSEEK_API_KEY: ${{ secrets.TEST_DEEPSEEK_API_KEY }}
PROXY_KEY: ${{ secrets.TEST_PROXY_KEY }}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
- name: Deploy to test environment
if: github.ref == 'refs/heads/develop'
run: npx wrangler publish --env test
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
## 📝 Testing Best Practices
### Test Structure
- **AAA Pattern**: Arrange, Act, Assert
- **Descriptive Names**: Test names should explain what they test
- **Single Responsibility**: One test should test one thing
- **Independent Tests**: Tests should not depend on each other
### Test Data
- **Use Factories**: Create test data with factory functions
- **Realistic Data**: Use data that resembles production data
- **Edge Cases**: Test boundary conditions and edge cases
### Assertions
- **Specific Assertions**: Use specific matchers for better error messages
- **Multiple Assertions**: Group related assertions in the same test
- **Error Testing**: Test both success and failure scenarios
### Maintenance
- **Keep Tests Updated**: Update tests when code changes
- **Remove Dead Tests**: Delete tests for removed functionality
- **Refactor Tests**: Keep test code clean and maintainable
---
**Good tests are an investment in code quality** 🧪
Comprehensive testing ensures your AI Proxy Worker is reliable, maintainable, and ready for production use.

731
docs/Testing.md Normal file
View File

@@ -0,0 +1,731 @@
# 测试指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Testing.en.md) | [🇨🇳 中文](./Testing.md)
</div>
AI Proxy Worker 的全面测试指南。本文档涵盖测试策略、工具和最佳实践,以确保代码质量和可靠性。
## 📋 测试概述
### 测试理念
- **早测试,常测试**:在开发功能时编写测试
- **质量胜过数量**:专注于有意义的测试而非覆盖率数字
- **真实场景**:测试实际用例和边界条件
- **可维护的测试**:编写易于理解和维护的测试
### 测试金字塔
```
/\
/ \ 单元测试 (70%)
/____\ - 快速、隔离、专注
/ \
/________\ 集成测试 (20%)
- API 端点、数据流
端到端测试 (10%)
- 完整用户场景
```
## 🧪 测试设置
### 前置要求
- Node.js 18+ 和 npm
- Cloudflare Workers 的 Wrangler CLI
- 测试框架(推荐 Jest
### 安装
```bash
# 安装测试依赖
npm install --save-dev jest @types/jest
npm install --save-dev @cloudflare/workers-types
# 用于 API 测试
npm install --save-dev supertest
npm install --save-dev node-fetch
# 用于模拟
npm install --save-dev jest-environment-miniflare
```
### 配置
创建 `jest.config.js`
```javascript
module.exports = {
testEnvironment: 'miniflare',
testEnvironmentOptions: {
scriptPath: './worker.js',
modules: true,
},
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
collectCoverageFrom: [
'worker.js',
'!**/node_modules/**',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
}
```
## 🔧 单元测试
### 测试 Worker 函数
独立测试单个函数:
```javascript
// tests/validation.test.js
import { validateChatRequest } from '../worker.js'
describe('validateChatRequest', () => {
it('应该验证正确的聊天请求', () => {
const validRequest = {
messages: [
{ role: 'user', content: '你好AI' }
],
model: 'deepseek-chat'
}
const result = validateChatRequest(validRequest)
expect(result.valid).toBe(true)
expect(result.errors).toHaveLength(0)
})
it('应该拒绝没有消息的请求', () => {
const invalidRequest = {
model: 'deepseek-chat'
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages 必须是数组')
})
it('应该拒绝无效的消息格式', () => {
const invalidRequest = {
messages: [
{ role: 'invalid', content: '你好' }
],
model: 'deepseek-chat'
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages[0].role 必须是 user、assistant 或 system')
})
it('应该拒绝过长的消息', () => {
const longContent = 'a'.repeat(100001)
const invalidRequest = {
messages: [
{ role: 'user', content: longContent }
],
model: 'deepseek-chat'
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('messages[0].content 超过最大长度')
})
it('应该验证可选参数', () => {
const requestWithParams = {
messages: [{ role: 'user', content: '你好' }],
model: 'deepseek-chat',
temperature: 0.7,
max_tokens: 1000
}
const result = validateChatRequest(requestWithParams)
expect(result.valid).toBe(true)
})
it('应该拒绝无效的温度值', () => {
const invalidRequest = {
messages: [{ role: 'user', content: '你好' }],
model: 'deepseek-chat',
temperature: 3.0 // 无效:> 2.0
}
const result = validateChatRequest(invalidRequest)
expect(result.valid).toBe(false)
expect(result.errors).toContain('temperature 必须是 0 到 2 之间的数字')
})
})
```
### 测试错误处理
彻底测试错误场景:
```javascript
// tests/error-handling.test.js
import { createErrorResponse, ApiError } from '../worker.js'
describe('错误处理', () => {
describe('createErrorResponse', () => {
it('应该创建标准错误响应', () => {
const error = new Error('测试错误')
const response = createErrorResponse(error, 400)
expect(response.status).toBe(400)
expect(response.headers.get('Content-Type')).toBe('application/json')
})
it('应该在调试模式下包含错误详情', () => {
const error = new Error('测试错误')
const env = { DEBUG_MODE: 'true' }
const details = { operation: 'test' }
const response = createErrorResponse(error, 400, details, env)
const body = JSON.parse(response.body)
expect(body.details).toEqual(details)
})
it('在生产环境中不应包含详情', () => {
const error = new Error('测试错误')
const env = { DEBUG_MODE: 'false' }
const details = { operation: 'test' }
const response = createErrorResponse(error, 400, details, env)
const body = JSON.parse(response.body)
expect(body.details).toBeUndefined()
})
})
describe('ApiError', () => {
it('应该创建带状态码的 API 错误', () => {
const apiError = new ApiError('上游 API 失败', 502)
expect(apiError.message).toBe('上游 API 失败')
expect(apiError.statusCode).toBe(502)
expect(apiError.name).toBe('ApiError')
})
})
})
```
### 测试工具函数
测试辅助函数:
```javascript
// tests/utils.test.js
import { sanitizeForLogging, getClientIP } from '../worker.js'
describe('工具函数', () => {
describe('sanitizeForLogging', () => {
it('应该移除敏感字段', () => {
const data = {
messages: [{ role: 'user', content: '你好' }],
api_key: 'sk-secret',
authorization: 'Bearer token'
}
const sanitized = sanitizeForLogging(data)
expect(sanitized.api_key).toBeUndefined()
expect(sanitized.authorization).toBeUndefined()
expect(sanitized.messages).toBeDefined()
})
it('应该截断长内容', () => {
const longContent = 'a'.repeat(200)
const data = {
messages: [{ role: 'user', content: longContent }]
}
const sanitized = sanitizeForLogging(data)
expect(sanitized.messages[0].content).toHaveLength(103) // 100 + "..."
expect(sanitized.messages[0].content).toContain('...[已截断]')
})
})
describe('getClientIP', () => {
it('应该从 CF-Connecting-IP 头提取 IP', () => {
const request = new Request('https://example.com', {
headers: { 'CF-Connecting-IP': '192.168.1.1' }
})
const ip = getClientIP(request)
expect(ip).toBe('192.168.1.1')
})
it('应该回退到 X-Forwarded-For', () => {
const request = new Request('https://example.com', {
headers: { 'X-Forwarded-For': '192.168.1.2' }
})
const ip = getClientIP(request)
expect(ip).toBe('192.168.1.2')
})
it('缺少头时应该返回 unknown', () => {
const request = new Request('https://example.com')
const ip = getClientIP(request)
expect(ip).toBe('unknown')
})
})
})
```
## 🌐 集成测试
### 测试 HTTP 端点
测试完整的请求/响应周期:
```javascript
// tests/integration/chat.test.js
import { unstable_dev } from 'wrangler'
describe('聊天端点集成', () => {
let worker
beforeAll(async () => {
worker = await unstable_dev('worker.js', {
experimental: { disableExperimentalWarning: true },
env: {
DEEPSEEK_API_KEY: 'test-key',
PROXY_KEY: 'test-proxy-key'
}
})
})
afterAll(async () => {
await worker.stop()
})
it('应该处理有效的聊天请求', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'user', content: '你好AI' }
]
})
})
expect(response.status).toBe(200)
const data = await response.json()
expect(data.choices).toBeDefined()
expect(data.choices[0].message).toBeDefined()
})
it('应该拒绝没有授权的请求', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: '你好' }]
})
})
expect(response.status).toBe(401)
})
it('应该处理格式错误的 JSON', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json'
},
body: 'invalid json'
})
expect(response.status).toBe(400)
const data = await response.json()
expect(data.error).toBe('invalid_json')
})
it('应该处理流式请求', async () => {
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: '你好' }],
stream: true
})
})
expect(response.status).toBe(200)
expect(response.headers.get('Content-Type')).toContain('text/event-stream')
})
})
```
### 测试速率限制
测试速率限制功能:
```javascript
// tests/integration/rate-limiting.test.js
describe('速率限制', () => {
let worker
beforeAll(async () => {
worker = await unstable_dev('worker.js', {
env: {
RATE_LIMIT_MAX_REQUESTS: '5',
RATE_LIMIT_WINDOW_MS: '60000'
}
})
})
afterAll(async () => {
await worker.stop()
})
it('应该允许限制内的请求', async () => {
const requests = Array(3).fill().map(() =>
worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json',
'CF-Connecting-IP': '192.168.1.100'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: '你好' }]
})
})
)
const responses = await Promise.all(requests)
responses.forEach(response => {
expect(response.status).toBe(200)
})
})
it('应该阻止超过限制的请求', async () => {
// 发送到达限制的请求
const requests = Array(6).fill().map(() =>
worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-proxy-key',
'Content-Type': 'application/json',
'CF-Connecting-IP': '192.168.1.101'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: '你好' }]
})
})
)
const responses = await Promise.all(requests)
// 前 5 个应该成功
responses.slice(0, 5).forEach(response => {
expect(response.status).toBe(200)
})
// 第 6 个应该被速率限制
expect(responses[5].status).toBe(429)
})
})
```
## 🎭 模拟外部 API
### 模拟 DeepSeek API 响应
为外部 API 调用创建模拟:
```javascript
// tests/mocks/deepseek-api.js
export const mockDeepSeekResponse = {
id: 'chatcmpl-test123',
object: 'chat.completion',
created: Date.now(),
model: 'deepseek-chat',
choices: [{
index: 0,
message: {
role: 'assistant',
content: '你好!今天我能为你做些什么?'
},
finish_reason: 'stop'
}],
usage: {
prompt_tokens: 10,
completion_tokens: 9,
total_tokens: 19
}
}
export const mockStreamingResponse = [
'data: {"choices":[{"delta":{"content":"你好"}}]}\n\n',
'data: {"choices":[{"delta":{"content":"!今天"}}]}\n\n',
'data: {"choices":[{"delta":{"content":"我能为你做些什么?"}}]}\n\n',
'data: [DONE]\n\n'
]
// 用于测试的模拟 fetch
export function mockFetch(url, options) {
if (url.includes('api.deepseek.com')) {
if (options.headers.Accept?.includes('text/event-stream')) {
return Promise.resolve({
ok: true,
status: 200,
headers: new Map([['content-type', 'text/event-stream']]),
body: {
getReader: () => ({
read: mockStreamingResponse.shift()
? () => Promise.resolve({
done: false,
value: new TextEncoder().encode(mockStreamingResponse.shift())
})
: () => Promise.resolve({ done: true })
})
}
})
}
return Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve(mockDeepSeekResponse)
})
}
return Promise.reject(new Error('未模拟的 URL'))
}
```
### 在测试中使用模拟
```javascript
// tests/integration/api-mocking.test.js
import { mockFetch, mockDeepSeekResponse } from '../mocks/deepseek-api.js'
describe('使用模拟的 API 集成', () => {
beforeEach(() => {
global.fetch = jest.fn(mockFetch)
})
afterEach(() => {
jest.restoreAllMocks()
})
it('应该处理成功的 API 响应', async () => {
const worker = await unstable_dev('worker.js')
const response = await worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: '你好' }]
})
})
expect(response.status).toBe(200)
const data = await response.json()
expect(data.choices[0].message.content).toBe(mockDeepSeekResponse.choices[0].message.content)
await worker.stop()
})
})
```
## 🔍 性能测试
### 负载测试
在负载下测试性能:
```javascript
// tests/performance/load.test.js
describe('性能测试', () => {
it('应该处理并发请求', async () => {
const worker = await unstable_dev('worker.js')
const concurrentRequests = 50
const startTime = Date.now()
const requests = Array(concurrentRequests).fill().map(() =>
worker.fetch('https://worker.dev/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer test-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: '性能测试' }]
})
})
)
const responses = await Promise.all(requests)
const endTime = Date.now()
// 所有请求都应该成功
responses.forEach(response => {
expect(response.status).toBe(200)
})
// 应该在合理时间内完成
const duration = endTime - startTime
expect(duration).toBeLessThan(10000) // 10 秒
console.log(`${concurrentRequests} 个并发请求在 ${duration}ms 内完成`)
await worker.stop()
})
})
```
## 📊 测试覆盖率
### 覆盖率配置
确保良好的测试覆盖率:
```javascript
// jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
collectCoverageFrom: [
'worker.js',
'src/**/*.js',
'!**/node_modules/**',
'!coverage/**',
'!tests/**'
],
coverageThreshold: {
global: {
branches: 80,
functions: 85,
lines: 85,
statements: 85
}
}
}
```
### 运行覆盖率
```bash
# 生成覆盖率报告
npm run test:coverage
# 查看 HTML 覆盖率报告
open coverage/lcov-report/index.html
```
## 🚀 持续集成
### GitHub Actions 工作流
创建 `.github/workflows/test.yml`
```yaml
name: 测试
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 设置 Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: 安装依赖
run: npm ci
- name: 运行代码检查
run: npm run lint
- name: 运行测试
run: npm run test:coverage
env:
DEEPSEEK_API_KEY: ${{ secrets.TEST_DEEPSEEK_API_KEY }}
PROXY_KEY: ${{ secrets.TEST_PROXY_KEY }}
- name: 上传覆盖率到 Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
- name: 部署到测试环境
if: github.ref == 'refs/heads/develop'
run: npx wrangler publish --env test
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
## 📝 测试最佳实践
### 测试结构
- **AAA 模式**:安排、行动、断言
- **描述性名称**:测试名称应解释它们测试的内容
- **单一职责**:一个测试应该测试一件事
- **独立测试**:测试之间不应该相互依赖
### 测试数据
- **使用工厂**:使用工厂函数创建测试数据
- **真实数据**:使用类似生产数据的数据
- **边界情况**:测试边界条件和边界情况
### 断言
- **具体断言**:使用具体的匹配器获得更好的错误消息
- **多个断言**:在同一个测试中分组相关断言
- **错误测试**:测试成功和失败场景
### 维护
- **保持测试更新**:代码更改时更新测试
- **删除死测试**:删除已删除功能的测试
- **重构测试**:保持测试代码干净和可维护
---
**好的测试是代码质量的投资** 🧪
全面的测试确保你的 AI Proxy Worker 可靠、可维护,并为生产使用做好准备。

473
docs/Troubleshooting.en.md Normal file
View File

@@ -0,0 +1,473 @@
# Troubleshooting Guide
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Troubleshooting.en.md) | [🇨🇳 中文](./Troubleshooting.md)
</div>
This guide covers common issues you might encounter when using AI Proxy Worker and their solutions. If your issue isn't listed here, please submit a new issue report in [GitHub Issues](../../issues).
## 🚨 Common Errors and Solutions
### 1. Authentication Related Errors
#### ❌ 401 Unauthorized
**Error Message:**
```json
{
"error": "unauthorized",
"details": "Invalid or missing authorization",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Possible Causes:**
- `PROXY_KEY` not set or incorrectly set
- Missing `Authorization` field in request headers
- Incorrect `Authorization` format
**Solutions:**
1. **Check environment variable settings:**
```bash
# View set secrets (doesn't show values)
wrangler secret list
# Reset PROXY_KEY
wrangler secret put PROXY_KEY
```
2. **Check request format:**
```bash
# Correct format
curl -H "Authorization: Bearer YOUR_PROXY_KEY" \
https://your-worker.workers.dev/chat
# Wrong format (missing Bearer)
curl -H "Authorization: YOUR_PROXY_KEY" \
https://your-worker.workers.dev/chat
```
3. **Verify key is correct:**
```javascript
// Ensure no spaces before/after key
const proxyKey = "YOUR_PROXY_KEY".trim();
```
#### ❌ Configuration Error
**Error Message:**
```json
{
"error": "configuration_error",
"details": "Service configuration error",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Possible Causes:**
- `DEEPSEEK_API_KEY` not set
- DeepSeek API key invalid or expired
**Solutions:**
1. **Reset DeepSeek API key:**
```bash
wrangler secret put DEEPSEEK_API_KEY
```
2. **Verify DeepSeek key validity:**
```bash
curl -X POST https://api.deepseek.com/chat/completions \
-H "Authorization: Bearer YOUR_DEEPSEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
3. **Check DeepSeek account status:**
- Login to [DeepSeek Platform](https://platform.deepseek.com/)
- Check account balance
- Confirm API key status
### 2. Request Format Errors
#### ❌ 400 Bad Request - Invalid JSON
**Error Message:**
```json
{
"error": "invalid_request",
"details": "Invalid JSON format",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Solutions:**
1. **Validate JSON format:**
```bash
# Use online JSON validator or
echo '{"model":"deepseek-chat","messages":[]}' | python -m json.tool
```
2. **Check special characters:**
```javascript
// Properly escape special characters
const message = "He said: \"Hello, world!\"";
const payload = JSON.stringify({
model: "deepseek-chat",
messages: [{ role: "user", content: message }]
});
```
#### ❌ 400 Bad Request - Missing Required Fields
**Error Message:**
```json
{
"error": "invalid_request",
"details": "Invalid request format. Missing or invalid messages array",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Solution:**
Ensure request contains required fields:
```json
{
"model": "deepseek-chat", // Required
"messages": [ // Required, must be array
{
"role": "user", // Required: user/assistant/system
"content": "Hello" // Required: message content
}
]
}
```
#### ❌ 413 Payload Too Large
**Error Message:**
```json
{
"error": "payload_too_large",
"details": "Request body too large. Maximum size: 1048576 bytes",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Solutions:**
1. **Reduce request content:**
- Shorten conversation history
- Reduce single message length
- Remove unnecessary parameters
2. **Process long text in batches:**
```javascript
function splitLongText(text, maxLength = 8000) {
const chunks = [];
for (let i = 0; i < text.length; i += maxLength) {
chunks.push(text.slice(i, i + maxLength));
}
return chunks;
}
```
3. **Adjust configuration (if you have permissions):**
```javascript
// In worker.js
const CONFIG = {
MAX_BODY_SIZE: 2 * 1024 * 1024, // Increase to 2MB
};
```
### 3. Network and Timeout Errors
#### ❌ 504 Gateway Timeout
**Error Message:**
```json
{
"error": "timeout",
"details": "Request to DeepSeek API timed out",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Solutions:**
1. **Retry request:**
```javascript
async function retryRequest(requestFn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await requestFn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
```
2. **Reduce request complexity:**
- Lower `max_tokens` parameter
- Simplify prompt content
- Use simpler models
3. **Check network connection:**
```bash
# Test connection to DeepSeek API
curl -I https://api.deepseek.com/
# Test connection to Worker
curl -I https://your-worker.workers.dev/
```
#### ❌ 502 Bad Gateway
**Error Message:**
```json
{
"error": "upstream_error",
"details": "Failed to connect to DeepSeek API",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**Solutions:**
1. **Check DeepSeek API status:**
- Visit [DeepSeek Status Page](https://status.deepseek.com/) (if available)
- Check social media for service status updates
2. **Verify API key:**
```bash
curl -X POST https://api.deepseek.com/chat/completions \
-H "Authorization: Bearer YOUR_DEEPSEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
3. **Wait and retry:**
- Wait a few minutes before retrying
- If persistent, might be temporary DeepSeek API outage
### 4. Deployment Related Issues
#### ❌ Wrangler Login Failed
**Error Message:**
```
Error: Failed to login. Please try again.
```
**Solutions:**
1. **Clear login state and re-login:**
```bash
wrangler logout
wrangler login
```
2. **Manual login (if browser won't open):**
```bash
wrangler login --browser=false
# Copy displayed URL to browser
```
3. **Check network proxy:**
```bash
# If using proxy
export https_proxy=http://proxy.example.com:8080
wrangler login
```
#### ❌ Deployment Failed
**Error Message:**
```
Error: Failed to publish your Worker
```
**Solutions:**
1. **Check wrangler.toml configuration:**
```toml
name = "ai-proxy-worker" # Ensure valid name
main = "worker.js" # Ensure file exists
compatibility_date = "2025-08-17" # Use valid date
```
2. **Verify code syntax:**
```bash
# Check JavaScript syntax
node -c worker.js
```
3. **Check account limits:**
- Free accounts have Worker quantity limits
- Check quota usage in Cloudflare Dashboard
## 🔍 Debugging Tools and Tips
### 1. View Worker Logs
**Real-time logs:**
```bash
# View real-time logs
wrangler tail
# Filter specific log levels
wrangler tail --format=pretty
```
**Cloudflare Dashboard logs:**
1. Login to Cloudflare Dashboard
2. Go to Workers & Pages
3. Select your Worker
4. Click "Logs" tab
### 2. Local Debugging
**Local development server:**
```bash
# Start local development environment
wrangler dev
# Specify port
wrangler dev --port 8787
```
**Add debug logs:**
```javascript
// Add debug info in worker.js
console.log('Request received:', {
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers)
});
```
### 3. Health Check Script
Create a simple health check script:
```bash
#!/bin/bash
# health-check.sh
WORKER_URL="https://your-worker.workers.dev"
PROXY_KEY="YOUR_PROXY_KEY"
echo "🔍 Starting health check..."
# 1. Basic health check
echo "1. Checking service status..."
curl -s "$WORKER_URL/" | jq .
# 2. Authentication test
echo "2. Testing authentication..."
curl -s -X POST "$WORKER_URL/chat" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[]}' | jq .
# 3. API call test
echo "3. Testing API call..."
curl -s -X POST "$WORKER_URL/chat" \
-H "Authorization: Bearer $PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "test"}]
}' | jq .
echo "✅ Health check complete"
```
## 📊 Monitoring and Alerting
### 1. Cloudflare Analytics
View in Cloudflare Dashboard:
- Request count and response time
- Error rate statistics
- Traffic distribution
### 2. Custom Monitoring
```javascript
// Add monitoring metrics in worker.js
const startTime = Date.now();
// Process request...
const duration = Date.now() - startTime;
console.log(`Request completed in ${duration}ms`, {
method: request.method,
status: response.status,
duration,
timestamp: new Date().toISOString()
});
```
### 3. External Monitoring Services
You can use the following services to monitor Worker:
- Uptime Robot
- Pingdom
- StatusCake
Monitoring configuration example:
```
URL: https://your-worker.workers.dev/
Method: GET
Expected Response: {"status":"ok"}
Check Interval: 5 minutes
```
## 🆘 Getting Help
If the above solutions don't resolve your issue:
### 1. Search Existing Issues
- Check [GitHub Issues](../../issues)
- Search [GitHub Discussions](../../discussions)
- Check [Cloudflare Community](https://community.cloudflare.com/)
### 2. Submit New Issue
When submitting issues on GitHub, please include:
**Basic Information:**
- Operating system and version
- Node.js and Wrangler versions
- Complete error message
**Reproduction Steps:**
- Detailed operation steps
- Commands or code used
- Expected vs actual results
**Log Information:**
```bash
# Get detailed logs
wrangler tail --format=pretty > logs.txt
```
**Example Request:**
```bash
# Provide failing curl command example
curl -v -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[...]}'
```
### 3. Community Resources
- [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)
- [DeepSeek API Documentation](https://platform.deepseek.com/api-docs)
- [GitHub Discussions](../../discussions) - Community discussion
- Discord/Telegram groups (if available)
---
**Issue Resolved?** 👉 [Back to Home](./Home.en) | [View More Examples](./Examples.en)

571
docs/Troubleshooting.md Normal file
View File

@@ -0,0 +1,571 @@
# 故障排除指南
<div align="center">
**🌍 Language / 语言**
[🇺🇸 English](./Troubleshooting.en.md) | [🇨🇳 中文](./Troubleshooting.md)
</div>
本指南涵盖了使用 AI Proxy Worker 时可能遇到的常见问题及其解决方案。如果你遇到的问题不在此列表中,请在 [GitHub Issues](../../issues) 中提交新的问题报告。
## 🚨 常见错误及解决方案
### 1. 认证相关错误
#### ❌ 401 Unauthorized
**错误信息:**
```json
{
"error": "unauthorized",
"details": "Invalid or missing authorization",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**可能原因:**
- `PROXY_KEY` 未设置或设置错误
- 请求头中缺少 `Authorization` 字段
- `Authorization` 格式不正确
**解决方案:**
1. **检查环境变量设置:**
```bash
# 查看已设置的密钥(不显示值)
wrangler secret list
# 重新设置 PROXY_KEY
wrangler secret put PROXY_KEY
```
2. **检查请求格式:**
```bash
# 正确格式
curl -H "Authorization: Bearer YOUR_PROXY_KEY" \
https://your-worker.workers.dev/chat
# 错误格式(缺少 Bearer
curl -H "Authorization: YOUR_PROXY_KEY" \
https://your-worker.workers.dev/chat
```
3. **验证密钥是否正确:**
```javascript
// 确保密钥前后没有空格
const proxyKey = "YOUR_PROXY_KEY".trim();
```
#### ❌ Configuration Error
**错误信息:**
```json
{
"error": "configuration_error",
"details": "Service configuration error",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**可能原因:**
- `DEEPSEEK_API_KEY` 未设置
- DeepSeek API 密钥无效或已过期
**解决方案:**
1. **重新设置 DeepSeek API 密钥:**
```bash
wrangler secret put DEEPSEEK_API_KEY
```
2. **验证 DeepSeek 密钥有效性:**
```bash
curl -X POST https://api.deepseek.com/chat/completions \
-H "Authorization: Bearer YOUR_DEEPSEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
3. **检查 DeepSeek 账户状态:**
- 登录 [DeepSeek 平台](https://platform.deepseek.com/)
- 检查账户余额
- 确认 API 密钥状态
### 2. 请求格式错误
#### ❌ 400 Bad Request - Invalid JSON
**错误信息:**
```json
{
"error": "invalid_request",
"details": "Invalid JSON format",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**解决方案:**
1. **验证 JSON 格式:**
```bash
# 使用在线 JSON 验证器或
echo '{"model":"deepseek-chat","messages":[]}' | python -m json.tool
```
2. **检查特殊字符:**
```javascript
// 正确转义特殊字符
const message = "He said: \"Hello, world!\"";
const payload = JSON.stringify({
model: "deepseek-chat",
messages: [{ role: "user", content: message }]
});
```
#### ❌ 400 Bad Request - Missing Required Fields
**错误信息:**
```json
{
"error": "invalid_request",
"details": "Invalid request format. Missing or invalid messages array",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**解决方案:**
确保请求包含必需字段:
```json
{
"model": "deepseek-chat", // 必需
"messages": [ // 必需,且必须是数组
{
"role": "user", // 必需user/assistant/system
"content": "Hello" // 必需:消息内容
}
]
}
```
#### ❌ 413 Payload Too Large
**错误信息:**
```json
{
"error": "payload_too_large",
"details": "Request body too large. Maximum size: 1048576 bytes",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**解决方案:**
1. **减少请求内容:**
- 缩短对话历史
- 减少单条消息长度
- 移除不必要的参数
2. **分批处理长文本:**
```javascript
function splitLongText(text, maxLength = 8000) {
const chunks = [];
for (let i = 0; i < text.length; i += maxLength) {
chunks.push(text.slice(i, i + maxLength));
}
return chunks;
}
```
3. **调整配置(如果有权限):**
```javascript
// 在 worker.js 中调整
const CONFIG = {
MAX_BODY_SIZE: 2 * 1024 * 1024, // 增加到 2MB
};
```
### 3. 网络和超时错误
#### ❌ 504 Gateway Timeout
**错误信息:**
```json
{
"error": "timeout",
"details": "Request to DeepSeek API timed out",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**解决方案:**
1. **重试请求:**
```javascript
async function retryRequest(requestFn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await requestFn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
```
2. **减少请求复杂度:**
- 降低 `max_tokens` 参数
- 简化提示内容
- 使用更简单的模型
3. **检查网络连接:**
```bash
# 测试到 DeepSeek API 的连接
curl -I https://api.deepseek.com/
# 测试到 Worker 的连接
curl -I https://your-worker.workers.dev/
```
#### ❌ 502 Bad Gateway
**错误信息:**
```json
{
"error": "upstream_error",
"details": "Failed to connect to DeepSeek API",
"timestamp": "2025-01-01T12:00:00.000Z"
}
```
**解决方案:**
1. **检查 DeepSeek API 状态:**
- 访问 [DeepSeek 状态页面](https://status.deepseek.com/)(如果有)
- 在社交媒体查看服务状态更新
2. **验证 API 密钥:**
```bash
curl -X POST https://api.deepseek.com/chat/completions \
-H "Authorization: Bearer YOUR_DEEPSEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
3. **等待并重试:**
- 等待几分钟后重试
- 如果持续出现,可能是 DeepSeek API 临时故障
### 4. 部署相关问题
#### ❌ Wrangler 登录失败
**错误信息:**
```
Error: Failed to login. Please try again.
```
**解决方案:**
1. **清除登录状态并重新登录:**
```bash
wrangler logout
wrangler login
```
2. **手动登录(如果浏览器无法打开):**
```bash
wrangler login --browser=false
# 复制显示的 URL 到浏览器中打开
```
3. **检查网络代理:**
```bash
# 如果使用代理
export https_proxy=http://proxy.example.com:8080
wrangler login
```
#### ❌ 部署失败
**错误信息:**
```
Error: Failed to publish your Worker
```
**解决方案:**
1. **检查 wrangler.toml 配置:**
```toml
name = "ai-proxy-worker" # 确保名称有效
main = "worker.js" # 确保文件存在
compatibility_date = "2025-01-01" # 使用有效日期
```
2. **验证代码语法:**
```bash
# 检查 JavaScript 语法
node -c worker.js
```
3. **检查账户限制:**
- 免费账户有 Worker 数量限制
- 检查 Cloudflare Dashboard 中的配额使用情况
#### ❌ Worker 无法访问
**现象:**
- 部署成功但访问 Worker URL 时出现错误
- 返回 Cloudflare 默认错误页面
**解决方案:**
1. **检查 Worker 状态:**
```bash
wrangler deployments list
```
2. **查看实时日志:**
```bash
wrangler tail
# 然后在另一个终端测试请求
```
3. **验证路由配置:**
```bash
# 检查健康检查端点
curl https://your-worker.workers.dev/
```
### 5. 流式响应问题
#### ❌ 流式响应中断
**现象:**
- 流式响应突然停止
- 收到不完整的响应
**解决方案:**
1. **检查客户端超时设置:**
```javascript
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60秒超时
fetch(url, {
signal: controller.signal,
// ...其他选项
});
```
2. **实现重连机制:**
```javascript
async function streamWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// 处理流式响应
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
```
3. **验证 SSE 格式:**
```javascript
// 确保正确解析 SSE 数据
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') break;
// 处理数据
}
}
```
## 🔍 调试工具和技巧
### 1. 查看 Worker 日志
**实时日志:**
```bash
# 查看实时日志
wrangler tail
# 过滤特定级别的日志
wrangler tail --format=pretty
```
**Cloudflare Dashboard 日志:**
1. 登录 Cloudflare Dashboard
2. 进入 Workers & Pages
3. 选择你的 Worker
4. 点击 "Logs" 标签页
### 2. 本地调试
**本地开发服务器:**
```bash
# 启动本地开发环境
wrangler dev
# 指定端口
wrangler dev --port 8787
```
**添加调试日志:**
```javascript
// 在 worker.js 中添加调试信息
console.log('Request received:', {
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers)
});
```
### 3. 健康检查脚本
创建一个简单的健康检查脚本:
```bash
#!/bin/bash
# health-check.sh
WORKER_URL="https://your-worker.workers.dev"
PROXY_KEY="YOUR_PROXY_KEY"
echo "🔍 健康检查开始..."
# 1. 基础健康检查
echo "1. 检查服务状态..."
curl -s "$WORKER_URL/" | jq .
# 2. 认证测试
echo "2. 测试认证..."
curl -s -X POST "$WORKER_URL/chat" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[]}' | jq .
# 3. API 调用测试
echo "3. 测试 API 调用..."
curl -s -X POST "$WORKER_URL/chat" \
-H "Authorization: Bearer $PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "test"}]
}' | jq .
echo "✅ 健康检查完成"
```
### 4. 性能监控
**响应时间测试:**
```bash
# 创建性能测试脚本
curl -w "@curl-format.txt" -s -o /dev/null \
-X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer YOUR_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[{"role":"user","content":"test"}]}'
```
**curl-format.txt 文件内容:**
```
time_namelookup: %{time_namelookup}s\n
time_connect: %{time_connect}s\n
time_appconnect: %{time_appconnect}s\n
time_pretransfer: %{time_pretransfer}s\n
time_redirect: %{time_redirect}s\n
time_starttransfer: %{time_starttransfer}s\n
----------\n
time_total: %{time_total}s\n
```
## 📊 监控和告警
### 1. Cloudflare Analytics
在 Cloudflare Dashboard 中查看:
- 请求数量和响应时间
- 错误率统计
- 流量分布
### 2. 自定义监控
```javascript
// 在 worker.js 中添加监控指标
const startTime = Date.now();
// 处理请求...
const duration = Date.now() - startTime;
console.log(`Request completed in ${duration}ms`, {
method: request.method,
status: response.status,
duration,
timestamp: new Date().toISOString()
});
```
### 3. 外部监控服务
可以使用以下服务监控 Worker
- Uptime Robot
- Pingdom
- StatusCake
- UptimeRobot
监控配置示例:
```
URL: https://your-worker.workers.dev/
Method: GET
Expected Response: {"status":"ok"}
Check Interval: 5 minutes
```
## 🆘 获取帮助
如果上述解决方案都无法解决你的问题:
### 1. 搜索现有问题
- 查看 [GitHub Issues](../../issues)
- 搜索 [GitHub Discussions](../../discussions)
- 查看 [Cloudflare Community](https://community.cloudflare.com/)
### 2. 提交新问题
在 GitHub Issues 中提交问题时,请包含:
**基本信息:**
- 操作系统和版本
- Node.js 和 Wrangler 版本
- 错误的完整信息
**重现步骤:**
- 详细的操作步骤
- 使用的命令或代码
- 期望的结果 vs 实际结果
**日志信息:**
```bash
# 获取详细日志
wrangler tail --format=pretty > logs.txt
```
**示例请求:**
```bash
# 提供失败的 curl 命令示例
curl -v -X POST https://your-worker.workers.dev/chat \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-chat","messages":[...]}'
```
### 3. 社区资源
- [Cloudflare Workers 文档](https://developers.cloudflare.com/workers/)
- [DeepSeek API 文档](https://platform.deepseek.com/api-docs)
- [GitHub Discussions](../../discussions) - 社区讨论
- [Discord/Telegram 群组](如果有)
---
**问题解决了?** 👉 [回到主页](./Home) | [查看更多示例](./Examples)

42
package.json Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "ai-proxy-worker",
"version": "1.0.0",
"description": "Enterprise-grade AI API Security Proxy - Enable your frontend applications to securely access AI services without exposing API keys, powered by Cloudflare's global edge network",
"main": "worker.js",
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler publish",
"deploy:dev": "wrangler publish --env development",
"deploy:prod": "wrangler publish --env production",
"tail": "wrangler tail",
"test": "echo \"Tests will be added in future versions\" && exit 0"
},
"keywords": [
"ai",
"proxy",
"deepseek",
"cloudflare-workers",
"api-gateway",
"edge-computing",
"serverless",
"chat-completion",
"ai-api",
"llm"
],
"author": "qinfuyao",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/qinfuyao/AI-Proxy-Worker.git"
},
"bugs": {
"url": "https://github.com/qinfuyao/AI-Proxy-Worker/issues"
},
"homepage": "https://github.com/qinfuyao/AI-Proxy-Worker#readme",
"devDependencies": {
"wrangler": "^3.0.0"
},
"engines": {
"node": ">=18.0.0"
}
}

325
worker.js Normal file
View File

@@ -0,0 +1,325 @@
// 配置常量
const CONFIG = {
DEEPSEEK_API_URL: 'https://api.deepseek.com/chat/completions',
MAX_BODY_SIZE: 1024 * 1024, // 1MB
REQUEST_TIMEOUT: 30000, // 30秒
VALIDATE_REQUEST_BODY: false, // 是否验证请求体格式(设为 true 启用严格验证)
DEFAULT_MODEL: 'deepseek-chat', // 默认模型
SUPPORTED_MODELS: [
'deepseek-chat', // 通用对话模型 (DeepSeek-V3)
'deepseek-reasoner' // 推理模型 (DeepSeek-R1)
]
};
// CORS 响应头
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
// 安全响应头
const SECURITY_HEADERS = {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
};
// 辅助函数:验证认证
function validateAuth(request, env) {
const expectedAuth = env.PROXY_KEY ? `Bearer ${env.PROXY_KEY}` : null;
const gotAuth = request.headers.get('authorization') || '';
return !expectedAuth || gotAuth === expectedAuth;
}
// 辅助函数:创建错误响应
function createErrorResponse(error, status = 500, details = null) {
const errorBody = {
error,
timestamp: new Date().toISOString(),
...(details && { details })
};
return new Response(JSON.stringify(errorBody), {
status,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
...CORS_HEADERS,
...SECURITY_HEADERS,
}
});
}
// 辅助函数:创建成功响应
function createResponse(body, status = 200, headers = {}) {
return new Response(body, {
status,
headers: {
...headers,
...CORS_HEADERS,
...SECURITY_HEADERS,
}
});
}
// 辅助函数验证Content-Type
function validateContentType(request) {
const contentType = request.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
throw new Error('Invalid content type. Expected application/json');
}
}
// 辅助函数:验证请求体大小
function validateContentLength(request) {
const contentLength = parseInt(request.headers.get('content-length') || '0');
if (contentLength > CONFIG.MAX_BODY_SIZE) {
throw new Error(`Request body too large. Maximum size: ${CONFIG.MAX_BODY_SIZE} bytes`);
}
}
// 辅助函数:验证单个消息格式
function validateSingleMessage(message) {
if (!message.role || !message.content) {
throw new Error('Invalid request format. Each message must have role and content');
}
if (!['system', 'user', 'assistant', 'tool'].includes(message.role)) {
throw new Error('Invalid request format. Invalid message role');
}
}
// 辅助函数:验证消息数组
function validateMessages(messages) {
if (!messages || !Array.isArray(messages)) {
throw new Error('Invalid request format. Missing or invalid messages array');
}
if (messages.length === 0) {
throw new Error('Invalid request format. Messages array cannot be empty');
}
for (const message of messages) {
validateSingleMessage(message);
}
}
// 辅助函数:验证模型(警告但不阻止)
function validateModel(model) {
if (model && !CONFIG.SUPPORTED_MODELS.includes(model)) {
console.warn(`Unsupported model: ${model}, supported models: ${CONFIG.SUPPORTED_MODELS.join(', ')}`);
// 注意:这里只是警告,不阻止请求,让 DeepSeek API 自己处理
}
}
// 辅助函数:验证请求体内容
async function validateRequestBody(request) {
try {
const body = await request.clone().json();
validateMessages(body.messages);
validateModel(body.model);
} catch (e) {
if (e.message.includes('Invalid request format')) {
throw e;
}
throw new Error('Invalid JSON format');
}
}
// 辅助函数:验证请求
async function validateRequest(request) {
validateContentType(request);
validateContentLength(request);
if (CONFIG.VALIDATE_REQUEST_BODY) {
await validateRequestBody(request);
}
}
// 辅助函数:发送上游请求
async function sendUpstreamRequest(request, env) {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, CONFIG.REQUEST_TIMEOUT);
try {
const accept = request.headers.get('accept') || 'application/json';
const contentType = request.headers.get('content-type') || 'application/json';
// 处理请求体,确保有默认模型
let requestBody;
try {
const bodyText = await request.text();
const body = JSON.parse(bodyText);
// 如果没有指定模型,使用默认模型
if (!body.model) {
body.model = CONFIG.DEFAULT_MODEL;
console.log(`No model specified, using default: ${CONFIG.DEFAULT_MODEL}`);
}
requestBody = JSON.stringify(body);
} catch (err) {
// 如果解析失败使用原始请求体让上游API处理错误
console.warn('Failed to parse request body for model injection:', err.message);
requestBody = await request.text();
}
const upstream = await fetch(CONFIG.DEEPSEEK_API_URL, {
method: 'POST',
signal: controller.signal,
headers: {
'Authorization': `Bearer ${env.DEEPSEEK_API_KEY}`,
'Content-Type': contentType,
'Accept': accept,
'User-Agent': 'AI-Proxy-Worker/1.0',
},
body: requestBody,
});
clearTimeout(timeoutId);
// 记录请求日志
console.log('DeepSeek API request:', {
status: upstream.status,
statusText: upstream.statusText,
timestamp: new Date().toISOString(),
});
return upstream;
} catch (err) {
clearTimeout(timeoutId);
if (err.name === 'AbortError') {
throw new Error('Request timeout');
}
throw err;
}
}
// 辅助函数:处理简单路由
function handleSimpleRoutes(request, url) {
// 处理 OPTIONS 请求CORS 预检)
if (request.method === 'OPTIONS') {
return createResponse(null, 200, CORS_HEADERS);
}
// 健康检查
if (request.method === 'GET' && url.pathname === '/') {
return createResponse(JSON.stringify({
status: 'ok',
service: 'AI Proxy Worker',
timestamp: new Date().toISOString()
}), 200, { 'Content-Type': 'application/json' });
}
return null;
}
// 辅助函数:处理错误响应
function handleError(err, request, url, startTime) {
const duration = Date.now() - startTime;
console.error('Request failed:', {
error: err.message,
duration: `${duration}ms`,
method: request.method,
pathname: url.pathname,
timestamp: new Date().toISOString(),
});
// 根据错误类型返回不同的状态码
if (err.message.includes('Invalid content type')) {
return createErrorResponse('invalid_content_type', 400, err.message);
}
if (err.message.includes('Request body too large')) {
return createErrorResponse('payload_too_large', 413, err.message);
}
if (err.message.includes('Invalid request format') || err.message.includes('Invalid JSON')) {
return createErrorResponse('invalid_request', 400, err.message);
}
if (err.message.includes('Request timeout')) {
return createErrorResponse('timeout', 504, 'Request to DeepSeek API timed out');
}
// 通用错误
return createErrorResponse('internal_error', 500, 'An unexpected error occurred');
}
export default {
/**
* Cloudflare Workers entry point
* AI Proxy Worker - Universal AI API proxy with enhanced error handling and security
*
* Current Version (v1.0): DeepSeek API support
* - deepseek-chat: General conversation model
* - deepseek-reasoner: Complex reasoning model
*
* Future Versions: Multi-AI support planned (OpenAI, Claude, Gemini)
*/
async fetch(request, env) {
const url = new URL(request.url);
const startTime = Date.now();
try {
// 处理简单路由
const simpleResponse = handleSimpleRoutes(request, url);
if (simpleResponse) return simpleResponse;
// 只允许 POST /chat
if (request.method !== 'POST' || url.pathname !== '/chat') {
return createErrorResponse('not_found', 404, 'Endpoint not found');
}
// 验证认证
if (!validateAuth(request, env)) {
return createErrorResponse('unauthorized', 401, 'Invalid or missing authorization');
}
// 检查必需的环境变量
if (!env.DEEPSEEK_API_KEY) {
console.error('Missing DEEPSEEK_API_KEY environment variable');
return createErrorResponse('configuration_error', 500, 'Service configuration error');
}
// 验证请求
await validateRequest(request);
// 发送上游请求
const upstream = await sendUpstreamRequest(request, env);
// 处理上游错误
if (!upstream.ok) {
const errorText = await upstream.text();
console.error('DeepSeek API error:', {
status: upstream.status,
statusText: upstream.statusText,
body: errorText,
timestamp: new Date().toISOString(),
});
return createErrorResponse('api_error', upstream.status, {
upstream_status: upstream.status,
upstream_message: upstream.statusText
});
}
// 返回成功响应
const responseHeaders = {
'Content-Type': upstream.headers.get('Content-Type') || 'application/json',
'Cache-Control': 'no-store, no-transform',
};
const duration = Date.now() - startTime;
console.log(`Request completed successfully in ${duration}ms`);
return createResponse(upstream.body, upstream.status, responseHeaders);
} catch (err) {
return handleError(err, request, url, startTime);
}
},
};

3
wrangler.toml Normal file
View File

@@ -0,0 +1,3 @@
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2025-08-17"