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