【Fuclaude 号池】 cf worker部署& node 部署

接着这位佬的继续加功能,加了个号池,选号功能 :joy:

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,有没有大佬指点一下怎么修改。 :joy:
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;
}
49 Likes

秀 可以考虑kv号池投喂?

3 Likes

anthropic马上成为下一个oai

6 Likes

感谢楼主!
赞美社区!
:love_you_gesture:

2 Likes

厉害了楼主

2 Likes

访问后,登录过程中发生错误是啥情况

1 Like

这里都要修改成自己的,ORIGINAL_WEBSITE可以用自己的 也可以用始皇的https://demo.fuclaude.com

一开始就是自己的

我这边使用正常,我是修改的这个脚本,可以找最开始写这个脚本的人。 :joy:

F12 → 控制台,看看 Login error: 开头的报错

1 Like

感谢!

2 Likes

应该是ORIGINAL_WEBSITE,这个最后去掉/

1 Like

node版本会跳转到ORIGINAL_WEBSITE,有没有大佬指点一下怎么修改。

1 Like

软件分享人工智能

感谢分享,另外免费版一天能用多少来着

10万?这个一会就几千了,不敢用多

1 Like

你只是最开始的请求代理到 ORIGINAL_WEBSITE,原网站让你重定向的时候你没有处理重定向的url,肯定就跳回去了

1 Like

谁能搞个nginx反代的配置啊?其他的感觉一律属于大炮打蚊子。

反代,基础的都可以,加了basic auth预览会有点问题

这个会把所有的key暴露在前端代码中,不安全