mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 13:59:28 +08:00
✨ feat: Add configurable proxy bypass setting for S3 backups
Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>
This commit is contained in:
parent
97ab200657
commit
ea9e7fccda
@ -39,7 +39,7 @@ export default class S3Storage {
|
|||||||
private root: string
|
private root: string
|
||||||
|
|
||||||
constructor(config: S3Config) {
|
constructor(config: S3Config) {
|
||||||
const { endpoint, region, accessKeyId, secretAccessKey, bucket, root } = config
|
const { endpoint, region, accessKeyId, secretAccessKey, bucket, root, bypassProxy = true } = config
|
||||||
|
|
||||||
const usePathStyle = (() => {
|
const usePathStyle = (() => {
|
||||||
if (!endpoint) return false
|
if (!endpoint) return false
|
||||||
@ -59,22 +59,24 @@ export default class S3Storage {
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
// Fix for S3 backup failure when using proxy
|
// Conditionally bypass proxy for S3 requests based on user configuration
|
||||||
// Issue: When proxy is enabled, S3 uploads can fail with incomplete writes
|
// When bypassProxy is true (default), S3 requests use a direct dispatcher to avoid
|
||||||
// Error: "Io error: put_object write size < data.size(), w_size=15728640, data.size=16396159"
|
// proxy interference with large file uploads that can cause incomplete transfers
|
||||||
// Root cause: AWS SDK uses global fetch/undici dispatcher which routes through proxy,
|
// Error example: "Io error: put_object write size < data.size(), w_size=15728640, data.size=16396159"
|
||||||
// causing data corruption or incomplete transfers for large files
|
let requestHandler: FetchHttpHandler | undefined
|
||||||
// Solution: Configure S3Client with a direct dispatcher that bypasses the global proxy
|
|
||||||
const directDispatcher = new UndiciAgent({
|
|
||||||
connect: {
|
|
||||||
timeout: 60000 // 60 second connection timeout
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const requestHandler = new FetchHttpHandler({
|
if (bypassProxy) {
|
||||||
requestTimeout: 300000, // 5 minute request timeout for large files
|
const directDispatcher = new UndiciAgent({
|
||||||
dispatcher: directDispatcher
|
connect: {
|
||||||
})
|
timeout: 60000 // 60 second connection timeout
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
requestHandler = new FetchHttpHandler({
|
||||||
|
requestTimeout: 300000, // 5 minute request timeout for large files
|
||||||
|
dispatcher: directDispatcher
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.client = new S3Client({
|
this.client = new S3Client({
|
||||||
region,
|
region,
|
||||||
@ -84,7 +86,7 @@ export default class S3Storage {
|
|||||||
secretAccessKey: secretAccessKey
|
secretAccessKey: secretAccessKey
|
||||||
},
|
},
|
||||||
forcePathStyle: usePathStyle,
|
forcePathStyle: usePathStyle,
|
||||||
requestHandler
|
...(requestHandler && { requestHandler })
|
||||||
})
|
})
|
||||||
|
|
||||||
this.bucket = bucket
|
this.bucket = bucket
|
||||||
|
|||||||
@ -3419,6 +3419,10 @@
|
|||||||
"help": "When enabled, file data will be skipped during backup, only configuration information will be backed up, significantly reducing backup file size",
|
"help": "When enabled, file data will be skipped during backup, only configuration information will be backed up, significantly reducing backup file size",
|
||||||
"label": "Lightweight Backup"
|
"label": "Lightweight Backup"
|
||||||
},
|
},
|
||||||
|
"bypassProxy": {
|
||||||
|
"help": "When enabled, S3 requests bypass the application proxy to prevent data corruption during large file uploads. Recommended to keep enabled unless your S3 endpoint requires proxy access.",
|
||||||
|
"label": "Bypass Proxy"
|
||||||
|
},
|
||||||
"syncStatus": {
|
"syncStatus": {
|
||||||
"error": "Sync error: {{message}}",
|
"error": "Sync error: {{message}}",
|
||||||
"label": "Sync Status",
|
"label": "Sync Status",
|
||||||
|
|||||||
@ -3419,6 +3419,10 @@
|
|||||||
"help": "开启后备份时将跳过文件数据,仅备份配置信息,显著减小备份文件体积",
|
"help": "开启后备份时将跳过文件数据,仅备份配置信息,显著减小备份文件体积",
|
||||||
"label": "精简备份"
|
"label": "精简备份"
|
||||||
},
|
},
|
||||||
|
"bypassProxy": {
|
||||||
|
"help": "开启后,S3 请求将绕过应用程序代理,以防止大文件上传时的数据损坏。建议保持开启,除非您的 S3 端点需要通过代理访问。",
|
||||||
|
"label": "绕过代理"
|
||||||
|
},
|
||||||
"syncStatus": {
|
"syncStatus": {
|
||||||
"error": "同步错误: {{message}}",
|
"error": "同步错误: {{message}}",
|
||||||
"label": "同步状态",
|
"label": "同步状态",
|
||||||
|
|||||||
@ -3419,6 +3419,10 @@
|
|||||||
"help": "開啟後備份時將跳過檔案資料,僅備份設定資訊,顯著減小備份檔案體積",
|
"help": "開啟後備份時將跳過檔案資料,僅備份設定資訊,顯著減小備份檔案體積",
|
||||||
"label": "精簡備份"
|
"label": "精簡備份"
|
||||||
},
|
},
|
||||||
|
"bypassProxy": {
|
||||||
|
"help": "開啟後,S3 請求將繞過應用程式代理,以防止大檔案上傳時的資料損壞。建議保持開啟,除非您的 S3 端點需要透過代理存取。",
|
||||||
|
"label": "繞過代理"
|
||||||
|
},
|
||||||
"syncStatus": {
|
"syncStatus": {
|
||||||
"error": "同步錯誤: {{message}}",
|
"error": "同步錯誤: {{message}}",
|
||||||
"label": "同步狀態",
|
"label": "同步狀態",
|
||||||
|
|||||||
@ -31,7 +31,8 @@ const S3Settings: FC = () => {
|
|||||||
root: s3RootInit = '',
|
root: s3RootInit = '',
|
||||||
syncInterval: s3SyncIntervalInit = 0,
|
syncInterval: s3SyncIntervalInit = 0,
|
||||||
maxBackups: s3MaxBackupsInit = 5,
|
maxBackups: s3MaxBackupsInit = 5,
|
||||||
skipBackupFile: s3SkipBackupFileInit = false
|
skipBackupFile: s3SkipBackupFileInit = false,
|
||||||
|
bypassProxy: s3BypassProxyInit = true
|
||||||
} = s3
|
} = s3
|
||||||
|
|
||||||
const [endpoint, setEndpoint] = useState<string | undefined>(s3EndpointInit)
|
const [endpoint, setEndpoint] = useState<string | undefined>(s3EndpointInit)
|
||||||
@ -41,6 +42,7 @@ const S3Settings: FC = () => {
|
|||||||
const [secretAccessKey, setSecretAccessKey] = useState<string | undefined>(s3SecretAccessKeyInit)
|
const [secretAccessKey, setSecretAccessKey] = useState<string | undefined>(s3SecretAccessKeyInit)
|
||||||
const [root, setRoot] = useState<string | undefined>(s3RootInit)
|
const [root, setRoot] = useState<string | undefined>(s3RootInit)
|
||||||
const [skipBackupFile, setSkipBackupFile] = useState<boolean>(s3SkipBackupFileInit)
|
const [skipBackupFile, setSkipBackupFile] = useState<boolean>(s3SkipBackupFileInit)
|
||||||
|
const [bypassProxy, setBypassProxy] = useState<boolean>(s3BypassProxyInit)
|
||||||
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
|
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
|
||||||
|
|
||||||
const [syncInterval, setSyncInterval] = useState<number>(s3SyncIntervalInit)
|
const [syncInterval, setSyncInterval] = useState<number>(s3SyncIntervalInit)
|
||||||
@ -82,6 +84,11 @@ const S3Settings: FC = () => {
|
|||||||
dispatch(setS3Partial({ skipBackupFile: value }))
|
dispatch(setS3Partial({ skipBackupFile: value }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onBypassProxyChange = (value: boolean) => {
|
||||||
|
setBypassProxy(value)
|
||||||
|
dispatch(setS3Partial({ bypassProxy: value }))
|
||||||
|
}
|
||||||
|
|
||||||
const renderSyncStatus = () => {
|
const renderSyncStatus = () => {
|
||||||
if (!endpoint) return null
|
if (!endpoint) return null
|
||||||
|
|
||||||
@ -261,6 +268,14 @@ const S3Settings: FC = () => {
|
|||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingHelpText>{t('settings.data.s3.skipBackupFile.help')}</SettingHelpText>
|
<SettingHelpText>{t('settings.data.s3.skipBackupFile.help')}</SettingHelpText>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.data.s3.bypassProxy.label')}</SettingRowTitle>
|
||||||
|
<Switch checked={bypassProxy} onChange={onBypassProxyChange} />
|
||||||
|
</SettingRow>
|
||||||
|
<SettingRow>
|
||||||
|
<SettingHelpText>{t('settings.data.s3.bypassProxy.help')}</SettingHelpText>
|
||||||
|
</SettingRow>
|
||||||
{syncInterval > 0 && (
|
{syncInterval > 0 && (
|
||||||
<>
|
<>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
|
|||||||
@ -401,7 +401,8 @@ export const initialState: SettingsState = {
|
|||||||
autoSync: false,
|
autoSync: false,
|
||||||
syncInterval: 0,
|
syncInterval: 0,
|
||||||
maxBackups: 0,
|
maxBackups: 0,
|
||||||
skipBackupFile: false
|
skipBackupFile: false,
|
||||||
|
bypassProxy: true
|
||||||
},
|
},
|
||||||
|
|
||||||
// Developer mode
|
// Developer mode
|
||||||
|
|||||||
@ -883,6 +883,7 @@ export type S3Config = {
|
|||||||
autoSync: boolean
|
autoSync: boolean
|
||||||
syncInterval: number
|
syncInterval: number
|
||||||
maxBackups: number
|
maxBackups: number
|
||||||
|
bypassProxy?: boolean // Whether to bypass proxy for S3 requests (default: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { Message } from './newMessage'
|
export type { Message } from './newMessage'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user