import { App, Button, Form, Input, Select, Space, Switch, Tag } from "antd"; import { Cloud, Info, KeyRound, ShieldCheck } from "lucide-react"; import { useEffect, useMemo, useState, type ReactNode } from "react"; import { getAdminOSSSetting, updateAdminOSSSetting, type AdminOSSSetting } from "@/services/api/auth"; import { useAdminContext } from "../admin-context"; import { AdminPageFrame } from "../components/admin-shell"; import { configuredSecretText, SettingsSectionCard } from "../components/admin-ui"; type OSSFormValues = { enabled?: boolean; provider: "aliyun"; region?: string; endpoint?: string; bucket?: string; accessKeyId?: string; accessKeySecret?: string; pathPrefix?: string }; export default function StorageSettingsPage() { const { message } = App.useApp(); const { references } = useAdminContext(); const [setting, setSetting] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [form] = Form.useForm(); const userNameById = useMemo(() => new Map(references.users.map((user) => [user.id, user.displayName || user.username])), [references.users]); useEffect(() => { void getAdminOSSSetting() .then(({ setting: value }) => { setSetting(value); form.setFieldsValue(formValues(value)); }) .catch((error) => message.error(error instanceof Error ? error.message : "读取 OSS 配置失败")) .finally(() => setLoading(false)); }, [form, message]); const save = async () => { const values = await form.validateFields(); if (values.enabled && !values.accessKeySecret?.trim() && !setting?.hasAccessKeySecret) return message.error("请填写 AccessKey Secret"); if (values.enabled && !values.endpoint?.trim()) return message.error("请填写 OSS Endpoint"); if (values.enabled && !values.bucket?.trim()) return message.error("请填写 OSS Bucket"); if (values.enabled && !values.accessKeyId?.trim()) return message.error("请填写 AccessKey ID"); setSaving(true); try { const result = await updateAdminOSSSetting({ enabled: values.enabled === true, provider: values.provider, region: values.region?.trim() || "", endpoint: values.endpoint?.trim() || "", bucket: values.bucket?.trim() || "", accessKeyId: values.accessKeyId?.trim() || "", accessKeySecret: values.accessKeySecret?.trim() || "", publicBaseUrl: "", pathPrefix: values.pathPrefix?.trim() || "" }); setSetting(result.setting); form.setFieldsValue(formValues(result.setting)); message.success("OSS 配置已保存"); } catch (error) { message.error(error instanceof Error ? error.message : "保存 OSS 配置失败"); } finally { setSaving(false); } }; return (
资源存储规则

启用后,新上传和生成的媒体由后端写入 OSS;未启用时写入后端数据卷。资源统一通过登录鉴权接口读取,不直接暴露 OSS 对象地址。

} title="平台 OSS" description="配置平台媒体资源的默认存储位置。" status={{setting?.enabled ? "已启用" : "未启用"}{setting?.hasAccessKeySecret ? configuredSecretText : "未保存密钥"}} footer={<>
{setting?.updatedAt ? `上次更新:${formatTime(setting.updatedAt)}${setting.updatedBy ? ` · ${userNameById.get(setting.updatedBy) || setting.updatedBy}` : ""}` : "尚未保存 OSS 配置"}
} >
} text="新资源优先上传 OSS" />} text="AccessKey Secret 不回显" />} text="异常时自动本地降级" />
); } function formValues(setting?: AdminOSSSetting | null): OSSFormValues { return { enabled: setting?.enabled || false, provider: setting?.provider || "aliyun", region: setting?.region || "", endpoint: setting?.endpoint || "", bucket: setting?.bucket || "", accessKeyId: setting?.accessKeyId || "", accessKeySecret: "", pathPrefix: setting?.pathPrefix || "" }; } function formatTime(value?: string) { return value ? new Date(value).toLocaleString("zh-CN", { hour12: false }) : "--"; } function Notice({ icon, text }: { icon: ReactNode; text: string }) { return
{icon}{text}
; }