feat(media): 对象存储 - 新增腾讯云 COS 与历史凭据兼容
Co-authored-by: MasterChiefCN <49179444+MasterChiefCN@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com> Original-PR: https://github.com/ddcat-ai/open-ai-canvas/pull/218
This commit is contained in:
@@ -7,7 +7,7 @@ import { useUserStore } from "@/stores/use-user-store";
|
||||
|
||||
type OSSFormValues = {
|
||||
enabled?: boolean;
|
||||
provider: "aliyun";
|
||||
provider: "aliyun" | "tencent";
|
||||
region?: string;
|
||||
endpoint?: string;
|
||||
bucket?: string;
|
||||
@@ -24,6 +24,11 @@ export function UserOSSSettingsForm() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const savedAt = formatSavedAt(setting?.updatedAt);
|
||||
const provider = Form.useWatch("provider", form) || "aliyun";
|
||||
const isTencentCOS = provider === "tencent";
|
||||
const hasCurrentProviderSecret = Boolean(setting && setting.provider === provider && setting.hasAccessKeySecret);
|
||||
const accessKeyIdLabel = isTencentCOS ? "SecretId" : "AccessKey ID";
|
||||
const accessKeySecretLabel = isTencentCOS ? "SecretKey" : "AccessKey Secret";
|
||||
|
||||
useEffect(() => {
|
||||
if (!actor?.id) return;
|
||||
@@ -35,7 +40,7 @@ export function UserOSSSettingsForm() {
|
||||
setSetting(data.setting);
|
||||
form.setFieldsValue(toFormValues(data.setting));
|
||||
})
|
||||
.catch((error) => active && message.error(error instanceof Error ? error.message : "读取个人 OSS 配置失败"))
|
||||
.catch((error) => active && message.error(error instanceof Error ? error.message : "读取个人对象存储配置失败"))
|
||||
.finally(() => active && setLoading(false));
|
||||
return () => {
|
||||
active = false;
|
||||
@@ -43,7 +48,7 @@ export function UserOSSSettingsForm() {
|
||||
}, [actor?.id, form, message]);
|
||||
|
||||
if (!actor) {
|
||||
return <div className="rounded-md border border-dashed border-border px-5 py-10 text-center text-sm text-foreground/55">登录后可配置个人 OSS。</div>;
|
||||
return <div className="rounded-md border border-dashed border-border px-5 py-10 text-center text-sm text-foreground/55">登录后可配置个人对象存储。</div>;
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
@@ -62,9 +67,9 @@ export function UserOSSSettingsForm() {
|
||||
});
|
||||
setSetting(data.setting);
|
||||
form.setFieldsValue(toFormValues(data.setting));
|
||||
message.success(data.setting.enabled ? "个人 OSS 已启用,后续上传将优先使用该存储" : "个人 OSS 已停用,后续上传将使用平台存储");
|
||||
message.success(data.setting.enabled ? "个人对象存储已启用,后续上传将优先使用该存储" : "个人对象存储已停用,后续上传将使用平台存储");
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : "保存个人 OSS 配置失败");
|
||||
message.error(error instanceof Error ? error.message : "保存个人对象存储配置失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -76,9 +81,9 @@ export function UserOSSSettingsForm() {
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2 text-sm font-semibold">
|
||||
<Cloud className="size-4" />
|
||||
我的 OSS
|
||||
我的对象存储
|
||||
</div>
|
||||
<p className="mt-1 max-w-3xl text-xs leading-5 text-foreground/55">启用后,新上传和新生成的媒体优先写入你的 OSS;停用时回退到平台存储。历史资源固定使用创建时的配置版本。</p>
|
||||
<p className="mt-1 max-w-3xl text-xs leading-5 text-foreground/55">支持阿里云 OSS 与腾讯云 COS。启用后,新上传和新生成的媒体优先写入你的存储桶;停用时回退到平台存储。历史资源固定使用创建时的配置版本。</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 gap-2">
|
||||
<Tag color={setting?.enabled ? "success" : "default"}>{setting?.enabled ? "已启用" : "未启用"}</Tag>
|
||||
@@ -89,36 +94,41 @@ export function UserOSSSettingsForm() {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-x-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<Form.Item name="enabled" label="启用个人 OSS" valuePropName="checked" className="mb-3">
|
||||
<Form.Item name="enabled" label="启用个人对象存储" valuePropName="checked" className="mb-3">
|
||||
<Switch checkedChildren="启用" unCheckedChildren="停用" />
|
||||
</Form.Item>
|
||||
<Form.Item name="provider" label="存储服务" rules={[{ required: true, message: "请选择存储服务" }]} className="mb-3">
|
||||
<Select options={[{ label: "阿里云 OSS", value: "aliyun" }]} />
|
||||
<Select
|
||||
options={[{ label: "阿里云 OSS", value: "aliyun" }, { label: "腾讯云 COS", value: "tencent" }]}
|
||||
onChange={(nextProvider: OSSFormValues["provider"]) => {
|
||||
if (nextProvider !== provider) form.setFieldsValue({ region: "", endpoint: "", bucket: "", accessKeyId: "", accessKeySecret: "" });
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="region" label="Region" className="mb-3">
|
||||
<Input spellCheck={false} placeholder="oss-cn-hangzhou" />
|
||||
<Input spellCheck={false} placeholder={isTencentCOS ? "ap-guangzhou" : "oss-cn-hangzhou"} />
|
||||
</Form.Item>
|
||||
<Form.Item name="endpoint" label="Endpoint" className="mb-3">
|
||||
<Input inputMode="url" spellCheck={false} placeholder="https://oss-cn-hangzhou.aliyuncs.com" />
|
||||
<Form.Item name="endpoint" label="Endpoint" extra={isTencentCOS ? "可留空,系统会根据 Region 生成标准 COS Endpoint。" : undefined} className="mb-3">
|
||||
<Input inputMode="url" spellCheck={false} placeholder={isTencentCOS ? "https://cos.ap-guangzhou.myqcloud.com" : "https://oss-cn-hangzhou.aliyuncs.com"} />
|
||||
</Form.Item>
|
||||
<Form.Item name="bucket" label="Bucket" className="mb-3">
|
||||
<Input spellCheck={false} placeholder="my-canvas-assets" />
|
||||
<Input spellCheck={false} placeholder={isTencentCOS ? "my-canvas-assets-1250000000" : "my-canvas-assets"} />
|
||||
</Form.Item>
|
||||
<Form.Item name="pathPrefix" label="路径前缀" className="mb-3">
|
||||
<Input spellCheck={false} placeholder="infinite-canvas" />
|
||||
</Form.Item>
|
||||
<Form.Item name="accessKeyId" label="AccessKey ID" className="mb-3 xl:col-span-1">
|
||||
<Input autoComplete="off" spellCheck={false} placeholder="阿里云 AccessKey ID" />
|
||||
<Form.Item name="accessKeyId" label={accessKeyIdLabel} className="mb-3 xl:col-span-1">
|
||||
<Input autoComplete="off" spellCheck={false} placeholder={isTencentCOS ? "腾讯云 SecretId" : "阿里云 AccessKey ID"} />
|
||||
</Form.Item>
|
||||
<Form.Item name="accessKeySecret" label={setting?.hasAccessKeySecret ? "AccessKey Secret(留空保留)" : "AccessKey Secret"} className="mb-3 xl:col-span-2">
|
||||
<Input.Password autoComplete="new-password" spellCheck={false} placeholder={setting?.hasAccessKeySecret ? "留空保留已加密密钥" : "阿里云 AccessKey Secret"} />
|
||||
<Form.Item name="accessKeySecret" label={hasCurrentProviderSecret ? `${accessKeySecretLabel}(留空保留)` : accessKeySecretLabel} className="mb-3 xl:col-span-2">
|
||||
<Input.Password autoComplete="new-password" spellCheck={false} placeholder={hasCurrentProviderSecret ? "留空保留已加密密钥" : isTencentCOS ? "腾讯云 SecretKey" : "阿里云 AccessKey Secret"} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 flex flex-wrap items-center justify-between gap-3 border-t border-border pt-3">
|
||||
<span className="text-xs text-foreground/50">{savedAt ? `上次保存:${savedAt}` : "尚未保存个人 OSS 配置"}</span>
|
||||
<span className="text-xs text-foreground/50">{savedAt ? `上次保存:${savedAt}` : "尚未保存个人对象存储配置"}</span>
|
||||
<Button type="primary" loading={saving} onClick={() => void save()}>
|
||||
保存个人 OSS
|
||||
保存个人对象存储
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user