Files
users/app/api/api-keys/route.ts
卡若 7c72871a7a chore: 同步本地到 main 和 Gitea
Made-with: Cursor
2026-03-16 14:48:26 +08:00

369 lines
10 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { getMongoClient } from '@/lib/mongodb'
// API密钥接口
interface APIKey {
id: string
name: string
key: string
secret: string
status: 'active' | 'disabled' | 'expired'
createdAt: string
expiresAt: string | null
lastUsed: string | null
permissions: FieldPermission[]
rateLimit: {
requestsPerDay: number
requestsPerMonth: number
}
billing: {
plan: 'free' | 'basic' | 'pro' | 'enterprise'
usedCredits: number
totalCredits: number
}
callStats: {
today: number
thisMonth: number
total: number
}
}
// 字段权限接口
interface FieldPermission {
fieldGroup: string
fields: {
name: string
label: string
enabled: boolean
price: number
}[]
}
// 计费套餐
const BILLING_PLANS = {
free: { credits: 1000, requestsPerDay: 100, requestsPerMonth: 3000 },
basic: { credits: 10000, requestsPerDay: 1000, requestsPerMonth: 30000 },
pro: { credits: 50000, requestsPerDay: 5000, requestsPerMonth: 150000 },
enterprise: { credits: -1, requestsPerDay: -1, requestsPerMonth: -1 },
}
// 默认字段权限
const DEFAULT_FIELD_PERMISSIONS: FieldPermission[] = [
{
fieldGroup: '基础信息',
fields: [
{ name: 'phone', label: '手机号', enabled: true, price: 1 },
{ name: 'qq', label: 'QQ号', enabled: true, price: 1 },
{ name: 'wechat', label: '微信号', enabled: false, price: 2 },
{ name: 'email', label: '邮箱', enabled: false, price: 1 },
{ name: 'nickname', label: '昵称', enabled: true, price: 0.5 },
]
},
{
fieldGroup: '用户画像',
fields: [
{ name: 'rfm_score', label: 'RFM评分', enabled: true, price: 2 },
{ name: 'user_level', label: '用户等级', enabled: true, price: 1 },
{ name: 'value_score', label: '价值评分', enabled: false, price: 3 },
{ name: 'activity_score', label: '活跃度评分', enabled: false, price: 2 },
{ name: 'loyalty_score', label: '忠诚度评分', enabled: false, price: 2 },
]
},
{
fieldGroup: '标签数据',
fields: [
{ name: 'basic_tags', label: '基础标签', enabled: true, price: 1 },
{ name: 'behavior_tags', label: '行为标签', enabled: false, price: 2 },
{ name: 'preference_tags', label: '偏好标签', enabled: false, price: 2 },
{ name: 'ai_tags', label: 'AI智能标签', enabled: false, price: 5 },
{ name: 'custom_tags', label: '自定义标签', enabled: true, price: 1 },
]
},
{
fieldGroup: '行为数据',
fields: [
{ name: 'last_active', label: '最后活跃时间', enabled: true, price: 0.5 },
{ name: 'visit_count', label: '访问次数', enabled: false, price: 1 },
{ name: 'purchase_history', label: '购买历史', enabled: false, price: 5 },
{ name: 'interaction_log', label: '交互记录', enabled: false, price: 3 },
{ name: 'channel_source', label: '渠道来源', enabled: true, price: 1 },
]
},
{
fieldGroup: '扩展数据',
fields: [
{ name: 'social_bindings', label: '社交绑定', enabled: false, price: 3 },
{ name: 'device_info', label: '设备信息', enabled: false, price: 2 },
{ name: 'location_data', label: '位置数据', enabled: false, price: 4 },
{ name: 'risk_assessment', label: '风险评估', enabled: false, price: 5 },
{ name: 'ai_insights', label: 'AI洞察', enabled: false, price: 10 },
]
},
]
const COLLECTION = 'api_keys'
const DB_NAME = 'KR'
async function getCollection() {
const client = await getMongoClient()
return client.db(DB_NAME).collection<APIKey & { _id?: any }>(COLLECTION)
}
// 生成随机密钥
function generateKey(prefix: string): string {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
let result = prefix
for (let i = 0; i < 20; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
}
return result
}
// GET: 获取API密钥列表
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const action = searchParams.get('action')
const keyId = searchParams.get('id')
const col = await getCollection()
// 验证单个密钥
if (action === 'validate') {
const apiKey = searchParams.get('key')
const apiSecret = searchParams.get('secret')
const key = await col.findOne({ key: apiKey, secret: apiSecret })
if (!key) {
return NextResponse.json({
success: false,
error: '无效的API密钥'
}, { status: 401 })
}
if (key.status !== 'active') {
return NextResponse.json({
success: false,
error: `密钥状态: ${key.status}`
}, { status: 403 })
}
if (key.expiresAt && new Date(key.expiresAt) < new Date()) {
return NextResponse.json({
success: false,
error: '密钥已过期'
}, { status: 403 })
}
return NextResponse.json({
success: true,
data: {
id: key.id,
name: key.name,
plan: key.billing.plan,
permissions: key.permissions,
rateLimit: key.rateLimit,
creditsRemaining: key.billing.totalCredits === -1 ? -1 : key.billing.totalCredits - key.billing.usedCredits
}
})
}
// 获取单个密钥详情
if (keyId) {
const key = await col.findOne({ id: keyId })
if (!key) {
return NextResponse.json({
success: false,
error: '密钥不存在'
}, { status: 404 })
}
return NextResponse.json({
success: true,
data: key
})
}
// 获取所有密钥列表
const keys = await col.find({}).sort({ createdAt: -1 }).toArray()
return NextResponse.json({
success: true,
data: keys.map(k => ({
...k,
key: k.key.substring(0, 12) + '••••••••••••',
secret: '••••••••••••••••'
})),
total: keys.length
})
} catch (error) {
console.error('获取API密钥失败:', error)
return NextResponse.json({
success: false,
error: '获取API密钥失败'
}, { status: 500 })
}
}
// POST: 创建新API密钥
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { name, plan = 'basic', expiresAt } = body
if (!name) {
return NextResponse.json({
success: false,
error: '请提供密钥名称'
}, { status: 400 })
}
const planConfig = BILLING_PLANS[plan as keyof typeof BILLING_PLANS] || BILLING_PLANS.basic
const newKey: APIKey = {
id: `key_${Date.now()}`,
name,
key: generateKey('sk-archer-'),
secret: generateKey('sec-'),
status: 'active',
createdAt: new Date().toISOString().split('T')[0],
expiresAt: expiresAt || null,
lastUsed: null,
permissions: JSON.parse(JSON.stringify(DEFAULT_FIELD_PERMISSIONS)),
rateLimit: {
requestsPerDay: planConfig.requestsPerDay,
requestsPerMonth: planConfig.requestsPerMonth
},
billing: {
plan: plan as 'free' | 'basic' | 'pro' | 'enterprise',
usedCredits: 0,
totalCredits: planConfig.credits
},
callStats: { today: 0, thisMonth: 0, total: 0 }
}
const col = await getCollection()
await col.insertOne(newKey)
return NextResponse.json({
success: true,
data: newKey,
message: '密钥创建成功'
})
} catch (error) {
console.error('创建API密钥失败:', error)
return NextResponse.json({
success: false,
error: '创建API密钥失败'
}, { status: 500 })
}
}
// PUT: 更新API密钥
export async function PUT(request: NextRequest) {
try {
const body = await request.json()
const { id, action, permissions, status } = body
const col = await getCollection()
const key = await col.findOne({ id })
if (!key) {
return NextResponse.json({
success: false,
error: '密钥不存在'
}, { status: 404 })
}
if (action === 'updatePermissions' && permissions) {
await col.updateOne({ id }, { $set: { permissions } })
const updated = await col.findOne({ id })
return NextResponse.json({
success: true,
data: updated,
message: '权限更新成功'
})
}
if (action === 'toggleStatus') {
const newStatus = key.status === 'active' ? 'disabled' : 'active'
await col.updateOne({ id }, { $set: { status: newStatus } })
const updated = await col.findOne({ id })
return NextResponse.json({
success: true,
data: updated,
message: `密钥已${newStatus === 'active' ? '启用' : '禁用'}`
})
}
if (action === 'regenerate') {
const newKey = generateKey('sk-archer-')
const newSecret = generateKey('sec-')
await col.updateOne({ id }, { $set: { key: newKey, secret: newSecret } })
const updated = await col.findOne({ id })
return NextResponse.json({
success: true,
data: updated,
message: '密钥已重新生成'
})
}
if (status) {
await col.updateOne({ id }, { $set: { status } })
}
const updated = await col.findOne({ id })
return NextResponse.json({
success: true,
data: updated,
message: '更新成功'
})
} catch (error) {
console.error('更新API密钥失败:', error)
return NextResponse.json({
success: false,
error: '更新API密钥失败'
}, { status: 500 })
}
}
// DELETE: 删除API密钥
export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
if (!id) {
return NextResponse.json({
success: false,
error: '请提供密钥ID'
}, { status: 400 })
}
const col = await getCollection()
const result = await col.findOneAndDelete({ id })
if (!result) {
return NextResponse.json({
success: false,
error: '密钥不存在'
}, { status: 404 })
}
return NextResponse.json({
success: true,
data: { id: result.id, name: result.name },
message: '密钥已删除'
})
} catch (error) {
console.error('删除API密钥失败:', error)
return NextResponse.json({
success: false,
error: '删除API密钥失败'
}, { status: 500 })
}
}