UNPKG

delta-sync

Version:

A lightweight framework for bi-directional database synchronization with automatic version tracking and conflict resolution.

225 lines (224 loc) 8.34 kB
// 修改后的 RestAdapter.ts 文件 export class RestAdapter { constructor(options) { this.baseUrl = options.baseUrl.endsWith('/') ? options.baseUrl.slice(0, -1) : options.baseUrl; this.headers = { 'Content-Type': 'application/json', ...options.headers }; if (options.apiKey) { this.headers['Authorization'] = `Bearer ${options.apiKey}`; } } async initSync() { try { const response = await fetch(`${this.baseUrl}/health`, { method: 'GET', headers: this.headers }); if (!response.ok) { throw new Error(`REST服务不可用: ${response.status} ${response.statusText}`); } } catch (error) { console.error('初始化REST适配器失败:', error); throw new Error(`无法连接到REST服务: ${error instanceof Error ? error.message : String(error)}`); } } async isAvailable() { try { const response = await fetch(`${this.baseUrl}/health`, { method: 'GET', headers: this.headers }); return response.ok; } catch (error) { return false; } } async read(storeName, options) { const params = new URLSearchParams(); if (options?.limit) params.append('limit', options.limit.toString()); if (options?.offset) params.append('offset', options.offset.toString()); if (options?.since) params.append('since', options.since.toString()); const url = `${this.baseUrl}/stores/${storeName}?${params.toString()}`; try { const response = await fetch(url, { method: 'GET', headers: this.headers }); if (!response.ok) { throw new Error(`读取数据失败: ${response.status} ${response.statusText}`); } const result = await response.json(); return { items: result.items || [], hasMore: result.hasMore || false }; } catch (error) { console.error(`从存储 ${storeName} 读取数据失败:`, error); throw error; } } async readBulk(storeName, ids) { if (!ids.length) return []; try { const response = await fetch(`${this.baseUrl}/stores/${storeName}/bulk`, { method: 'POST', headers: this.headers, body: JSON.stringify({ ids }) }); if (!response.ok) { throw new Error(`批量读取数据失败: ${response.status} ${response.statusText}`); } const result = await response.json(); return result.items || []; } catch (error) { console.error(`从存储 ${storeName} 批量读取数据失败:`, error); throw error; } } async putBulk(storeName, items) { if (!items.length) return []; try { const response = await fetch(`${this.baseUrl}/stores/${storeName}`, { method: 'PUT', headers: this.headers, body: JSON.stringify({ items }) }); if (!response.ok) { throw new Error(`批量写入数据失败: ${response.status} ${response.statusText}`); } const result = await response.json(); return result.items || []; } catch (error) { console.error(`向存储 ${storeName} 批量写入数据失败:`, error); throw error; } } async deleteBulk(storeName, ids) { if (!ids.length) return; try { const response = await fetch(`${this.baseUrl}/stores/${storeName}/delete`, { method: 'POST', headers: this.headers, body: JSON.stringify({ ids }) }); if (!response.ok) { throw new Error(`批量删除数据失败: ${response.status} ${response.statusText}`); } } catch (error) { console.error(`从存储 ${storeName} 批量删除数据失败:`, error); throw error; } } // 文件操作 API - 修复后的版本 async readFile(fileId) { try { console.log(`正在读取文件: ${fileId}`); const response = await fetch(`${this.baseUrl}/files/${fileId}`, { method: 'GET', headers: { ...this.headers, 'Accept': '*/*', 'Content-Type': 'application/json' // 确保Content-Type正确 } }); if (!response.ok) { throw new Error(`读取文件失败: ${response.status} ${response.statusText}`); } return await response.blob(); } catch (error) { console.error(`读取文件 ${fileId} 失败:`, error); throw error; } } async saveFile(content, fileId) { try { console.log(`正在上传文件: ${fileId}`); const formData = new FormData(); // 处理不同类型的内容 let blob; if (typeof content === 'string') { if (content.includes(';base64,')) { // 处理 base64 编码的数据 const [metaPart, base64Data] = content.split(';base64,'); const mimeType = metaPart.replace('data:', '') || 'application/octet-stream'; const byteCharacters = atob(base64Data); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); blob = new Blob([byteArray], { type: mimeType }); } else { // 纯文本 blob = new Blob([content], { type: 'text/plain' }); } } else if (content instanceof ArrayBuffer) { blob = new Blob([content], { type: 'application/octet-stream' }); } else { // 已经是 Blob 类型 blob = content; } // 重要:添加文件到表单,不指定文件名 formData.append('file', blob); formData.append('fileId', fileId); const headers = { ...this.headers }; delete headers['Content-Type']; // 让浏览器自动设置正确的 Content-Type const response = await fetch(`${this.baseUrl}/files`, { method: 'POST', headers: headers, body: formData }); if (!response.ok) { const errorText = await response.text(); throw new Error(`上传文件失败: ${response.status} ${response.statusText} - ${errorText}`); } const result = await response.json(); // 确保返回的附件信息使用我们指定的 ID return { ...result, id: fileId }; } catch (error) { console.error(`上传文件 ${fileId} 失败:`, error); throw error; } } async deleteFile(fileId) { try { console.log(`正在删除文件: ${fileId}`); const response = await fetch(`${this.baseUrl}/files/${fileId}`, { method: 'DELETE', headers: this.headers }); if (!response.ok) { throw new Error(`删除文件失败: ${response.status} ${response.statusText}`); } console.log(`文件 ${fileId} 删除成功`); } catch (error) { console.error(`删除文件 ${fileId} 失败:`, error); throw error; } } }