码桶
发现社区成员的开源项目
settings.js7.6 KB
// Per-user token management (accessible by all authenticated users)
// GET: List user's configured token slots, or retrieve a specific token value
export async function onRequestGet(context) {
const { env } = context;
const kv = env.CF_DNS_KV;
const url = new URL(context.request.url);
const retrieveIndex = url.searchParams.get('retrieve');
if (!kv) {
return new Response(JSON.stringify({ error: 'KV storage not configured.' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
// Determine target user (admin can query other users)
const user = context.data.user || { username: 'admin', role: 'admin' };
const targetUser = url.searchParams.get('user');
const username = (targetUser && user.role === 'admin') ? targetUser : user.username;
const tokensJson = await kv.get(`USER_TOKENS:${username}`);
const tokens = tokensJson ? JSON.parse(tokensJson) : [];
// If ?retrieve=N is set, return the actual token value
if (retrieveIndex !== null) {
const idx = parseInt(retrieveIndex);
const entry = tokens.find(t => t.id === idx);
if (!entry) {
return new Response(JSON.stringify({ error: 'Token not found.' }), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response(JSON.stringify({ token: entry.token || entry.key || '' }), {
headers: { 'Content-Type': 'application/json' }
});
}
// Otherwise list all accounts (without actual token values)
const accounts = tokens.map(t => {
const tp = t.type || (t.email ? 'global_key' : 'api_token');
return { id: t.id, name: t.name, type: tp, source: 'kv', hint: tp === 'global_key' ? (t.email || '') : (t.token ? '…' + t.token.slice(-4) : '') };
});
// Fallback: if no KV tokens and user is admin, auto-detect env var accounts
if (accounts.length === 0 && username === 'admin') {
for (let i = 0; i < 10; i++) {
const envToken = i === 0 ? env.CF_API_TOKEN : env[`CF_API_TOKEN${i}`];
const envEmail = i === 0 ? env.CF_API_EMAIL : env[`CF_API_EMAIL${i}`];
const envKey = i === 0 ? env.CF_GLOBAL_API_KEY : env[`CF_GLOBAL_API_KEY${i}`];
if (envEmail && (envKey || envToken)) {
accounts.push({ id: i, name: envEmail, type: 'global_key', source: 'env', hint: envEmail });
} else if (envToken) {
accounts.push({ id: i, name: `API Token ${i}`, type: 'api_token', source: 'env', hint: '…' + envToken.slice(-4) });
} else {
break;
}
}
}
return new Response(JSON.stringify({ accounts }), {
headers: { 'Content-Type': 'application/json' }
});
}
// POST: Save a CF API Token or Global API Key to user's token list
export async function onRequestPost(context) {
const { env } = context;
const kv = env.CF_DNS_KV;
if (!kv) {
return new Response(JSON.stringify({ error: 'KV storage not configured.' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
const user = context.data.user || { username: 'admin', role: 'admin' };
const body = await context.request.json();
const { token, accountIndex, name, type, email, key } = body;
// Admin can manage other users' tokens
const targetUser = body.user;
const username = (targetUser && user.role === 'admin') ? targetUser : user.username;
const isGlobalKey = type === 'global_key';
if (isGlobalKey) {
// Global API Key requires email and key
if (!email || email.trim() === '' || !key || key.trim() === '') {
return new Response(JSON.stringify({ error: 'Email and Global API Key are required.' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// Verify the Global API Key via user endpoint
const verifyRes = await fetch('https://api.cloudflare.com/client/v4/user', {
headers: {
'X-Auth-Email': email,
'X-Auth-Key': key,
'Content-Type': 'application/json'
}
});
const verifyData = await verifyRes.json();
if (!verifyData.success) {
return new Response(JSON.stringify({ error: 'Invalid Global API Key or email.' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
} else {
// API Token mode
if (!token || token.trim() === '') {
return new Response(JSON.stringify({ error: 'Token is required.' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// Verify the token is valid before saving
const verifyRes = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const verifyData = await verifyRes.json();
if (!verifyData.success || !verifyData.result || verifyData.result.status !== 'active') {
return new Response(JSON.stringify({ error: 'Invalid or inactive token.' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
}
// Read existing tokens
const tokensJson = await kv.get(`USER_TOKENS:${username}`);
const tokens = tokensJson ? JSON.parse(tokensJson) : [];
const idx = accountIndex != null ? parseInt(accountIndex) : 0;
const entryName = (name && name.trim()) ? name.trim() : `Account ${idx}`;
// Build the entry based on type
let newEntry;
if (isGlobalKey) {
newEntry = { id: idx, name: entryName, email, key, type: 'global_key' };
} else {
newEntry = { id: idx, name: entryName, token, type: 'api_token' };
}
// Update existing or add new
const existing = tokens.findIndex(t => t.id === idx);
if (existing >= 0) {
tokens[existing] = newEntry;
} else {
tokens.push(newEntry);
}
await kv.put(`USER_TOKENS:${username}`, JSON.stringify(tokens));
return new Response(JSON.stringify({ success: true, message: 'Token saved.', id: idx }), {
headers: { 'Content-Type': 'application/json' }
});
}
// DELETE: Remove a token from user's token list
export async function onRequestDelete(context) {
const { env } = context;
const kv = env.CF_DNS_KV;
if (!kv) {
return new Response(JSON.stringify({ error: 'KV storage not configured.' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
const user = context.data.user || { username: 'admin', role: 'admin' };
const url = new URL(context.request.url);
const accountIndex = parseInt(url.searchParams.get('index') || '0');
// Admin can manage other users' tokens
const targetUser = url.searchParams.get('user');
const username = (targetUser && user.role === 'admin') ? targetUser : user.username;
const tokensJson = await kv.get(`USER_TOKENS:${username}`);
const tokens = tokensJson ? JSON.parse(tokensJson) : [];
const filtered = tokens.filter(t => t.id !== accountIndex);
await kv.put(`USER_TOKENS:${username}`, JSON.stringify(filtered));
return new Response(JSON.stringify({ success: true, message: 'Token removed.' }), {
headers: { 'Content-Type': 'application/json' }
});
}