接着这位佬的继续加功能,加了个号池,选号功能
cf workers
const CONFIG = {
ORIGINAL_WEBSITE: "https://demo.fuclaude.com", //填入 fuclaude 网站
SESSION_KEYS: [ "sk-ant-1",
"sk-ant-2",
"sk-ant-3",
"sk-ant-4" ], // 在此处加入你的 session_key
SITE_PASSWORD: "passwd",
GUEST_PASSWORD: "passwd"
};
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
if (url.pathname === '/login') {
return handleRootPath(request, url, true);
}
if (url.pathname === '/') {
return handleRootPath(request, url);
}
return proxyRequest(request);
}
async function handleRootPath(request, url, forceLogin = false) {
const cookie = request.headers.get('Cookie') || '';
if ((!forceLogin) && cookie.includes('_Secure-next-auth.session-data')) {
return Response.redirect(`${url.origin}/new`, 302);
}
if (request.method === 'POST') {
return handleLogin(request, url);
}
return new Response(formHtml, {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}
// 修改 handleLogin 函数以使用选定的 SESSION_KEY
async function handleLogin(request, url) {
try {
const formData = await request.formData();
const loginType = formData.get('login_type');
const selectedKeyIndex = formData.get('session_key_index');
let body = {
'session_key': CONFIG.SESSION_KEYS[selectedKeyIndex]
};
if (loginType === 'site') {
const sitePassword = formData.get('site_password');
if (sitePassword !== CONFIG.SITE_PASSWORD) {
return new Response('站点密码错误', {
status: 403,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
} else if (loginType === 'guest') {
const username = formData.get('username');
const guestPassword = formData.get('guest_password');
if (!username || username.trim() === '') {
return new Response('访客登录必须提供用户名', {
status: 400,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
if (guestPassword !== CONFIG.GUEST_PASSWORD) {
return new Response('访客密码错误', {
status: 403,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
body.unique_name = username;
} else {
return new Response('无效的登录类型', {
status: 400,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
const authUrl = `${CONFIG.ORIGINAL_WEBSITE}/manage-api/auth/oauth_token`;
const apiResponse = await fetch(authUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
if (!apiResponse.ok) {
throw new Error(`API request failed with status ${apiResponse.status}`);
}
const respJson = await apiResponse.json();
const login_url = respJson.login_url || '/';
return Response.redirect(`https://${url.host}${login_url}`, 302);
} catch (error) {
console.error('Login error:', error);
return new Response('登录过程中发生错误', {
status: 500,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
}
async function proxyRequest(request) {
const url = new URL(request.url);
const newUrl = `${CONFIG.ORIGINAL_WEBSITE}${url.pathname}${url.search}`;
const modifiedRequest = new Request(newUrl, request);
const response = await fetch(modifiedRequest);
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('text/html') && url.pathname !== '/login_oauth') {
let html = await response.text();
const regex = /<div[^>]*>(?=[\s\S]*?<h3[\s\S]*?<\/h3>)(?=[\s\S]*?<p[\s\S]*?<\/p>)(?=[\s\S]*?<div[\s\S]*?<\/div>)[\s\S]*?<\/div>/gi
html = html.replace(regex, '');
return new Response(html, {
headers: response.headers
})
}
return response;
}
const formHtml = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fuclaude 登录</title>
<style>
body, html {
height: 100%;
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
padding: 20px;
}
.form-container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
h1 {
color: #4a5568;
text-align: center;
margin-bottom: 30px;
font-size: 28px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #4a5568;
font-weight: 500;
}
input[type="text"] {
width: 100%;
padding: 12px;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.3s ease;
}
input[type="text"]:focus {
outline: none;
border-color: #667eea;
}
input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.3s ease;
}
input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
button {
width: 100%;
padding: 12px;
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s ease;
margin-bottom: 10px;
}
button:hover {
background-color: #5a67d8;
}
.key-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 10px;
margin-bottom: 20px;
}
.key-button {
padding: 10px;
background-color: #4a5568;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.key-button.selected {
background-color: #667eea;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="form-container" id="keySelection">
<h1>Shared Fuclaude</h1>
<div class="key-buttons" id="keyButtons">
<!-- SESSION_KEY 按钮将通过 JavaScript 动态生成 -->
</div>
</div>
<div class="form-container hidden" id="guestLogin">
<h1>访客登录</h1>
<form method="POST">
<input type="hidden" name="login_type" value="guest">
<input type="hidden" id="guest_selected_key_index" name="session_key_index" value="">
<div class="form-group">
<label for="guest_password">访客密码:</label>
<input type="password" id="guest_password" name="guest_password" placeholder="输入访客密码" required>
</div>
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="用于隔离会话,填一个别人猜不到的即可" required>
</div>
<button type="submit">登录</button>
</form>
<button class="switch-btn" onclick="toggleForm()">切换到站点密码登录</button>
<button class="switch-btn" onclick="backToKeySelection()">返回选择 KEY</button>
</div>
<div class="form-container hidden" id="siteLogin">
<h1>站点密码登录</h1>
<form method="POST">
<input type="hidden" name="login_type" value="site">
<input type="hidden" id="site_selected_key_index" name="session_key_index" value="">
<div class="form-group">
<label for="site_password">站点密码:</label>
<input type="password" id="site_password" name="site_password" placeholder="输入站点密码" required>
</div>
<button type="submit">登录</button>
</form>
<button class="switch-btn" onclick="toggleForm()">切换到访客登录</button>
<button class="switch-btn" onclick="backToKeySelection()">返回选择 KEY</button>
</div>
</div>
<script>
const keyCount = ${CONFIG.SESSION_KEYS.length};
function createKeyButtons() {
const container = document.getElementById('keyButtons');
for (let i = 0; i < keyCount; i++) {
const button = document.createElement('button');
button.type = 'button';
button.className = 'key-button';
button.textContent = \`\${i + 1}\`;
button.onclick = () => selectKey(i);
container.appendChild(button);
}
}
function selectKey(index) {
document.getElementById('guest_selected_key_index').value = index;
document.getElementById('site_selected_key_index').value = index;
document.getElementById('keySelection').classList.add('hidden');
document.getElementById('guestLogin').classList.remove('hidden');
}
function backToKeySelection() {
document.getElementById('keySelection').classList.remove('hidden');
document.getElementById('guestLogin').classList.add('hidden');
document.getElementById('siteLogin').classList.add('hidden');
}
function toggleForm() {
const guestLogin = document.getElementById('guestLogin');
const siteLogin = document.getElementById('siteLogin');
if (guestLogin.classList.contains('hidden')) {
guestLogin.classList.remove('hidden');
siteLogin.classList.add('hidden');
} else {
guestLogin.classList.add('hidden');
siteLogin.classList.remove('hidden');
}
}
// 初始化按钮
createKeyButtons();
</script>
</body>
</html>
`;
先选号再输入密码
node 版本
由于这个脚本部署的worker 请求量过大,所以弄了个node版本。
但是这个版本会跳转到ORIGINAL_WEBSITE,有没有大佬指点一下怎么修改。
app.js
const express = require('express');
const fetch = require('node-fetch');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
const CONFIG = {
ORIGINAL_WEBSITE: "https://demo.fuclaude.com", //或者填入 fuclaude 网站
SESSION_KEYS: [ "sk-ant-sid01",
"sk-ant-sid02",
"sk-ant-sid03",
"sk-ant-sid04" ], // 在此处加入你的 session_key
SITE_PASSWORD: "m",
GUEST_PASSWORD: "m"
};
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send(formHtml);
});
app.post('/login', async (req, res) => {
try {
const loginType = req.body.login_type;
const selectedKeyIndex = req.body.session_key_index;
let body = {
'session_key': CONFIG.SESSION_KEYS[selectedKeyIndex]
};
if (loginType === 'site') {
const sitePassword = req.body.site_password;
if (sitePassword !== CONFIG.SITE_PASSWORD) {
return res.status(403).send('站点密码错误');
}
} else if (loginType === 'guest') {
const username = req.body.username;
const guestPassword = req.body.guest_password;
if (!username || username.trim() === '') {
return res.status(400).send('访客登录必须提供用户名');
}
if (guestPassword !== CONFIG.GUEST_PASSWORD) {
return res.status(403).send('访客密码错误');
}
body.unique_name = username;
} else {
return res.status(400).send('无效的登录类型');
}
const authUrl = `${CONFIG.ORIGINAL_WEBSITE}/manage-api/auth/oauth_token`;
const apiResponse = await fetch(authUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
if (!apiResponse.ok) {
throw new Error(`API request failed with status ${apiResponse.status}`);
}
const respJson = await apiResponse.json();
const login_url = respJson.login_url || '/';
res.redirect(`${CONFIG.ORIGINAL_WEBSITE}${login_url}`);
} catch (error) {
console.error('Login error:', error);
res.status(500).send('登录过程中发生错误');
}
});
app.use(async (req, res) => {
try {
const newUrl = `${CONFIG.ORIGINAL_WEBSITE}${req.url}`;
const response = await fetch(newUrl, {
method: req.method,
headers: req.headers,
body: req.method !== 'GET' && req.method !== 'HEAD' ? req.body : undefined
});
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('text/html') && !req.url.includes('/login_oauth')) {
let html = await response.text();
const regex = /<div[^>]*>(?=[\s\S]*?<h3[\s\S]*?<\/h3>)(?=[\s\S]*?<p[\s\S]*?<\/p>)(?=[\s\S]*?<div[\s\S]*?<\/div>)[\s\S]*?<\/div>/gi;
html = html.replace(regex, '');
res.send(html);
} else {
res.status(response.status).set(response.headers).send(await response.buffer());
}
} catch (error) {
console.error('Proxy error:', error);
res.status(500).send('代理请求时发生错误');
}
});
const formHtml = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Claude 号池</title>
<style>
body, html {
height: 100%;
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
padding: 20px;
}
.form-container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
h1 {
color: #4a5568;
text-align: center;
margin-bottom: 30px;
font-size: 28px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #4a5568;
font-weight: 500;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.3s ease;
}
input[type="text"]:focus, input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
button {
width: 100%;
padding: 12px;
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s ease;
margin-bottom: 10px;
}
button:hover {
background-color: #5a67d8;
}
.key-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 10px;
margin-bottom: 20px;
}
.key-button {
padding: 10px;
background-color: #4a5568;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.key-button.selected {
background-color: #667eea;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="form-container" id="keySelection">
<h1>Claude 号池</h1>
<div class="key-buttons" id="keyButtons">
<!-- SESSION_KEY 按钮将通过 JavaScript 动态生成 -->
</div>
</div>
<div class="form-container hidden" id="guestLogin">
<h1>访客登录</h1>
<form method="POST" action="/login">
<input type="hidden" name="login_type" value="guest">
<input type="hidden" id="guest_selected_key_index" name="session_key_index" value="">
<div class="form-group">
<label for="guest_password">访客密码:</label>
<input type="password" id="guest_password" name="guest_password" placeholder="输入访客密码" required>
</div>
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="用于隔离会话,填一个别人猜不到的即可" required>
</div>
<button type="submit">登录</button>
</form>
<button class="switch-btn" onclick="toggleForm()">切换到站点密码登录</button>
<button class="switch-btn" onclick="backToKeySelection()">返回选择 KEY</button>
</div>
<div class="form-container hidden" id="siteLogin">
<h1>站点密码登录</h1>
<form method="POST" action="/login">
<input type="hidden" name="login_type" value="site">
<input type="hidden" id="site_selected_key_index" name="session_key_index" value="">
<div class="form-group">
<label for="site_password">站点密码:</label>
<input type="password" id="site_password" name="site_password" placeholder="输入站点密码" required>
</div>
<button type="submit">登录</button>
</form>
<button class="switch-btn" onclick="toggleForm()">切换到访客登录</button>
<button class="switch-btn" onclick="backToKeySelection()">返回选择 KEY</button>
</div>
</div>
<script>
const keyCount = ${CONFIG.SESSION_KEYS.length};
function createKeyButtons() {
const container = document.getElementById('keyButtons');
for (let i = 0; i < keyCount; i++) {
const button = document.createElement('button');
button.type = 'button';
button.className = 'key-button';
button.textContent = \`\${i + 1}\`;
button.onclick = () => selectKey(i);
container.appendChild(button);
}
}
function selectKey(index) {
document.getElementById('guest_selected_key_index').value = index;
document.getElementById('site_selected_key_index').value = index;
document.getElementById('keySelection').classList.add('hidden');
document.getElementById('guestLogin').classList.remove('hidden');
}
function backToKeySelection() {
document.getElementById('keySelection').classList.remove('hidden');
document.getElementById('guestLogin').classList.add('hidden');
document.getElementById('siteLogin').classList.add('hidden');
}
function toggleForm() {
const guestLogin = document.getElementById('guestLogin');
const siteLogin = document.getElementById('siteLogin');
if (guestLogin.classList.contains('hidden')) {
guestLogin.classList.remove('hidden');
siteLogin.classList.add('hidden');
} else {
guestLogin.classList.add('hidden');
siteLogin.classList.remove('hidden');
}
}
// 初始化按钮
createKeyButtons();
</script>
</body>
</html>
`;
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
package.json
{
"name": "fuclaude-server",
"version": "1.0.0",
"main": "server.js",
"dependencies": {
"express": "^4.17.1",
"node-fetch": "^2.6.1",
"body-parser": "^1.19.0"
},
"scripts": {
"start": "node app.js"
}
}
nginx 修改,不显示
以及退出跳转至号池的网址。
location ^~ / {
# 检查是否是退出请求
if ($request_uri ~* "/logout") {
return 302 https://xxxxxx; # 填这个node的地址
}
proxy_pass http://172.16.238.10:8181;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
add_header Strict-Transport-Security "max-age=31536000";
# 添加JavaScript来拦截页面内的跳转
sub_filter '</head>' '
<style>div[data-theme="claude"][data-mode="dark"] { display: none; }</style>
<script>
(function() {
var originalPushState = history.pushState;
history.pushState = function() {
var result = originalPushState.apply(this, arguments);
if (arguments[2] && (arguments[2].includes("/logout") || arguments[2].includes("/login"))) {
window.location.href = "https://xxxxxx"; #填这个node的地址
}
return result;
};
var originalReplaceState = history.replaceState;
history.replaceState = function() {
var result = originalReplaceState.apply(this, arguments);
if (arguments[2] && (arguments[2].includes("/logout") || arguments[2].includes("/login"))) {
window.location.href = "https://xxxxxx"; #填这个node的地址
}
return result;
};
})();
</script>
</head>';
sub_filter_once on;
}