前言
本教程将指导你如何通过 Cloudflare Worker 在 Cursor 中以第三方api的方式访问 Claude 和 Deepseek 模型。通过覆盖 OpenAI 的 URL 地址,我们可以使用 第三方api 来访问这些模型。
功能特点
- 支持 OpenAI 的 GPT 模型
- 支持将请求中的 ‘my-cld’ 改写为 ‘claude’(通过 第三方api 代理地址)
- 支持将请求中的 ‘my-deepseek’ 改写为 ‘deepseek’(直接通过 Deepseek 官方 API)
详细步骤
1. 创建 Cloudflare Worker
- 登录到 Cloudflare Dashboard
- 在左侧菜单中选择 “Workers & Pages”
- 点击 “Create Application”
- 选择 “Create Worker”
- 为你的 Worker 命名,然后点击 “Deploy”
- 进入 Worker 编辑界面
2. 配置环境变量
在 Cloudflare Dashboard 中:
- 进入你的 Worker 的设置页面
- 点击 “Settings” 选项卡
- 找到 “Variables” 部分
- 点击 “Add variable” 按钮
添加以下环境变量:
ONEAPI_UPSTREAM
: 你的 第三方api 代理地址DEEPSEEK_UPSTREAM
: deepseek 代理地址, 官方为 api.deepseek.comVALID_ORIGINAL_API_KEY
: 你的 第三方api keyDEEPSEEK_API_KEY
: 你的 Deepseek API key
对于包含敏感信息的变量(API Keys),确保:
- 点击 “Encrypt” 选项
- 设置类型为 “Secret text”
3. 配置 Worker 代码
将以下代码复制到 Worker 中:
export default {
async fetch(request, env, ctx) {
return await handleRequest(request, env);
}
};
// 主要处理函数
async function handleRequest(request, env) {
// 从环境变量获取配置
const default_upstream = env.ONEAPI_UPSTREAM
const deepseek_upstream = env.DEEPSEEK_UPSTREAM
const upstream_path = '/'
const upstream_mobile = default_upstream
const blocked_region = []
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']
const https = true
const disable_cache = false
// 从环境变量获取 API Keys
const VALID_ORIGINAL_API_KEY = env.VALID_ORIGINAL_API_KEY
const DEEPSEEK_API_KEY = env.DEEPSEEK_API_KEY
const region = request.headers.get('cf-ipcountry');
const upper_region = region ? region.toUpperCase() : '';
const ip_address = request.headers.get('cf-connecting-ip');
const user_agent = request.headers.get('user-agent');
let response = null;
let url = new URL(request.url);
let url_hostname = url.hostname;
let isDeepseekRequest = false;
let originalModel = '';
if (request.method === 'POST') {
const contentType = request.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
try {
const clonedRequest = request.clone();
const body = await clonedRequest.json();
const originalApiKey = request.headers.get('authorization')?.replace('Bearer ', '').trim() || '';
if (originalApiKey === VALID_ORIGINAL_API_KEY &&
body.model && typeof body.model === 'string' &&
(body.model.startsWith('deepseek') || body.model.startsWith('my-deepseek'))) {
isDeepseekRequest = true;
originalModel = body.model;
}
} catch (error) { }
}
}
if (https == true) {
url.protocol = 'https:';
} else {
url.protocol = 'http:';
}
let upstream_domain;
if (isDeepseekRequest) {
upstream_domain = deepseek_upstream;
} else if (await device_status(user_agent)) {
upstream_domain = default_upstream;
} else {
upstream_domain = upstream_mobile;
}
url.host = upstream_domain;
if (url.pathname == '//' || url.pathname == '/') {
url.pathname = upstream_path;
} else {
if (isDeepseekRequest) {
url.pathname = '/v1/chat/completions';
} else {
url.pathname = upstream_path + url.pathname;
}
}
if (blocked_region.includes(upper_region)) {
response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
status: 403
});
} else if (blocked_ip_address.includes(ip_address)) {
response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
status: 403
});
} else {
let method = request.method;
if (method === 'POST') {
const contentType = request.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
let body = await request.json();
if (body.model && typeof body.model === 'string') {
if (isDeepseekRequest) {
if (originalModel.startsWith('my-deepseek')) {
body.model = originalModel.replace('my-deepseek', 'deepseek');
}
} else {
body.model = body.model.replace('my-cld', 'claude');
}
}
const headers = new Headers();
for (const [key, value] of request.headers.entries()) {
headers.set(key.toLowerCase(), value);
}
if (isDeepseekRequest) {
headers.set('authorization', `Bearer ${DEEPSEEK_API_KEY}`);
}
headers.set('content-type', 'application/json');
let original_response = await fetch(url.href, {
method: method,
headers: headers,
body: JSON.stringify(body)
});
let original_response_clone = original_response.clone();
let original_text = null;
let response_headers = original_response.headers;
let new_response_headers = new Headers(response_headers);
let status = original_response.status;
if (disable_cache) {
new_response_headers.set('Cache-Control', 'no-store');
}
new_response_headers.set('access-control-allow-origin', '*');
new_response_headers.set('access-control-allow-credentials', 'true');
new_response_headers.delete('content-security-policy');
new_response_headers.delete('content-security-policy-report-only');
new_response_headers.delete('clear-site-data');
if (new_response_headers.get("x-pjax-url")) {
new_response_headers.set("x-pjax-url", response_headers.get("x-pjax-url").replace("//" + upstream_domain, "//" + url_hostname));
}
const content_type = new_response_headers.get('content-type');
if (content_type != null && content_type.includes('text/html') && content_type.includes('UTF-8')) {
original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);
} else {
original_text = original_response_clone.body;
}
response = new Response(original_text, {
status,
headers: new_response_headers
});
} else {
response = await fetch(request);
}
} else {
response = await fetch(request);
}
}
return response;
}
async function replace_response_text(response, upstream_domain, host_name) {
let text = await response.text()
var i, j;
for (i in replace_dict) {
j = replace_dict[i]
if (i == '$upstream') {
i = upstream_domain
} else if (i == '$custom_domain') {
i = host_name
}
if (j == '$upstream') {
j = upstream_domain
} else if (j == '$custom_domain') {
j = host_name
}
let re = new RegExp(i, 'g')
text = text.replace(re, j);
}
return text;
}
async function device_status(user_agent_info) {
var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (var v = 0; v < agents.length; v++) {
if (user_agent_info.indexOf(agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}
const replace_dict = {
'$upstream': '$custom_domain',
};
配置说明
在 Cloudflare Workers 中配置的环境变量:
ONEAPI_UPSTREAM
: 你的第三方api代理地址,如:api.yourdomain.com
- 类型:普通文本
- 示例:
api.yourdomain.com
DEEPSEEK_UPSTREAM
: 你的deepseek代理地址,如:api.deepseek.com
- 类型:普通文本
- 示例:
api.deepseek.com
VALID_ORIGINAL_API_KEY
: 你的第三方api的 API key- 类型:加密文本(Secret)
- 示例:
sk-xxxxxxxxxxxxxxxxxxxxxxxx
DEEPSEEK_API_KEY
: 你的 Deepseek API key- 类型:加密文本(Secret)
- 示例:
sk-xxxxxxxxxxxxxxxxxxxxxxxx
安全提示:将 API Keys 存储为加密的环境变量可以:
- 防止密钥意外泄露到代码库中
- 方便进行密钥轮换
- 在日志中自动隐藏敏感信息
- 符合安全最佳实践
4. 验证配置
部署完成后,你可以使用以下 curl 命令验证配置是否正常:
# 测试 Claude 模型
curl https://your-worker.workers.dev/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: Bearer sk-oriapikey" -d '{
"messages": [
{
"role": "system",
"content": "You are a test assistant."
},
{
"role": "user",
"content": "1+1=?"
}
],
"model": "my-cld-3-5-sonnet-latest"
}'
# 测试 Deepseek 模型
curl https://your-worker.workers.dev/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: Bearer sk-oriapikey" -d '{
"messages": [
{
"role": "system",
"content": "You are a test assistant."
},
{
"role": "user",
"content": "1+1=?"
}
],
"model": "my-deepseek-coder"
}'
5. 在 Cursor 中配置
- 打开 Cursor 设置
- 找到 AI 设置部分
- 在 OpenAI API Key 中填入你配置的
VALID_ORIGINAL_API_KEY
- 在 API URL 中填入
https://your-worker.workers.dev/v1
重要说明:在 Cursor 中点击验证按钮可能会显示 “Invalid openai api key” 的错误提示,这是正常现象,不会影响实际使用。只要你在上一步(步骤4)中使用 curl 命令验证通过,就可以正常在 Cursor 的聊天界面中使用各个模型。这是因为 Cursor 的验证机制直接检查 OpenAI 的 API,而我们使用的是代理服务。
模型映射说明
本配置支持以下模型映射:
my-cld-*
→claude-*
:所有以my-cld
开头的模型名称会被自动映射到对应的 Claude 模型my-deepseek-*
→deepseek-*
:所有以my-deepseek
开头的模型名称会被自动映射到对应的 Deepseek 模型
例如:
my-cld-3-5-sonnet-latest
→claude-3-5-sonnet-latest
my-deepseek-coder
→deepseek-coder
常见问题排查
如果遇到 403 错误:
- 检查 API key 是否正确配置
- 确认 IP 和地区是否在屏蔽列表中
如果遇到模型不可用:
- 确认模型名称是否正确映射
- 检查 第三方api 是否支持该模型
- 验证 Deepseek API key 是否有效
如果遇到连接问题:
- 确认 Worker 是否正常部署
- 检查 upstream 地址是否正确配置
- 验证网络连接是否正常
注意事项
- 请确保你的 API Key 安全,不要泄露给他人
- 建议定期更换 API Key
- 如果使用频率较高,请注意 Cloudflare Worker 的使用配额
- 建议在正式使用前进行充分的测试验证
如果你需要任何帮助或遇到问题,欢迎提出反馈。