163 lines
5.1 KiB
TypeScript
163 lines
5.1 KiB
TypeScript
import { API_BASE_URL } from '@/config/api';
|
||
import type {
|
||
SaveListItem,
|
||
SaveDetailResponse,
|
||
CreateSaveFromUrlRequest,
|
||
UpdateSaveRequest,
|
||
SaveResponse,
|
||
User
|
||
} from '@shared-types';
|
||
|
||
// Получить токен из сессии Better Auth
|
||
async function getAuthToken(): Promise<string | null> {
|
||
// Better Auth хранит токен в cookies, но для React Native нужно использовать другой подход
|
||
// Временно возвращаем null, токен будет передаваться через headers в Better Auth клиенте
|
||
return null;
|
||
}
|
||
|
||
async function apiRequest<T>(
|
||
endpoint: string,
|
||
options: RequestInit = {}
|
||
): Promise<T> {
|
||
const token = await getAuthToken();
|
||
|
||
const headers: Record<string, string> = {
|
||
'Content-Type': 'application/json',
|
||
...(options.headers as Record<string, string> || {}),
|
||
};
|
||
|
||
if (token) {
|
||
headers['Authorization'] = `Bearer ${token}`;
|
||
}
|
||
|
||
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
||
...options,
|
||
headers,
|
||
credentials: 'include', // Для cookies Better Auth
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
||
throw new Error(error.error || `HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
|
||
// API для работы с сейвами
|
||
export const savesApi = {
|
||
// Получить все сейвы текущего пользователя
|
||
async getMySaves(): Promise<SaveListItem[]> {
|
||
return apiRequest<SaveListItem[]>('/saves/my');
|
||
},
|
||
|
||
// Получить сейв по ID
|
||
async getSaveById(id: number, shareToken?: string): Promise<SaveDetailResponse> {
|
||
const url = shareToken
|
||
? `/saves/${id}?share=${shareToken}`
|
||
: `/saves/${id}`;
|
||
return apiRequest<SaveDetailResponse>(url);
|
||
},
|
||
|
||
// Создать сейв из URL
|
||
async createFromUrl(data: CreateSaveFromUrlRequest): Promise<SaveResponse> {
|
||
return apiRequest<SaveResponse>('/saves/external', {
|
||
method: 'POST',
|
||
body: JSON.stringify(data),
|
||
});
|
||
},
|
||
|
||
// Загрузить файл
|
||
async uploadFile(
|
||
file: File | { uri: string; type: string; name: string },
|
||
metadata?: {
|
||
name?: string;
|
||
description?: string;
|
||
tags?: string[];
|
||
visibility?: 'public' | 'link';
|
||
}
|
||
): Promise<SaveResponse> {
|
||
const formData = new FormData();
|
||
|
||
// Для React Native используем другой формат
|
||
if ('uri' in file) {
|
||
// React Native
|
||
formData.append('file', {
|
||
uri: file.uri,
|
||
type: file.type,
|
||
name: file.name,
|
||
} as any);
|
||
} else {
|
||
// Web
|
||
formData.append('file', file);
|
||
}
|
||
|
||
if (metadata?.name) formData.append('name', metadata.name);
|
||
if (metadata?.description) formData.append('description', metadata.description);
|
||
if (metadata?.tags) {
|
||
metadata.tags.forEach(tag => formData.append('tags[]', tag));
|
||
}
|
||
if (metadata?.visibility) formData.append('visibility', metadata.visibility);
|
||
|
||
const token = await getAuthToken();
|
||
const headers: Record<string, string> = {};
|
||
if (token) {
|
||
headers['Authorization'] = `Bearer ${token}`;
|
||
}
|
||
// Не устанавливаем Content-Type для FormData - браузер/платформа сделает это автоматически
|
||
|
||
const response = await fetch(`${API_BASE_URL}/saves/upload`, {
|
||
method: 'POST',
|
||
headers,
|
||
body: formData,
|
||
credentials: 'include',
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
||
throw new Error(error.error || `HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
return response.json();
|
||
},
|
||
|
||
// Обновить сейв
|
||
async updateSave(id: number, data: UpdateSaveRequest): Promise<SaveDetailResponse> {
|
||
return apiRequest<SaveDetailResponse>(`/saves/${id}`, {
|
||
method: 'PATCH',
|
||
body: JSON.stringify(data),
|
||
});
|
||
},
|
||
|
||
// Удалить сейв
|
||
async deleteSave(id: number): Promise<{ success: boolean; message: string }> {
|
||
return apiRequest<{ success: boolean; message: string }>(`/saves/${id}`, {
|
||
method: 'DELETE',
|
||
});
|
||
},
|
||
|
||
// Получить URL для скачивания
|
||
getDownloadUrl(id: number, shareToken?: string): string {
|
||
const baseUrl = `${API_BASE_URL}/saves/${id}/download`;
|
||
return shareToken ? `${baseUrl}?share=${shareToken}` : baseUrl;
|
||
},
|
||
|
||
// Получить публичные сейвы пользователя по slug (userId)
|
||
async getPublicSavesByUser(slug: string): Promise<SaveListItem[]> {
|
||
return apiRequest<SaveListItem[]>(`/saves/u/${slug}`);
|
||
},
|
||
};
|
||
|
||
// API для работы с пользователями
|
||
export const usersApi = {
|
||
// Получить пользователя по имени
|
||
async getUserByName(name: string): Promise<User> {
|
||
return apiRequest<User>(`/users/by-name?name=${encodeURIComponent(name)}`);
|
||
},
|
||
|
||
// Получить пользователя по ID
|
||
async getUserById(id: string): Promise<User> {
|
||
return apiRequest<User>(`/users/${id}`);
|
||
},
|
||
};
|