feat: 迁移自定义功能至 1.5.9.9 版本
包含音乐源限制、CI 防 OOM 优化、数据库连接池优化、管理端权限及 UI 细节定制等功能
This commit is contained in:
@@ -6,12 +6,12 @@ import { eq } from 'drizzle-orm'
|
||||
* DELETE /api/admin/api-keys/[id]
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 检查用户权限 - 只有超级管理员可以管理 API Key
|
||||
// 检查用户权限 - 只有超级管理员可以管理API Key
|
||||
const user = event.context.user
|
||||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: '只有超级管理员可以管理 API Key'
|
||||
statusMessage: '只有超级管理员可以管理API Key'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default defineEventHandler(async (event) => {
|
||||
if (!apiKeyId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'API Key ID 不能为空'
|
||||
statusMessage: 'API Key ID不能为空'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default defineEventHandler(async (event) => {
|
||||
if (existingApiKey.length === 0) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: 'API Key 不存在'
|
||||
statusMessage: 'API Key不存在'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: `删除 API Key 失败:${error.message}`
|
||||
statusMessage: `删除API Key失败: ${error.message}`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,12 +6,12 @@ import { eq } from 'drizzle-orm'
|
||||
* GET /api/admin/api-keys/[id]
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 检查用户权限 - 只有超级管理员可以管理 API Key
|
||||
// 检查用户权限 - 只有超级管理员可以管理API Key
|
||||
const user = event.context.user
|
||||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: '只有超级管理员可以管理 API Key'
|
||||
statusMessage: '只有超级管理员可以管理API Key'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default defineEventHandler(async (event) => {
|
||||
if (!apiKeyId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'API Key ID 不能为空'
|
||||
statusMessage: 'API Key ID不能为空'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export default defineEventHandler(async (event) => {
|
||||
if (!apiKey) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: 'API Key 不存在'
|
||||
statusMessage: 'API Key不存在'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: `获取 API Key 详情失败:${error.message}`
|
||||
statusMessage: `获取API Key详情失败: ${error.message}`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ import { apiKeyPermissions, apiKeys, db } from '~/drizzle/db'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { z } from 'zod'
|
||||
import { getBeijingTime } from '~/utils/timeUtils'
|
||||
import { apiPermissionSchema } from './permissions'
|
||||
|
||||
/**
|
||||
* 更新API Key
|
||||
@@ -21,18 +20,18 @@ const updateApiKeySchema = z.object({
|
||||
expiresAt: z.string().optional().nullable(),
|
||||
|
||||
permissions: z
|
||||
.array(apiPermissionSchema)
|
||||
.array(z.enum(['schedules:read', 'songs:read']))
|
||||
.min(1, '至少需要选择一个权限')
|
||||
.optional()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 检查用户权限 - 只有超级管理员可以管理 API Key
|
||||
// 检查用户权限 - 只有超级管理员可以管理API Key
|
||||
const user = event.context.user
|
||||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: '只有超级管理员可以管理 API Key'
|
||||
statusMessage: '只有超级管理员可以管理API Key'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,7 +40,7 @@ export default defineEventHandler(async (event) => {
|
||||
if (!apiKeyId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'API Key ID 不能为空'
|
||||
statusMessage: 'API Key ID不能为空'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ export default defineEventHandler(async (event) => {
|
||||
if (existingApiKey.length === 0) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: 'API Key 不存在'
|
||||
statusMessage: 'API Key不存在'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,14 +82,14 @@ export default defineEventHandler(async (event) => {
|
||||
if (isNaN(expiresAt.getTime())) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '无效的过期时间格式'
|
||||
statusMessage: '无效的过期时间格式'
|
||||
})
|
||||
}
|
||||
// 验证过期时间不能是过去的时间
|
||||
if (expiresAt <= getBeijingTime()) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '过期时间不能是过去的时间'
|
||||
statusMessage: '过期时间不能是过去的时间'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -100,7 +99,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '无效的过期时间格式'
|
||||
statusMessage: '无效的过期时间格式'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -148,17 +147,17 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
|
||||
// 处理 Zod 验证错误
|
||||
// 处理Zod验证错误
|
||||
if (error.name === 'ZodError') {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: `请求参数验证失败:${error.errors.map((e: any) => e.message).join(', ')}`
|
||||
statusMessage: `请求参数验证失败: ${error.errors.map((e: any) => e.message).join(', ')}`
|
||||
})
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: `更新 API Key 失败:${error.message}`
|
||||
statusMessage: `更新API Key失败: ${error.message}`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,12 +6,12 @@ import { and, count, desc, eq, inArray, sql } from 'drizzle-orm'
|
||||
* GET /api/admin/api-keys
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 检查用户权限 - 只有超级管理员可以管理 API Key
|
||||
// 检查用户权限 - 只有超级管理员可以管理API Key
|
||||
const user = event.context.user
|
||||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: '只有超级管理员可以管理 API Key'
|
||||
statusMessage: '只有超级管理员可以管理API Key'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ export default defineEventHandler(async (event) => {
|
||||
: 'active'
|
||||
}
|
||||
} catch (keyError) {
|
||||
console.error('处理API Key数据失败:', keyError)
|
||||
console.error('处理API Key数据失败:', keyError, key)
|
||||
return null
|
||||
}
|
||||
})
|
||||
@@ -193,7 +193,7 @@ export default defineEventHandler(async (event) => {
|
||||
} catch (error: any) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: `获取 API Key 列表失败:${error.message}`
|
||||
statusMessage: `获取API Key列表失败: ${error.message}`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { apiKeyPermissions, apiKeys, db } from '~/drizzle/db'
|
||||
import crypto from 'crypto'
|
||||
import { z } from 'zod'
|
||||
import { getBeijingTime } from '~/utils/timeUtils'
|
||||
import { apiPermissionSchema } from './permissions'
|
||||
import { generateApiKey, hashApiKey } from '~~/server/utils/apiKeyUtils'
|
||||
|
||||
/**
|
||||
* 创建API Key
|
||||
@@ -17,16 +16,16 @@ const createApiKeySchema = z.object({
|
||||
.optional(),
|
||||
expiresAt: z.union([z.string(), z.null(), z.undefined()]).optional(),
|
||||
|
||||
permissions: z.array(apiPermissionSchema).min(1, '至少需要选择一个权限')
|
||||
permissions: z.array(z.enum(['schedules:read', 'songs:read'])).min(1, '至少需要选择一个权限')
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 检查用户权限 - 只有超级管理员可以管理 API Key
|
||||
// 检查用户权限 - 只有超级管理员可以管理API Key
|
||||
const user = event.context.user
|
||||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: '只有超级管理员可以管理 API Key'
|
||||
statusMessage: '只有超级管理员可以管理API Key'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ export default defineEventHandler(async (event) => {
|
||||
// 生成API Key
|
||||
const apiKey = generateApiKey()
|
||||
const keyPrefix = apiKey.substring(0, 10) // vhub_xxxxx
|
||||
const keyHash = await hashApiKey(apiKey)
|
||||
const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex')
|
||||
|
||||
// 处理过期时间
|
||||
let expiresAt: Date | null = null
|
||||
@@ -60,14 +59,14 @@ export default defineEventHandler(async (event) => {
|
||||
if (isNaN(expiresAt.getTime())) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '无效的过期时间格式'
|
||||
statusMessage: '无效的过期时间格式'
|
||||
})
|
||||
}
|
||||
// 验证过期时间不能是过去的时间
|
||||
if (expiresAt <= getBeijingTime()) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '过期时间不能是过去的时间'
|
||||
statusMessage: '过期时间不能是过去的时间'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -77,7 +76,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '无效的过期时间格式'
|
||||
statusMessage: '无效的过期时间格式'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -100,15 +99,7 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
.returning({ id: apiKeys.id })
|
||||
|
||||
const createdApiKey = apiKeyResult[0]
|
||||
if (!createdApiKey) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: '创建 API Key 失败'
|
||||
})
|
||||
}
|
||||
|
||||
const apiKeyId = createdApiKey.id
|
||||
const apiKeyId = apiKeyResult[0].id
|
||||
|
||||
// 插入权限记录
|
||||
const permissionValues = validatedData.permissions.map((permission) => ({
|
||||
@@ -120,7 +111,7 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
return {
|
||||
id: apiKeyId,
|
||||
apiKey,
|
||||
apiKey, // 只在创建时返回完整的API Key
|
||||
name: validatedData.name,
|
||||
description: validatedData.description,
|
||||
keyPrefix,
|
||||
@@ -144,18 +135,27 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
|
||||
// 处理 Zod 验证错误
|
||||
// 处理Zod验证错误
|
||||
if (error.name === 'ZodError') {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: `请求参数验证失败:${error.errors.map((e: any) => e.message).join(', ')}`
|
||||
statusMessage: `请求参数验证失败: ${error.errors.map((e: any) => e.message).join(', ')}`
|
||||
})
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: `创建 API Key 失败:${error.message}`
|
||||
statusMessage: `创建API Key失败: ${error.message}`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 生成API Key
|
||||
* 格式: vhub_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (37字符)
|
||||
*/
|
||||
function generateApiKey(): string {
|
||||
const prefix = 'vhub_'
|
||||
const randomBytes = crypto.randomBytes(16).toString('hex') // 32字符
|
||||
return prefix + randomBytes
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import { ApiLogService } from '~~/server/services/apiLogService'
|
||||
* GET /api/admin/api-keys/logs
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 检查用户权限 - 只有超级管理员可以查看 API 日志
|
||||
// 检查用户权限 - 只有超级管理员可以查看API日志
|
||||
const user = event.context.user
|
||||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: '只有超级管理员可以查看 API 日志'
|
||||
statusMessage: '只有超级管理员可以查看API日志'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ export default defineEventHandler(async (event) => {
|
||||
stats: stats.basic
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取 API 访问日志失败:', error)
|
||||
console.error('获取API访问日志失败:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: '获取 API 访问日志失败'
|
||||
statusMessage: '获取API访问日志失败'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user