diff --git a/public/locales/en-US/dashboard.json b/public/locales/en-US/dashboard.json
index 19ed7a0..700862d 100644
--- a/public/locales/en-US/dashboard.json
+++ b/public/locales/en-US/dashboard.json
@@ -828,6 +828,15 @@
"onedrive": "OneDrive",
"s3": "S3 Compatible",
"obs": "Huawei Cloud OBS",
+ "load_balance": "Load Balance",
+ "childPolicy": "Child Storage Policy",
+ "childPolicyDes": "Select the child storage policies to add to the load balance pool.",
+ "weight": "Weight",
+ "addTargetPolicy": "Add Child Policy",
+ "selectPolicies": "Select Policies",
+ "selectPoliciesDes": "Select storage policies to add to the load balance pool.",
+ "loadBalanceDes": "When using the load balanced storage policy, new uploads will be randomly distributed to different child storage policies based on weight.",
+ "xChildPolicies": "{{count}} child storage policies",
"refresh": "Refresh",
"delete": "Delete",
"edit": "Edit",
diff --git a/public/locales/ja-JP/dashboard.json b/public/locales/ja-JP/dashboard.json
index a3f7c92..c6003cc 100644
--- a/public/locales/ja-JP/dashboard.json
+++ b/public/locales/ja-JP/dashboard.json
@@ -829,6 +829,15 @@
"onedrive": "OneDrive",
"s3": "S3互換",
"obs": "Huawei Cloud OBS",
+ "load_balance": "負荷分散",
+ "childPolicy": "子ストレージポリシー",
+ "childPolicyDes": "負荷分散に追加する子ストレージポリシーを選択してください。",
+ "weight": "Weight重み",
+ "addTargetPolicy": "子ストレージポリシーを追加",
+ "selectPolicies": "ストレージポリシーを選択",
+ "selectPoliciesDes": "負荷分散に追加するストレージポリシーを選択してください。",
+ "loadBalanceDes": "負荷分散ストレージポリシーを使用する場合、新規アップロードは重みに基づいてランダムに異なる子ストレージポリシーに分散されます。",
+ "xChildPolicies": "{{count}} 子ストレージポリシー",
"refresh": "更新",
"delete": "削除",
"edit": "編集",
diff --git a/public/locales/zh-CN/dashboard.json b/public/locales/zh-CN/dashboard.json
index 2f6f252..40fdd04 100644
--- a/public/locales/zh-CN/dashboard.json
+++ b/public/locales/zh-CN/dashboard.json
@@ -829,6 +829,15 @@
"onedrive": "OneDrive",
"s3": "S3 兼容",
"obs": "华为云 OBS",
+ "load_balance": "负载均衡",
+ "childPolicy": "子存储策略",
+ "childPolicyDes": "选择你要添加到负载均衡中的子存储策略。",
+ "weight": "权重",
+ "addTargetPolicy": "添加子存储策略",
+ "selectPolicies": "选择策略",
+ "selectPoliciesDes": "选择要添加到负载均衡的存储策略。",
+ "loadBalanceDes": "使用负载均衡存储策略时,新上传的文件会根据权重随机分配到不同的子存储策略中。",
+ "xChildPolicies": "{{count}} 个子存储策略",
"refresh": "刷新",
"delete": "删除",
"edit": "编辑",
diff --git a/public/locales/zh-TW/dashboard.json b/public/locales/zh-TW/dashboard.json
index a64228f..36d5695 100644
--- a/public/locales/zh-TW/dashboard.json
+++ b/public/locales/zh-TW/dashboard.json
@@ -825,6 +825,15 @@
"onedrive": "OneDrive",
"s3": "S3 相容",
"obs": "華為雲 OBS",
+ "load_balance": "負載均衡",
+ "childPolicy": "子儲存策略",
+ "childPolicyDes": "選擇你要添加到負載均衡中的子儲存策略。",
+ "weight": "權重",
+ "addTargetPolicy": "添加子儲存策略",
+ "selectPolicies": "選擇儲存策略",
+ "selectPoliciesDes": "選擇要添加到負載均衡的儲存策略。",
+ "loadBalanceDes": "使用負載均衡儲存策略時,新上傳的文件會根據權重隨機分配到不同的子儲存策略中。",
+ "xChildPolicies": "{{count}} 子儲存策略",
"refresh": "重新整理",
"delete": "刪除",
"edit": "編輯",
diff --git a/public/static/img/lb.svg b/public/static/img/lb.svg
new file mode 100644
index 0000000..8a9132c
--- /dev/null
+++ b/public/static/img/lb.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/api/explorer.ts b/src/api/explorer.ts
index 0fa09f5..d42303f 100644
--- a/src/api/explorer.ts
+++ b/src/api/explorer.ts
@@ -99,6 +99,7 @@ export enum PolicyType {
upyun = "upyun",
s3 = "s3",
obs = "obs",
+ load_balance = "load_balance",
}
export interface StoragePolicy {
diff --git a/src/component/Admin/StoragePolicy/SelectProvider.tsx b/src/component/Admin/StoragePolicy/SelectProvider.tsx
index fe3bc5c..d339985 100644
--- a/src/component/Admin/StoragePolicy/SelectProvider.tsx
+++ b/src/component/Admin/StoragePolicy/SelectProvider.tsx
@@ -15,6 +15,9 @@ import { SecondaryButton } from "../../Common/StyledComponents";
import DraggableDialog from "../../Dialogs/DraggableDialog";
import Open from "../../Icons/Open";
import { PolicyPropsMap } from "./StoragePolicySetting";
+import { useState } from "react";
+import ProDialog from "../Common/ProDialog.tsx";
+import { ProChip } from "../../Pages/Setting/SettingForm.tsx";
export interface SelectProviderProps {
open: boolean;
@@ -30,6 +33,7 @@ const StyledCard = styled(Card)(({ theme }) => ({
const SelectProvider = ({ open, onClose, onSelect }: SelectProviderProps) => {
const { t } = useTranslation("dashboard");
+ const [proOpen, setProOpen] = useState(false);
return (
{
maxWidth: "sm",
}}
>
+ setProOpen(false)} />
{Object.values(PolicyType).map((type) => (
- onSelect(type)}>
+ (PolicyPropsMap[type].pro ? setProOpen(true) : onSelect(type))}
+ >
{t(PolicyPropsMap[type].name)}
+ {PolicyPropsMap[type].pro && }
diff --git a/src/component/Admin/StoragePolicy/StoragePolicySetting.tsx b/src/component/Admin/StoragePolicy/StoragePolicySetting.tsx
index 97a83b7..76a7308 100644
--- a/src/component/Admin/StoragePolicy/StoragePolicySetting.tsx
+++ b/src/component/Admin/StoragePolicy/StoragePolicySetting.tsx
@@ -60,6 +60,7 @@ export interface PolicyProps {
credentialDes?: React.ReactNode;
corsExposedHeaders?: string[];
endpointNotEnforcePrefix?: boolean;
+ pro?: boolean;
}
export const PolicyPropsMap: Record = {
@@ -70,6 +71,12 @@ export const PolicyPropsMap: Record = {
wizard: LocalWizard,
chunkSizeDes: "policy.chunkSizeDes",
},
+ [PolicyType.load_balance]: {
+ name: "policy.load_balance",
+ img: "/static/img/lb.svg",
+ wizardSize: "sm",
+ pro: true,
+ },
[PolicyType.remote]: {
name: "policy.remote",
img: "/static/img/remote.png",
diff --git a/src/component/Uploader/Uploader.tsx b/src/component/Uploader/Uploader.tsx
index 96b533b..3d363d4 100644
--- a/src/component/Uploader/Uploader.tsx
+++ b/src/component/Uploader/Uploader.tsx
@@ -2,19 +2,19 @@ import dayjs from "dayjs";
import { useSnackbar } from "notistack";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
+import { ContextMenuTypes } from "../../redux/fileManagerSlice.ts";
import { closeUploadTaskList, openUploadTaskList, setUploadProgress } from "../../redux/globalStateSlice.ts";
import { useAppDispatch, useAppSelector } from "../../redux/hooks.ts";
import { refreshFileList, updateUserCapacity } from "../../redux/thunks/filemanager.ts";
import SessionManager, { UserSettings } from "../../session";
+import useActionDisplayOpt from "../FileManager/ContextMenu/useActionDisplayOpt.ts";
+import { FileManagerIndex } from "../FileManager/FileManager.tsx";
import UploadManager, { SelectType } from "./core";
import { UploaderError } from "./core/errors";
import Base, { Status } from "./core/uploader/base.ts";
import { DropFileBackground } from "./DropFile.tsx";
import PasteUploadDialog from "./PasteUploadDialog.tsx";
import TaskList from "./Popup/TaskList.tsx";
-import useActionDisplayOpt from "../FileManager/ContextMenu/useActionDisplayOpt.ts";
-import { ContextMenuTypes } from "../../redux/fileManagerSlice.ts";
-import { FileManagerIndex } from "../FileManager/FileManager.tsx";
let totalProgressCollector: NodeJS.Timeout | null = null;
let lastProgressStart = -1;
diff --git a/src/component/Uploader/core/errors/index.ts b/src/component/Uploader/core/errors/index.ts
index 19ee33b..7a54d12 100644
--- a/src/component/Uploader/core/errors/index.ts
+++ b/src/component/Uploader/core/errors/index.ts
@@ -1,8 +1,8 @@
-import { OneDriveError, QiniuError, UpyunError } from "../types";
-import i18next from "../../../../i18n";
-import { AppError, Response } from "../../../../api/request.ts";
import { StoragePolicy } from "../../../../api/explorer.ts";
+import { AppError, Response } from "../../../../api/request.ts";
+import i18next from "../../../../i18n";
import { sizeToString } from "../../../../util";
+import { OneDriveError, QiniuError, UpyunError } from "../types";
export enum UploaderErrorName {
InvalidFile = "InvalidFile",
@@ -31,6 +31,7 @@ export enum UploaderErrorName {
FailedFinishOSSUpload = "FailedFinishOSSUpload",
FailedFinishQiniuUpload = "FailedFinishQiniuUpload",
FailedTransformResponse = "FailedTransformResponse",
+ LoadBalancePolicyNoAvailable = "LoadBalancePolicyNoAvailable",
}
const RETRY_ERROR_LIST = [
diff --git a/src/component/Uploader/core/index.ts b/src/component/Uploader/core/index.ts
index a1a4d9a..baf429c 100644
--- a/src/component/Uploader/core/index.ts
+++ b/src/component/Uploader/core/index.ts
@@ -165,14 +165,14 @@ export default class UploadManager {
// 选择文件
public select = (dst: string, type = SelectType.File): Promise => {
- return new Promise((resolve) => {
+ return new Promise((resolve, reject) => {
if (this.policy == undefined) {
this.logger.warn(`Calling file selector while no policy is set`);
throw new UploaderError(UploaderErrorName.NoPolicySelected, "No policy selected.");
}
- this.fileInput.onchange = (ev: Event) => this.addFiles(ev, dst, resolve);
- this.directoryInput.onchange = (ev: Event) => this.addFiles(ev, dst, resolve);
+ this.fileInput.onchange = (ev: Event) => this.addFiles(ev, dst, resolve, reject);
+ this.directoryInput.onchange = (ev: Event) => this.addFiles(ev, dst, resolve, reject);
this.fileInput.value = "";
this.directoryInput.value = "";
type == SelectType.File ? this.fileInput.click() : this.directoryInput.click();
@@ -197,8 +197,8 @@ export default class UploadManager {
if (!this.currentPath) {
return;
}
- const uploaders = await new Promise((resolve) =>
- this.addFiles(files, this.currentPath ?? defaultPath, resolve, getName),
+ const uploaders = await new Promise((resolve, reject) =>
+ this.addFiles(files, this.currentPath ?? defaultPath, resolve, reject, getName),
);
this.o.onProactiveFileAdded && this.o.onProactiveFileAdded(uploaders);
};
@@ -207,6 +207,7 @@ export default class UploadManager {
ev: Event | File[],
dst: string,
resolve: (value: Base[] | PromiseLike) => void,
+ reject: (reason?: any) => void,
getName?: (file: File) => string,
) => {
let files: File[] = [];
@@ -221,22 +222,25 @@ export default class UploadManager {
}
if (files.length > 0) {
- resolve(
- files.map(
- (file): Base =>
- this.dispatchUploader({
- type: TaskType.file,
- policy: this.policy as StoragePolicy,
- dst: getDirectoryUploadDst(dst, file),
- file: file,
- size: file.size,
- overwrite: this.overwrite,
- name: getName ? getName(file) : file.name,
- chunkProgress: [],
- resumed: false,
- }),
- ),
- );
+ let uploaders: Base[] = [];
+ try {
+ uploaders = files.map((file): Base => {
+ return this.dispatchUploader({
+ type: TaskType.file,
+ policy: this.policy as StoragePolicy,
+ dst: getDirectoryUploadDst(dst, file),
+ file: file,
+ size: file.size,
+ overwrite: this.overwrite,
+ name: getName ? getName(file) : file.name,
+ chunkProgress: [],
+ resumed: false,
+ });
+ });
+ resolve(uploaders);
+ } catch (e) {
+ reject(e);
+ }
}
};
@@ -248,7 +252,9 @@ export default class UploadManager {
if (containFile) {
this.o.onDropLeave && this.o.onDropLeave(e);
const items = await getAllFileEntries(e.dataTransfer!.items);
- const uploaders = await new Promise((resolve) => this.addFiles(items, this.currentPath, resolve));
+ const uploaders = await new Promise((resolve, reject) =>
+ this.addFiles(items, this.currentPath as string, resolve, reject),
+ );
this.o.onProactiveFileAdded && this.o.onProactiveFileAdded(uploaders);
}
};