前言
距离一时火热的优雅系列拼车工具发布已经远超半年了,上个月 oaifree 被噶,之前的 chatgpt 优雅系列变成历史。
最近用了一段时间 oaipro,感觉和小伙伴拼车还是之前的方案优雅。
于是,想要重振优雅系列的荣光~
Credits
感谢这些基于 cf worker 的相关工作,有所参考:
- 【Fuclaude号池worker代码,含教程】在Claude的帮助下糊个Claude风格的Fuclaude号池分享的cf-worker
- 源码分享 - Cloudflare Worker 建立 Claude 镜像号池
- 【Fuclaude 号池】 cf worker部署& node 部署 - #3,来自 EFL
- …
先看效果
-
选择’账号’+输入’网站密码’+输入’unique name’的方式直接登录,适合几个小伙伴内共享使用若干个 Claude 号,可以有 Claude Pro
-
退出登录后会重回我们的共享登录页
Worker
KV
- kv_shared_fuclaude
- SITE_PASSWORD
- account_nickname_to_session_keys,格式是 json, key 为账户昵称,value 为 session key。
{ "账户昵称1(随便起什么)": "sk-ant-sid01--xxxxxxxxxx...", "账户昵称2": "sk-ant-sid01--xxxxxxxxxx..." }
Binding
代码
var KV = kv_shared_fuclaude;
const HOST = 'demo.fuclaude.com';
const BASE_URL = `https://${HOST}`;
const AUTH_ENDPOINT = '/manage-api/auth/oauth_token';
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function generateLoginHtml(accounts) {
return `<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI服务</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
padding: 20px;
}
form, .response-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
max-width: 600px;
margin: 20px auto;
}
h1 {
text-align: center;
}
h2 {
text-align: center;
}
p {
display: block;
margin-bottom: 10px;
font-size: 16px;
}
input[type="text"], textarea {
width: calc(100% - 22px);
padding: 10px;
margin-top: 5px;
margin-bottom: 20px;
border-radius: 5px;
border: 1px solid #ccc;
}
textarea {
font-family: 'Courier New', monospace;
background-color: #f8f8f8;
height: 150px; /* Adjust height based on your needs */
}
button {
background-color: #000000;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
font-weight:600;
width:100% !important
}
button:hover {
background-color: #1e293b;
}
@media (max-width: 768px) {
body, form, .response-container {
padding: 10px;
}
}
.checkbox-group {
display: flex;
justify-content: space-between;
}
.checkbox-group input[type="checkbox"] {
margin-right: 5px;
}
.checkbox-group label {
margin-right: 10px;
}
select {
width: calc(100% - 22px);
padding: 10px;
margin-top: 5px;
margin-bottom: 20px;
border-radius: 5px;
border: 1px solid #ccc;
font-size: 16px;
background-color: white;
}
select:focus {
outline: none;
border-color: #000;
}
</style>
</head>
<body>
<h1>标题</h1>
<form method="POST">
<label for="account-select">请选择一个号:</label>
<select id="account-select" name="account">
<option value="" disabled selected>请选择一个账号</option>
${Object.keys(accounts).map((nickname) => `
<option value="${nickname}">${nickname}</option>
`).join('')}
</select>
<br/>
<label for="site_password">请输入本网站使用密码:</label>
<input type="text" id="site_password" name="site_password" placeholder="The website password">
<label for="unique_name">请输入独一无二的名字,以区分你的身份,用于会话隔离:</label>
<input type="text" id="unique_name" name="unique_name" placeholder="Your unique name" required value="">
<br/>
<button type="submit">访问使用</button>
</form>
</html>`;
}
async function handleRequest(request) {
const requestURL = new URL(request.url);
// ------------ 反代到 fuclaude
const path = requestURL.pathname;
if (path !== '/login') {
requestURL.host = HOST;
return fetch(new Request(requestURL, request))
}
const accountsJsonStr = await KV.get('account_nickname_to_session_keys');
const accounts = JSON.parse(accountsJsonStr);
// 返回账号列表
if (request.method === "GET") {
const html = await generateLoginHtml(accounts);
return new Response(html, {
headers: {
'Content-Type': 'text/html; charset=utf-8'
},
});
}
// 选中账号进行登录
if (request.method === "POST") {
const formData = await request.formData();
const account = formData.get("account");
const sitePassword = formData.get('site_password') || '';
const uniqueName = formData.get('unique_name');
// @ts-ignore
const SITE_PASSWORD = await KV.get('SITE_PASSWORD') || '';
if (sitePassword !== SITE_PASSWORD) {
return new Response('站密码错误', { status: 401 }); //如果你不需要密码访问,注释此行代码
}
const sessionKey = accounts[account];
const apiResp = await fetch(`${BASE_URL}${AUTH_ENDPOINT}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
session_key: sessionKey,
unique_name: uniqueName
}),
});
const respData = await apiResp.json();
if (respData.login_url) {
return Response.redirect(`https://${requestURL.host}${respData.login_url}`, 301)
}
return new Response("未知错误~", {
headers: {
'Content-Type': 'text/html; charset=utf-8'
},
});
}
}
其他
- 喜欢的点个赞再走
- 如果有更好的想法改动,之后可以引这篇帖子,其他佬友能在该帖的相关帖子上看到