取消背景图
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Real API client for connecting to the FastAPI backend.
|
||||
* Handles auth tokens, request/response encryption, snake_case→camelCase conversion.
|
||||
* Supports both JSON bodies and multipart FormData bodies.
|
||||
*/
|
||||
|
||||
import { encrypt, decrypt, isCryptoAvailable } from './crypto';
|
||||
@@ -24,6 +25,7 @@ function toCamel(s: string): string {
|
||||
function keysToCamel(obj: unknown): unknown {
|
||||
if (Array.isArray(obj)) return obj.map(keysToCamel);
|
||||
if (obj !== null && typeof obj === 'object') {
|
||||
if (obj instanceof File || obj instanceof Blob || obj instanceof FormData) return obj;
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj as Record<string, unknown>).map(([k, v]) => [toCamel(k), keysToCamel(v)])
|
||||
);
|
||||
@@ -54,31 +56,35 @@ async function tryDecrypt(data: string): Promise<string | null> {
|
||||
|
||||
export async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const { method = 'GET', body, auth = true, encryptBody = USE_ENCRYPTION } = options;
|
||||
const isFormData = typeof FormData !== 'undefined' && body instanceof FormData;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
const headers: Record<string, string> = {};
|
||||
if (!isFormData) headers['Content-Type'] = 'application/json';
|
||||
|
||||
if (auth) {
|
||||
const token = getToken();
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
let bodyStr: string | undefined;
|
||||
let requestBody: BodyInit | undefined;
|
||||
if (body !== undefined) {
|
||||
const json = JSON.stringify(body);
|
||||
if (encryptBody) {
|
||||
headers['X-Encrypted'] = 'true';
|
||||
bodyStr = JSON.stringify({ data: await encrypt(json) });
|
||||
if (isFormData) {
|
||||
requestBody = body as FormData;
|
||||
} else {
|
||||
bodyStr = json;
|
||||
const json = JSON.stringify(body);
|
||||
if (encryptBody) {
|
||||
headers['X-Encrypted'] = 'true';
|
||||
requestBody = JSON.stringify({ data: await encrypt(json) });
|
||||
} else {
|
||||
requestBody = json;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(`${BASE_URL}/api${path}`, {
|
||||
method,
|
||||
headers,
|
||||
body: bodyStr,
|
||||
body: requestBody,
|
||||
});
|
||||
|
||||
if (res.status === 204) return undefined as T;
|
||||
@@ -96,7 +102,7 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
|
||||
|
||||
// If the response is encrypted, decrypt it
|
||||
const hasEncryptedData = parsed && typeof parsed.data === 'string';
|
||||
if (hasEncryptedData && encryptBody) {
|
||||
if (hasEncryptedData && encryptBody && !isFormData) {
|
||||
const decrypted = await tryDecrypt(parsed.data);
|
||||
if (decrypted !== null) {
|
||||
parsed = JSON.parse(decrypted);
|
||||
@@ -107,7 +113,8 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
|
||||
|
||||
// Handle error responses
|
||||
if (!res.ok) {
|
||||
const msg = parsed?.detail || `请求失败 (${res.status})`;
|
||||
const rawMsg = parsed?.detail || parsed?.message || `请求失败 (${res.status})`;
|
||||
const msg = typeof rawMsg === 'string' ? rawMsg : JSON.stringify(rawMsg);
|
||||
if (res.status === 401) {
|
||||
clearToken();
|
||||
// Only redirect to login if not already on login page
|
||||
@@ -125,7 +132,7 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
|
||||
// Convenience methods
|
||||
export const api = {
|
||||
get: <T>(path: string, auth = true) => apiRequest<T>(path, { auth }),
|
||||
post: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'POST', body, auth }),
|
||||
put: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'PUT', body, auth }),
|
||||
post: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'POST', body, auth, encryptBody: body instanceof FormData ? false : USE_ENCRYPTION }),
|
||||
put: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'PUT', body, auth, encryptBody: body instanceof FormData ? false : USE_ENCRYPTION }),
|
||||
delete: <T>(path: string, auth = true) => apiRequest<T>(path, { method: 'DELETE', auth }),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user