1- // WebDAV 同步工具
1+ // WebDAV 同步工具(使用 IndexedDB 解决 Safari 配额限制)
22interface WebDAVConfig {
33 url : string
44 username : string
@@ -7,6 +7,9 @@ interface WebDAVConfig {
77
88const WEBDAV_CONFIG_KEY = 'webdav_config'
99const WEBDAV_FILE_NAME = 'chatgpt-backup.json'
10+ const DB_NAME = 'ChatStorage'
11+ const DB_VERSION = 1
12+ const STORE_NAME = 'data'
1013
1114export function getWebDAVConfig ( ) : WebDAVConfig | null {
1215 const config = localStorage . getItem ( WEBDAV_CONFIG_KEY )
@@ -17,6 +20,67 @@ export function saveWebDAVConfig(config: WebDAVConfig): void {
1720 localStorage . setItem ( WEBDAV_CONFIG_KEY , JSON . stringify ( config ) )
1821}
1922
23+ // IndexedDB 操作
24+ function openDB ( ) : Promise < IDBDatabase > {
25+ return new Promise ( ( resolve , reject ) => {
26+ const request = indexedDB . open ( DB_NAME , DB_VERSION )
27+ request . onerror = ( ) => reject ( request . error )
28+ request . onupgradeneeded = ( event : any ) => {
29+ const db = event . target . result
30+ if ( ! db . objectStoreNames . contains ( STORE_NAME ) )
31+ db . createObjectStore ( STORE_NAME )
32+ }
33+ request . onsuccess = ( ) => resolve ( request . result )
34+ } )
35+ }
36+
37+ async function saveToStorage ( data : string ) : Promise < void > {
38+ // 优先使用 IndexedDB(配额更大,Safari 友好)
39+ try {
40+ const db = await openDB ( )
41+ const tx = db . transaction ( STORE_NAME , 'readwrite' )
42+ const store = tx . objectStore ( STORE_NAME )
43+ await new Promise < void > ( ( resolve , reject ) => {
44+ const req = store . put ( data , 'chatStorage' )
45+ req . onsuccess = ( ) => resolve ( )
46+ req . onerror = ( ) => reject ( req . error )
47+ } )
48+ db . close ( )
49+
50+ // 同步到 localStorage(兼容现有代码读取)
51+ try {
52+ localStorage . setItem ( 'chatStorage' , data )
53+ }
54+ catch {
55+ // localStorage 失败不影响,数据已保存到 IndexedDB
56+ }
57+ }
58+ catch ( error ) {
59+ // IndexedDB 失败,降级到 localStorage
60+ localStorage . setItem ( 'chatStorage' , data )
61+ }
62+ }
63+
64+ async function loadFromStorage ( ) : Promise < string > {
65+ // 优先从 IndexedDB 读取
66+ try {
67+ const db = await openDB ( )
68+ const tx = db . transaction ( STORE_NAME , 'readonly' )
69+ const store = tx . objectStore ( STORE_NAME )
70+ const data = await new Promise < string > ( ( resolve , reject ) => {
71+ const req = store . get ( 'chatStorage' )
72+ req . onsuccess = ( ) => resolve ( req . result || '{}' )
73+ req . onerror = ( ) => reject ( req . error )
74+ } )
75+ db . close ( )
76+ return data
77+ }
78+ catch {
79+ // IndexedDB 失败,降级到 localStorage
80+ return localStorage . getItem ( 'chatStorage' ) || '{}'
81+ }
82+ }
83+
2084async function webdavRequest ( config : WebDAVConfig , method : string , data ?: string ) {
2185 const url = `${ config . url . replace ( / \/ $ / , '' ) } /${ WEBDAV_FILE_NAME } `
2286 const res = await fetch ( '/api/webdav-proxy' , {
@@ -34,10 +98,9 @@ export async function syncToWebDAV(): Promise<void> {
3498 const config = getWebDAVConfig ( )
3599 if ( ! config )
36100 throw new Error ( 'WebDAV 未配置' )
37-
38- const data = localStorage . getItem ( 'chatStorage' ) || '{}'
39-
40- // 尝试上传,如果失败尝试创建目录再上传
101+
102+ const data = await loadFromStorage ( )
103+
41104 try {
42105 await webdavRequest ( config , 'PUT' , data )
43106 }
@@ -65,7 +128,7 @@ export async function syncFromWebDAV(): Promise<void> {
65128 const config = getWebDAVConfig ( )
66129 if ( ! config )
67130 throw new Error ( 'WebDAV 未配置' )
68-
131+
69132 const result = await webdavRequest ( config , 'GET' )
70- localStorage . setItem ( 'chatStorage' , result . data || '{}' )
133+ await saveToStorage ( result . data || '{}' )
71134}
0 commit comments