diff --git a/src/actions/system-config.ts b/src/actions/system-config.ts index 3c3c625b3..b4615b04c 100644 --- a/src/actions/system-config.ts +++ b/src/actions/system-config.ts @@ -27,6 +27,10 @@ export async function saveSystemSettings(formData: { siteTitle: string; allowGlobalUsageView: boolean; currencyDisplay?: string; + enableAutoCleanup?: boolean; + cleanupRetentionDays?: number; + cleanupSchedule?: string; + cleanupBatchSize?: number; }): Promise> { try { const session = await getSession(); @@ -39,6 +43,10 @@ export async function saveSystemSettings(formData: { siteTitle: validated.siteTitle.trim(), allowGlobalUsageView: validated.allowGlobalUsageView, currencyDisplay: validated.currencyDisplay, + enableAutoCleanup: validated.enableAutoCleanup, + cleanupRetentionDays: validated.cleanupRetentionDays, + cleanupSchedule: validated.cleanupSchedule, + cleanupBatchSize: validated.cleanupBatchSize, }); revalidatePath("/settings/config"); diff --git a/src/app/api/admin/system-config/route.ts b/src/app/api/admin/system-config/route.ts new file mode 100644 index 000000000..ce600b468 --- /dev/null +++ b/src/app/api/admin/system-config/route.ts @@ -0,0 +1,84 @@ +import { getSession } from "@/lib/auth"; +import { logger } from "@/lib/logger"; +import { getSystemSettings, updateSystemSettings } from "@/repository/system-config"; +import { UpdateSystemSettingsSchema } from "@/lib/validation/schemas"; +import { z } from "zod"; + +/** + * GET /api/admin/system-config + * 获取系统配置 + */ +export async function GET() { + const session = await getSession(); + + if (!session || session.user.role !== "admin") { + return new Response("Unauthorized", { status: 401 }); + } + + try { + const settings = await getSystemSettings(); + return Response.json(settings); + } catch (error) { + logger.error("获取系统配置失败", { error }); + return Response.json({ error: "获取系统配置失败" }, { status: 500 }); + } +} + +/** + * POST /api/admin/system-config + * 更新系统配置 + * + * Body: { + * siteTitle: string; + * allowGlobalUsageView: boolean; + * currencyDisplay?: string; + * enableAutoCleanup?: boolean; + * cleanupRetentionDays?: number; + * cleanupSchedule?: string; + * cleanupBatchSize?: number; + * } + */ +export async function POST(req: Request) { + const session = await getSession(); + + if (!session || session.user.role !== "admin") { + return new Response("Unauthorized", { status: 401 }); + } + + try { + const body = await req.json(); + + // 验证请求数据 + const validated = UpdateSystemSettingsSchema.parse(body); + + // 更新系统设置 + const updated = await updateSystemSettings({ + siteTitle: validated.siteTitle.trim(), + allowGlobalUsageView: validated.allowGlobalUsageView, + currencyDisplay: validated.currencyDisplay, + enableAutoCleanup: validated.enableAutoCleanup, + cleanupRetentionDays: validated.cleanupRetentionDays, + cleanupSchedule: validated.cleanupSchedule, + cleanupBatchSize: validated.cleanupBatchSize, + }); + + logger.info("系统配置已更新", { + userId: session.user.id, + changes: validated, + }); + + return Response.json(updated); + } catch (error) { + if (error instanceof z.ZodError) { + const firstError = error.issues[0]; + return Response.json( + { error: firstError.message || "数据验证失败" }, + { status: 400 } + ); + } + + logger.error("更新系统配置失败", { error }); + const message = error instanceof Error ? error.message : "更新系统配置失败"; + return Response.json({ error: message }, { status: 500 }); + } +} diff --git a/src/lib/validation/schemas.ts b/src/lib/validation/schemas.ts index 8d34264cd..444f7f14b 100644 --- a/src/lib/validation/schemas.ts +++ b/src/lib/validation/schemas.ts @@ -220,6 +220,21 @@ export const UpdateSystemSettingsSchema = z.object({ { message: "不支持的货币类型" } ) .optional(), + // 日志清理配置(可选) + enableAutoCleanup: z.boolean().optional(), + cleanupRetentionDays: z.coerce + .number() + .int("保留天数必须是整数") + .min(1, "保留天数不能少于1天") + .max(365, "保留天数不能超过365天") + .optional(), + cleanupSchedule: z.string().min(1, "执行时间不能为空").optional(), + cleanupBatchSize: z.coerce + .number() + .int("批量大小必须是整数") + .min(1000, "批量大小不能少于1000") + .max(100000, "批量大小不能超过100000") + .optional(), }); // 导出类型推断