feat: 修改S3配置的类型,并实现系统管理页面中S3相关的账号和cos配置

This commit is contained in:
Pan Qiancheng 2025-10-16 14:44:21 +08:00
parent 6ce0cf80aa
commit 88482b96c1
5 changed files with 1157 additions and 590 deletions

View File

@ -11,6 +11,140 @@ import {
LocalCloudConfig,
} from '../../../../types/Config';
function S3Account(props: {
accounts: LocalCloudConfig[];
setValue: (path: string, value: any) => void;
removeItem: (path: string, index: number) => void;
addItem: (path: string, index: number) => void;
}) {
const { accounts, setValue, removeItem, addItem } = props;
return (
<Col flex="auto">
<Divider orientation="left" className={Styles.title}>
S3配置
</Divider>
<Tabs
tabPosition={'top'}
size={'middle'}
type="editable-card"
hideAdd={!(accounts.length > 0)}
onEdit={(targetKey: any, action: 'add' | 'remove') => {
if (action === 'add') {
addItem('', accounts.length);
} else {
removeItem('', parseInt(targetKey, 10));
}
}}
items={
accounts.length > 0
? accounts.map((ele, idx) => ({
key: `${idx}`,
label: `帐号${idx + 1}`,
children: (
<Form
colon={false}
labelAlign="left"
layout="vertical"
style={{ marginTop: 10 }}
>
<Form.Item
label="accessKey"
//name="accessKey"
>
<>
<Input
placeholder="请输入accessKey"
type="text"
value={ele.accessKey}
onChange={(e) =>
setValue(
`${idx}.accessKey`,
e.target.value
)
}
/>
</>
</Form.Item>
<Form.Item
label="secretKey"
//name="secretKey"
>
<>
<Input
placeholder="请输入secretKey"
type="text"
value={ele.secretKey}
onChange={(e) =>
setValue(
`${idx}.secretKey`,
e.target.value
)
}
/>
</>
</Form.Item>
</Form>
),
}))
: [
{
label: '新建帐号',
key: '0',
children: (
<Form
colon={true}
labelAlign="left"
layout="vertical"
style={{ marginTop: 10 }}
>
<Form.Item
label="accessKey"
// name="accessKey"
>
<>
<Input
placeholder="请输入accessKey"
type="text"
value=""
onChange={(e) =>
setValue(
`0.accessKey`,
e.target.value
)
}
/>
</>
</Form.Item>
<Form.Item
label="secretKey"
// name="secretKey"
>
<>
<Input
placeholder="请输入secretKey"
type="text"
value=""
onChange={(e) =>
setValue(
`0.secretKey`,
e.target.value
)
}
/>
</>
</Form.Item>
</Form>
),
},
]
}
></Tabs>
</Col>
);
}
function LocalAccount(props: {
accounts: LocalCloudConfig[];
setValue: (path: string, value: any) => void;
@ -1035,7 +1169,7 @@ export default function Account(props: {
removeItem: (path: string, index: number) => void;
}) {
const { account, setValue, removeItem } = props;
const { tencent, qiniu, ali, amap, ctyun, local } = account;
const { tencent, qiniu, ali, amap, ctyun, local, s3 } = account;
return (
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
<Row>
@ -1073,6 +1207,12 @@ export default function Account(props: {
removeItem={(path, index) => removeItem(`local`, index)}
addItem={(path, index) => setValue(`local.${index}`, {})}
/>
<S3Account
accounts={s3 || []}
setValue={(path, value) => setValue(`s3.${path}`, value)}
removeItem={(path, index) => removeItem(`s3`, index)}
addItem={(path, index) => setValue(`s3.${index}`, {})}
/>
<AmapAccount
accounts={amap || []}
setValue={(path, value) => setValue(`amap.${path}`, value)}

File diff suppressed because it is too large Load Diff

View File

@ -94,13 +94,12 @@ export type S3Zone =
// S3/Minio 配置类型
export type S3CosConfig = {
accessKey: string;
secretKey: string; // S3 需要 secretKey
endpoint?: string; // 自定义端点Minio 必需AWS S3 可选)
buckets: {
zone?: S3Zone; // S3 区域 (某些 S3 兼容服务可能不需要)
name: string; // 桶名称
domain: string; // 访问域名
protocol: Protocol | Protocol[];
endpoint?: string; // 自定义端点Minio 必需AWS S3 可选)
pathStyle?: boolean; // 是否使用路径风格 URLMinio 常用)
}[];
defaultBucket: string; // 默认上传桶

View File

@ -4,24 +4,36 @@ import { CosBackend } from '../../types/Cos';
import S3 from './s3';
import { OpSchema } from '../../oak-app-domain/ExtraFile/Schema';
import { S3CosConfig } from '../../types/Config';
import { S3CosConfig, S3Zone } from '../../types/Config';
import { S3Instance, S3SDK } from 'oak-external-sdk';
import { OakExternalException } from 'oak-domain/lib/types/Exception';
export default class S3Backend extends S3 implements CosBackend<EntityDict> {
private getConfigAndInstance(
application: EntityDict['application']['Schema']
application: EntityDict['application']['Schema'],
bucket: string | null | undefined
) {
const { config, account } = this.getConfig(application);
const { config, account, endpoint, defaultBucket } = this.getConfig(application);
const realBucket = bucket || defaultBucket;
assert(realBucket, '没有指定上传桶,且配置中也没有默认上传桶');
// 在配置中找名称匹配的桶
const { buckets } = config as S3CosConfig;
let bucketConfig = bucket
? buckets.find((ele) => ele.name === realBucket)
: buckets[0];
const instance = S3SDK.getInstance(
account.accessKey,
account.secretKey,
config.buckets[0]?.endpoint, // 使用第一个 bucket 的 endpoint 作为默认
config.buckets[0]?.zone
endpoint,
bucketConfig?.zone || 'us-east-1' as S3Zone
);
return {
config,
instance,
endpoint,
};
}
@ -31,8 +43,7 @@ export default class S3Backend extends S3 implements CosBackend<EntityDict> {
) {
const { bucket } = extraFile;
const key = this.formKey(extraFile);
const { instance, config: s3CosConfig } =
this.getConfigAndInstance(application);
const { instance, config: s3CosConfig, endpoint } = this.getConfigAndInstance(application, bucket);
const { buckets } = s3CosConfig as S3CosConfig;
let bucket2 = bucket;
@ -48,7 +59,7 @@ export default class S3Backend extends S3 implements CosBackend<EntityDict> {
const uploadInfo = await (instance as S3Instance).getUploadInfo(
bucket2,
key,
b.endpoint,
endpoint,
b.pathStyle
);
@ -65,9 +76,10 @@ export default class S3Backend extends S3 implements CosBackend<EntityDict> {
application: EntityDict['application']['Schema'],
extraFile: OpSchema
) {
const { bucket } = extraFile;
const key = this.formKey(extraFile);
const { instance, config: s3CosConfig } =
this.getConfigAndInstance(application);
const { instance, config: s3CosConfig, endpoint } =
this.getConfigAndInstance(application, bucket);
const b = (s3CosConfig as S3CosConfig).buckets.find(
(ele) => ele.name === extraFile.bucket
@ -81,7 +93,7 @@ export default class S3Backend extends S3 implements CosBackend<EntityDict> {
const result = await (instance as S3Instance).isExistObject(
extraFile.bucket!,
key,
b.endpoint,
endpoint,
b.pathStyle
);
@ -95,9 +107,10 @@ export default class S3Backend extends S3 implements CosBackend<EntityDict> {
application: EntityDict['application']['Schema'],
extraFile: OpSchema
) {
const { bucket } = extraFile
const key = this.formKey(extraFile);
const { instance, config: s3CosConfig } =
this.getConfigAndInstance(application);
const { instance, config: s3CosConfig, endpoint } =
this.getConfigAndInstance(application, bucket);
const b = (s3CosConfig as S3CosConfig).buckets.find(
(ele) => ele.name === extraFile.bucket
@ -111,7 +124,7 @@ export default class S3Backend extends S3 implements CosBackend<EntityDict> {
await (instance as S3Instance).removeFile(
extraFile.bucket!,
key,
b.endpoint,
endpoint,
b.pathStyle
);
} catch (err: any) {

View File

@ -20,7 +20,7 @@ export default class S3 implements Cos<EntityDict> {
const { config } = system!;
const s3Config = config.Cos?.s3;
assert(s3Config);
const { accessKey } = s3Config;
const { accessKey, endpoint, defaultBucket } = s3Config;
const account = config.Account?.s3?.find(
(ele) => ele.accessKey === accessKey
);
@ -28,6 +28,8 @@ export default class S3 implements Cos<EntityDict> {
return {
config: s3Config,
account,
endpoint,
defaultBucket,
};
}
@ -90,7 +92,7 @@ export default class S3 implements Cos<EntityDict> {
extraFile: Partial<EntityDict['extraFile']['OpSchema']>,
style?: string
) {
const { config: s3CosConfig } = this.getConfig(application);
const { config: s3CosConfig, endpoint } = this.getConfig(application);
if (s3CosConfig) {
let bucket = (
@ -98,7 +100,7 @@ export default class S3 implements Cos<EntityDict> {
).find((ele) => ele.name === extraFile.bucket!);
if (bucket) {
const { domain, protocol, endpoint, pathStyle } = bucket;
const { domain, protocol, pathStyle } = bucket;
let protocol2 = protocol;
if (protocol instanceof Array) {
const index = (protocol as Protocol[]).includes('https:')