mirror of
https://github.com/ZSCGR/CloudFlare-ImgBed.git
synced 2026-08-18 14:43:44 +08:00
- Add 'internal' token type to API Token system, hidden from the security settings token list - When WebDAV is enabled, auto-create an internal API Token with list/upload/delete permissions, stored in WebDAV config - When WebDAV is disabled, auto-delete the internal token - WebDAV handler reads the internal token from config and sends it as Bearer auth; if missing, creates one on-the-fly as fallback - Works across both Docker and Cloudflare deployments since the token is persisted in KV/D1, not in process memory
287 lines
8.8 KiB
JavaScript
287 lines
8.8 KiB
JavaScript
import { getDatabase } from '../../utils/databaseAdapter.js';
|
|
import { filterAutoDeleteTokens } from '../../utils/auth/tokenExpiration.js';
|
|
|
|
export async function onRequest(context) {
|
|
// API Token管理,支持创建、删除、列出Token
|
|
const {
|
|
request,
|
|
env
|
|
} = context;
|
|
|
|
const db = getDatabase(env);
|
|
const url = new URL(request.url)
|
|
const method = request.method
|
|
|
|
// GET - 获取所有Token列表
|
|
if (method === 'GET') {
|
|
const tokens = await getApiTokens(db)
|
|
return new Response(JSON.stringify(tokens), {
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
// POST - 创建新Token
|
|
if (method === 'POST') {
|
|
const body = await request.json()
|
|
const { name, permissions, owner, expiresAt = null, autoDelete = false } = body
|
|
|
|
if (!name || !permissions || !owner) {
|
|
return new Response(JSON.stringify({ error: '缺少必要参数' }), {
|
|
status: 400,
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
const token = await createApiToken(db, name, permissions, owner, expiresAt, autoDelete)
|
|
return new Response(JSON.stringify(token), {
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
// DELETE - 删除Token
|
|
if (method === 'DELETE') {
|
|
const tokenId = url.searchParams.get('id')
|
|
|
|
if (!tokenId) {
|
|
return new Response(JSON.stringify({ error: '缺少Token ID' }), {
|
|
status: 400,
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
const result = await deleteApiToken(db, tokenId)
|
|
return new Response(JSON.stringify(result), {
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
// PUT - 更新Token权限
|
|
if (method === 'PUT') {
|
|
const body = await request.json()
|
|
const { tokenId, permissions, expiresAt = null, autoDelete = false } = body
|
|
|
|
if (!tokenId || !permissions) {
|
|
return new Response(JSON.stringify({ error: '缺少必要参数' }), {
|
|
status: 400,
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
const result = await updateApiToken(db, tokenId, permissions, expiresAt, autoDelete)
|
|
return new Response(JSON.stringify(result), {
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
return new Response('Method not allowed', { status: 405 })
|
|
}
|
|
|
|
// 获取所有API Token
|
|
async function getApiTokens(db) {
|
|
const settingsStr = await db.get('manage@sysConfig@security')
|
|
const settings = settingsStr ? JSON.parse(settingsStr) : {}
|
|
const tokens = settings.apiTokens?.tokens || {}
|
|
|
|
// 将 tokens 对象转为数组,并应用向后兼容默认值
|
|
// 过滤掉 internal 类型的 token(不展示在安全设置的 token 列表中)
|
|
const tokenArray = Object.keys(tokens)
|
|
.filter(id => tokens[id].type !== 'internal')
|
|
.map(id => {
|
|
const token = tokens[id]
|
|
return {
|
|
id,
|
|
name: token.name,
|
|
owner: token.owner,
|
|
permissions: token.permissions,
|
|
createdAt: token.createdAt,
|
|
updatedAt: token.updatedAt,
|
|
token: token.token,
|
|
expiresAt: token.expiresAt ?? null,
|
|
autoDelete: token.autoDelete ?? false
|
|
}
|
|
})
|
|
|
|
// 使用 filterAutoDeleteTokens 识别需要自动删除的 Token
|
|
const { toDelete, toKeep } = filterAutoDeleteTokens(tokenArray)
|
|
|
|
// 从数据库中删除符合自动删除条件的 Token
|
|
if (toDelete.length > 0) {
|
|
for (const t of toDelete) {
|
|
delete settings.apiTokens.tokens[t.id]
|
|
}
|
|
await db.put('manage@sysConfig@security', JSON.stringify(settings))
|
|
}
|
|
|
|
// 返回时不包含实际token值,只返回基本信息
|
|
const tokenList = toKeep.map(t => ({
|
|
id: t.id,
|
|
name: t.name,
|
|
owner: t.owner,
|
|
permissions: t.permissions,
|
|
createdAt: t.createdAt,
|
|
updatedAt: t.updatedAt,
|
|
token: t.token.substr(0, 15) + '...', // 只显示前15位
|
|
expiresAt: t.expiresAt,
|
|
autoDelete: t.autoDelete
|
|
}))
|
|
|
|
return { tokens: tokenList }
|
|
}
|
|
|
|
// 创建新的API Token
|
|
export async function createApiToken(db, name, permissions, owner, expiresAt = null, autoDelete = false, type = 'user') {
|
|
const settingsStr = await db.get('manage@sysConfig@security')
|
|
const settings = settingsStr ? JSON.parse(settingsStr) : {}
|
|
|
|
if (!settings.apiTokens) {
|
|
settings.apiTokens = { tokens: {} }
|
|
}
|
|
|
|
const tokenId = generateTokenId()
|
|
const token = generateApiToken()
|
|
const now = new Date().toISOString()
|
|
|
|
const tokenData = {
|
|
id: tokenId,
|
|
name,
|
|
token,
|
|
owner,
|
|
permissions,
|
|
type,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
expiresAt: expiresAt ?? null,
|
|
autoDelete: autoDelete === true
|
|
}
|
|
|
|
settings.apiTokens.tokens[tokenId] = tokenData
|
|
|
|
// 保存到数据库
|
|
await db.put('manage@sysConfig@security', JSON.stringify(settings))
|
|
|
|
return {
|
|
id: tokenId,
|
|
name,
|
|
token,
|
|
owner,
|
|
permissions,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
expiresAt: tokenData.expiresAt,
|
|
autoDelete: tokenData.autoDelete
|
|
}
|
|
}
|
|
|
|
// 删除API Token
|
|
export async function deleteApiToken(db, tokenId) {
|
|
const settingsStr = await db.get('manage@sysConfig@security')
|
|
const settings = settingsStr ? JSON.parse(settingsStr) : {}
|
|
|
|
if (!settings.apiTokens?.tokens?.[tokenId]) {
|
|
return { error: 'Token 不存在' }
|
|
}
|
|
|
|
delete settings.apiTokens.tokens[tokenId]
|
|
|
|
// 保存到数据库
|
|
await db.put('manage@sysConfig@security', JSON.stringify(settings))
|
|
|
|
return { success: true, message: 'Token 已删除' }
|
|
}
|
|
|
|
// 更新API Token
|
|
async function updateApiToken(db, tokenId, permissions, expiresAt = null, autoDelete = false) {
|
|
const settingsStr = await db.get('manage@sysConfig@security')
|
|
const settings = settingsStr ? JSON.parse(settingsStr) : {}
|
|
|
|
if (!settings.apiTokens?.tokens?.[tokenId]) {
|
|
return { error: 'Token 不存在' }
|
|
}
|
|
|
|
settings.apiTokens.tokens[tokenId].permissions = permissions
|
|
settings.apiTokens.tokens[tokenId].updatedAt = new Date().toISOString()
|
|
settings.apiTokens.tokens[tokenId].expiresAt = expiresAt ?? null
|
|
settings.apiTokens.tokens[tokenId].autoDelete = autoDelete === true
|
|
|
|
// 保存到数据库
|
|
await db.put('manage@sysConfig@security', JSON.stringify(settings))
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Token 已更新',
|
|
tokenId
|
|
}
|
|
}
|
|
|
|
// 生成随机Token(使用密码学安全随机数)
|
|
function generateApiToken() {
|
|
const array = new Uint8Array(32);
|
|
crypto.getRandomValues(array);
|
|
const hex = Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
return 'imgbed_' + hex;
|
|
}
|
|
|
|
// 生成Token ID(使用密码学安全随机数)
|
|
function generateTokenId() {
|
|
const array = new Uint8Array(12);
|
|
crypto.getRandomValues(array);
|
|
return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
|
|
// 根据Token获取权限(供其他API使用)
|
|
export async function getTokenPermissions(db, token) {
|
|
const settingsStr = await db.get('manage@sysConfig@security')
|
|
const settings = settingsStr ? JSON.parse(settingsStr) : {}
|
|
const tokens = settings.apiTokens?.tokens || {}
|
|
|
|
// 查找匹配的token
|
|
for (const tokenId in tokens) {
|
|
if (tokens[tokenId].token === token) {
|
|
return tokens[tokenId].permissions
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
// 根据Token获取完整数据对象(供tokenValidator使用)
|
|
export async function getTokenData(db, token) {
|
|
const settingsStr = await db.get('manage@sysConfig@security')
|
|
const settings = settingsStr ? JSON.parse(settingsStr) : {}
|
|
const tokens = settings.apiTokens?.tokens || {}
|
|
|
|
// 查找匹配的token
|
|
for (const tokenId in tokens) {
|
|
if (tokens[tokenId].token === token) {
|
|
const t = tokens[tokenId]
|
|
return {
|
|
id: t.id,
|
|
name: t.name,
|
|
token: t.token,
|
|
owner: t.owner,
|
|
permissions: t.permissions,
|
|
createdAt: t.createdAt,
|
|
updatedAt: t.updatedAt,
|
|
expiresAt: t.expiresAt ?? null,
|
|
autoDelete: t.autoDelete ?? false
|
|
}
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|