client/storage.js

126 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 本地部署版本的存储封装:通过 fetch 调用本机 Express 服务的 API
// 数据最终落盘到 server/data.json。接口形态与 Claude artifacts 的
// window.storage 保持一致get 返回 {key, value},不存在时 reject
// 这样上层业务代码App.jsx几乎不需要改动。
const API_BASE = '/api/storage';
export async function storageGet(key) {
const res = await fetch(`${API_BASE}/${encodeURIComponent(key)}`);
if (res.status === 404) {
throw new Error(`key not found: ${key}`);
}
if (!res.ok) {
throw new Error(`storage get failed: ${res.status}`);
}
const data = await res.json();
return { key, value: data.value };
}
export async function storageSet(key, value) {
const res = await fetch(`${API_BASE}/${encodeURIComponent(key)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ value }),
});
if (!res.ok) {
throw new Error(`storage set failed: ${res.status}`);
}
return res.json();
}
// 上传一个与客户相关的沟通附件(文件本身存到磁盘,元数据由调用方存进客户记录)
export async function uploadFile(customerId, file) {
const fd = new FormData();
fd.append('file', file);
const res = await fetch(`/api/uploads/${encodeURIComponent(customerId)}`, {
method: 'POST',
body: fd,
});
if (!res.ok) {
throw new Error(`upload failed: ${res.status}`);
}
return res.json();
}
export async function deleteFile(customerId, fileName) {
const res = await fetch(`/api/uploads/${encodeURIComponent(customerId)}/${encodeURIComponent(fileName)}`, {
method: 'DELETE',
});
if (!res.ok) {
throw new Error(`delete failed: ${res.status}`);
}
return res.json();
}
export async function authCheck() {
const res = await fetch('/api/auth/check');
return res.json();
}
export async function authLogin(username, password) {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || 'login failed');
return data;
}
export async function authLogout() {
const res = await fetch('/api/auth/logout', { method: 'POST' });
return res.json().catch(() => ({}));
}
export async function authChangePassword(currentPassword, newPassword) {
const res = await fetch('/api/auth/change-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ currentPassword, newPassword }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || 'change password failed');
return data;
}
export async function getAiConfig() {
const res = await fetch('/api/ai-config');
return res.json().catch(() => ({ hasKey: false }));
}
export async function saveAiConfig(apiKey) {
const res = await fetch('/api/ai-config', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || 'save failed');
return data;
}
export async function deleteAiConfig() {
const res = await fetch('/api/ai-config', { method: 'DELETE' });
return res.json().catch(() => ({}));
}
export async function testAiConfig() {
const res = await fetch('/api/ai-config/test', { method: 'POST' });
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || 'test failed');
return data;
}
export async function generateAiDraft(customerId, channel) {
const res = await fetch('/api/ai/draft', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ customerId, channel }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || 'generate failed');
return data;
}