搭建方式:CF Worker和KV储存
KV储存: 创建命名空间 - CLAUDE_KV
然后点进去worker-设置-绑定如下图
CF Worker 代码,把claude的sk-key改到范例里面就能用了
// Configuration
const CACHE_DURATION_MINUTES = 50;//对话缓存时间 min
const CHECK_PERIOD_MINUTES = 240; //繁忙复位时间 min
const RETENTION_PERIOD_MINUTES = 240;
const BASE_URL = 'https://claude.asia';
const AUTH_ENDPOINT = '/manage-api/auth/oauth_token';
const CACHE_KEY = 'login_urls_cache';
// Convert minutes to milliseconds
const CACHE_DURATION_MS = CACHE_DURATION_MINUTES * 60 * 1000;
const CHECK_PERIOD_MS = CHECK_PERIOD_MINUTES * 60 * 1000;
const RETENTION_PERIOD_MS = RETENTION_PERIOD_MINUTES * 60 * 1000;
// Status constants
const STATUS = {
BUSY: { color: 'busy', status: '繁忙' },
FREE: { color: 'free', status: '空闲' }
};
//放SESSION_KEYS的地方,带引号,一行一个
const SESSION_KEYS = [
'sk-ant-sid01-hh-mtHqsoN1K0yVF-9s7DGrtZRcKedOJJOuwKczckLzVLN_J-XgNA9FKmoHV4_QinzgiVNZjtUe7BQfxA',
'sk-ant-sid01-6QAgd-Dt0nfMeKAXK2x5jpJbM7QbQPkMToHOv3C-OjviencKp27Gm0faNtm6cpuQ7hrpDVQ4dXtwKAAA',
//'sk-ant-sid01-6QAgd-Dt0nfMeAXK2x5jtpJbM7QbQPkMToHOv3C-OjviencKp27Gm0faNtmjoTR6cpuQ7hrpDVQ4AA',
//'sk-ant-sid01-6QAgd-Dt0nfMeAXK2x5jtpJbM7QbQPkMToHOv3C-OjviencKp27Gm0faNtmjoTR6cpuQ7hrpDVQ4AA',
];
async function getLoginUrl(sessionKey) {
try {
const response = await fetch(`${BASE_URL}${AUTH_ENDPOINT}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
session_key: sessionKey,
unique_name: Date.now().toString()
}),
});
const data = await response.json();
if (data.login_url) {
return data.login_url.startsWith('http')
? data.login_url
: `${BASE_URL}${data.login_url}`;
}
} catch (error) {
console.error("Login URL Error:", error);
}
return '#';
}
async function getAccountStatus(accountNumber, env) {
const key = `clicks_${accountNumber}`;
const currentTime = Date.now();
try {
const clicks = await env.CLAUDE_KV.get(key, 'json') || [];
const recentClicks = clicks.filter(time => (currentTime - time) < CHECK_PERIOD_MS);
return recentClicks.length >= 1 ? STATUS.BUSY : STATUS.FREE;
} catch (error) {
console.error("Status Error:", error);
return STATUS.FREE;
}
}
async function updateClickTime(accountNumber, env) {
const key = `clicks_${accountNumber}`;
const currentTime = Date.now();
try {
const clicks = await env.CLAUDE_KV.get(key, 'json') || [];
const recentClicks = clicks.filter(time => (currentTime - time) < RETENTION_PERIOD_MS);
recentClicks.push(currentTime);
await env.CLAUDE_KV.put(key, JSON.stringify(recentClicks));
} catch (error) {
console.error("Update Click Error:", error);
}
}
async function generateHTML(loginUrls, accountStatuses) {
return `<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Claude 镜像号池</title>
<style>
body, html {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
margin: 0;
padding: 0;
min-height: 100vh;
background: linear-gradient(135deg, #ffffff, #f0f0f0);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
}
h1, h2 {
color: #2c3e50;
margin: 0;
}
.accounts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 15px;
margin: 30px 0;
}
.account {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 80px;
text-decoration: none;
padding: 12px 8px;
border-radius: 12px;
font-size: 0.95rem;
transition: all 0.3s ease;
border: 1px solid rgba(0,0,0,0.1);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.account.free {
background-color: #81c784;
color: #1b5e20;
}
.account.busy {
background-color: #ef9a9a;
color: #c62828;
}
.account:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.account span {
margin-top: 5px;
font-size: 0.85rem;
opacity: 0.9;
}
footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #eee;
text-align: center;
color: #666;
}
@media (max-width: 768px) {
.accounts {
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.account {
min-height: 70px;
padding: 10px 5px;
}
}
</style>
<script>
function updateStatus(accountNumber, event) {
event.preventDefault();
const link = event.currentTarget;
const url = link.href;
fetch('?clicked=' + accountNumber)
.then(() => {
window.open(url, '_blank');
setTimeout(() => location.reload(), 500);
})
.catch(console.error);
}
</script>
</head>
<body>
<div class="container">
<header>
<img src="https://share.claude.asia/img/logo.png" height="30" alt="logo">
</header>
<main>
<h2 style="text-align:center;">免费账号</h2>
<div class="accounts">
${loginUrls.map((url, i) => `
<a href="${url}"
class="account ${accountStatuses[i].color}"
onclick="updateStatus(${i + 1}, event)">
账号${i + 1}
<span>(${accountStatuses[i].status})</span>
</a>
`).join('')}
</div>
</main>
<footer>
<div class="copyright">© Claude.asia</div>
</footer>
</div>
</body>
</html>`;
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Handle click updates
if (url.searchParams.has('clicked')) {
const clickedAccount = parseInt(url.searchParams.get('clicked'));
if (clickedAccount > 0 && clickedAccount <= SESSION_KEYS.length) {
await updateClickTime(clickedAccount, env);
}
return new Response('OK');
}
// Get login URLs from cache or API
const cacheKey = 'login_urls_cache';
let loginUrls;
try {
const cachedData = await env.CLAUDE_KV.get(cacheKey, 'json');
if (cachedData && (Date.now() - cachedData.timestamp) < (CACHE_DURATION * 1000)) {
loginUrls = cachedData.urls;
} else {
// Dynamically generate login URLs based on SESSION_KEYS length
loginUrls = await Promise.all(SESSION_KEYS.map(key => getLoginUrl(key)));
await env.CLAUDE_KV.put(cacheKey, JSON.stringify({
timestamp: Date.now(),
urls: loginUrls
}));
}
} catch (error) {
console.error("Cache Error:", error);
loginUrls = await Promise.all(SESSION_KEYS.map(key => getLoginUrl(key)));
}
// Get account statuses dynamically based on SESSION_KEYS length
const accountStatuses = await Promise.all(
SESSION_KEYS.map((_, i) => getAccountStatus(i + 1, env))
);
// Generate and return HTML
const html = await generateHTML(loginUrls, accountStatuses);
return new Response(html, {
headers: {
'Content-Type': 'text/html;charset=UTF-8'
}
});
}
};
提取sk keys方式↓:
源码魔改自下面项目