初始化
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios';
|
||||
import type { ApiResponse } from '@/types';
|
||||
|
||||
// API配置
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
|
||||
|
||||
class ApiService {
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor() {
|
||||
this.client = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
this.client.interceptors.request.use(
|
||||
(config) => {
|
||||
// 可以在这里添加认证token等
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
this.client.interceptors.response.use(
|
||||
(response: AxiosResponse<ApiResponse>) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
// 统一错误处理
|
||||
if (error.response) {
|
||||
const { status, data } = error.response;
|
||||
console.error(`API Error ${status}:`, data);
|
||||
} else if (error.request) {
|
||||
console.error('Network Error:', error.request);
|
||||
} else {
|
||||
console.error('Request Error:', error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求
|
||||
*/
|
||||
async get<T = any>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
||||
const response = await this.client.get<ApiResponse<T>>(url, config);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求
|
||||
*/
|
||||
async post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
||||
const response = await this.client.post<ApiResponse<T>>(url, data, config);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT请求
|
||||
*/
|
||||
async put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
||||
const response = await this.client.put<ApiResponse<T>>(url, data, config);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE请求
|
||||
*/
|
||||
async delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
||||
const response = await this.client.delete<ApiResponse<T>>(url, config);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 健康检查
|
||||
*/
|
||||
async healthCheck(): Promise<ApiResponse> {
|
||||
return this.get('/health');
|
||||
}
|
||||
}
|
||||
|
||||
export const apiService = new ApiService();
|
||||
export default apiService;
|
||||
@@ -0,0 +1,81 @@
|
||||
import apiService from './api';
|
||||
import type { ApiResponse, Artist, User } from '@/types';
|
||||
|
||||
export interface ArtistArtworksOptions {
|
||||
type?: 'art' | 'manga' | 'novel';
|
||||
filter?: 'for_ios' | 'for_android';
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface ArtistFollowingOptions {
|
||||
restrict?: 'public' | 'private';
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface ArtistFollowersOptions {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
class ArtistService {
|
||||
/**
|
||||
* 获取作者信息
|
||||
*/
|
||||
async getArtistInfo(id: number): Promise<ApiResponse<Artist>> {
|
||||
return apiService.get<Artist>(`/api/artist/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取作者作品列表
|
||||
*/
|
||||
async getArtistArtworks(id: number, options: ArtistArtworksOptions = {}): Promise<ApiResponse<{ artworks: any[]; next_url?: string; total: number }>> {
|
||||
const params = new URLSearchParams();
|
||||
if (options.type) params.append('type', options.type);
|
||||
if (options.filter) params.append('filter', options.filter);
|
||||
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
||||
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
||||
|
||||
const query = params.toString();
|
||||
const url = query ? `/api/artist/${id}/artworks?${query}` : `/api/artist/${id}/artworks`;
|
||||
return apiService.get<{ artworks: any[]; next_url?: string; total: number }>(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取作者关注列表
|
||||
*/
|
||||
async getArtistFollowing(id: number, options: ArtistFollowingOptions = {}): Promise<ApiResponse<{ users: User[]; next_url?: string; total: number }>> {
|
||||
const params = new URLSearchParams();
|
||||
if (options.restrict) params.append('restrict', options.restrict);
|
||||
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
||||
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
||||
|
||||
const query = params.toString();
|
||||
const url = query ? `/api/artist/${id}/following?${query}` : `/api/artist/${id}/following`;
|
||||
return apiService.get<{ users: User[]; next_url?: string; total: number }>(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取作者粉丝列表
|
||||
*/
|
||||
async getArtistFollowers(id: number, options: ArtistFollowersOptions = {}): Promise<ApiResponse<{ users: User[]; next_url?: string; total: number }>> {
|
||||
const params = new URLSearchParams();
|
||||
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
||||
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
||||
|
||||
const query = params.toString();
|
||||
const url = query ? `/api/artist/${id}/followers?${query}` : `/api/artist/${id}/followers`;
|
||||
return apiService.get<{ users: User[]; next_url?: string; total: number }>(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关注/取消关注作者
|
||||
*/
|
||||
async followArtist(id: number, action: 'follow' | 'unfollow'): Promise<ApiResponse> {
|
||||
return apiService.post(`/api/artist/${id}/follow`, { action });
|
||||
}
|
||||
}
|
||||
|
||||
export const artistService = new ArtistService();
|
||||
export default artistService;
|
||||
@@ -0,0 +1,71 @@
|
||||
import apiService from './api';
|
||||
import type { ApiResponse, Artwork, SearchParams, PaginatedResponse } from '@/types';
|
||||
|
||||
export interface ArtworkDetailOptions {
|
||||
include_user?: boolean;
|
||||
include_series?: boolean;
|
||||
}
|
||||
|
||||
export interface ArtworkImagesResponse {
|
||||
artwork_id: number;
|
||||
total_pages: number;
|
||||
images: Array<{
|
||||
page: number;
|
||||
original: string;
|
||||
large: string;
|
||||
medium: string;
|
||||
square_medium: string;
|
||||
}>;
|
||||
selected_size: string;
|
||||
}
|
||||
|
||||
class ArtworkService {
|
||||
/**
|
||||
* 获取作品详情
|
||||
*/
|
||||
async getArtworkDetail(id: number, options: ArtworkDetailOptions = {}): Promise<ApiResponse<Artwork>> {
|
||||
const params = new URLSearchParams();
|
||||
if (options.include_user !== undefined) {
|
||||
params.append('include_user', options.include_user.toString());
|
||||
}
|
||||
if (options.include_series !== undefined) {
|
||||
params.append('include_series', options.include_series.toString());
|
||||
}
|
||||
|
||||
const query = params.toString();
|
||||
const url = query ? `/api/artwork/${id}?${query}` : `/api/artwork/${id}`;
|
||||
return apiService.get<Artwork>(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取作品预览信息
|
||||
*/
|
||||
async getArtworkPreview(id: number): Promise<ApiResponse<Artwork>> {
|
||||
return apiService.get<Artwork>(`/api/artwork/${id}/preview`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取作品图片URL
|
||||
*/
|
||||
async getArtworkImages(id: number, size: string = 'medium'): Promise<ApiResponse<ArtworkImagesResponse>> {
|
||||
return apiService.get<ArtworkImagesResponse>(`/api/artwork/${id}/images?size=${size}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索作品
|
||||
*/
|
||||
async searchArtworks(params: SearchParams): Promise<ApiResponse<{ artworks: Artwork[]; next_url?: string; total: number }>> {
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.append('keyword', params.keyword);
|
||||
if (params.type) queryParams.append('type', params.type);
|
||||
if (params.sort) queryParams.append('sort', params.sort);
|
||||
if (params.duration) queryParams.append('duration', params.duration);
|
||||
if (params.offset !== undefined) queryParams.append('offset', params.offset.toString());
|
||||
if (params.limit !== undefined) queryParams.append('limit', params.limit.toString());
|
||||
|
||||
return apiService.get<{ artworks: Artwork[]; next_url?: string; total: number }>(`/api/artwork/search?${queryParams.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const artworkService = new ArtworkService();
|
||||
export default artworkService;
|
||||
@@ -0,0 +1,59 @@
|
||||
import apiService from './api';
|
||||
import type { ApiResponse, LoginStatus } from '@/types';
|
||||
|
||||
export interface LoginUrlResponse {
|
||||
login_url: string;
|
||||
code_verifier: string;
|
||||
}
|
||||
|
||||
export interface LoginCallbackRequest {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface LoginCallbackResponse {
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
account: string;
|
||||
};
|
||||
}
|
||||
|
||||
class AuthService {
|
||||
/**
|
||||
* 获取登录状态
|
||||
*/
|
||||
async getLoginStatus(): Promise<ApiResponse<LoginStatus>> {
|
||||
return apiService.get<LoginStatus>('/api/auth/status');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录URL
|
||||
*/
|
||||
async getLoginUrl(): Promise<ApiResponse<LoginUrlResponse>> {
|
||||
return apiService.get<LoginUrlResponse>('/api/auth/login-url');
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登录回调
|
||||
*/
|
||||
async handleLoginCallback(code: string): Promise<ApiResponse<LoginCallbackResponse>> {
|
||||
return apiService.post<LoginCallbackResponse>('/api/auth/callback', { code });
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新登录
|
||||
*/
|
||||
async relogin(): Promise<ApiResponse> {
|
||||
return apiService.post('/api/auth/relogin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出
|
||||
*/
|
||||
async logout(): Promise<ApiResponse> {
|
||||
return apiService.post('/api/auth/logout');
|
||||
}
|
||||
}
|
||||
|
||||
export const authService = new AuthService();
|
||||
export default authService;
|
||||
@@ -0,0 +1,66 @@
|
||||
import apiService from './api';
|
||||
import type { ApiResponse, DownloadTask, DownloadParams } from '@/types';
|
||||
|
||||
export interface DownloadArtworkRequest extends DownloadParams {
|
||||
size?: 'original' | 'large' | 'medium' | 'square_medium';
|
||||
quality?: 'high' | 'medium' | 'low';
|
||||
format?: 'auto' | 'jpg' | 'png';
|
||||
}
|
||||
|
||||
export interface DownloadMultipleRequest extends DownloadParams {
|
||||
artworkIds: number[];
|
||||
concurrent?: number;
|
||||
}
|
||||
|
||||
export interface DownloadArtistRequest extends DownloadParams {
|
||||
type?: 'art' | 'manga' | 'novel';
|
||||
filter?: 'for_ios' | 'for_android';
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
class DownloadService {
|
||||
/**
|
||||
* 下载单个作品
|
||||
*/
|
||||
async downloadArtwork(id: number, params: DownloadArtworkRequest = {}): Promise<ApiResponse<any>> {
|
||||
return apiService.post(`/api/download/artwork/${id}`, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下载作品
|
||||
*/
|
||||
async downloadMultipleArtworks(params: DownloadMultipleRequest): Promise<ApiResponse<any>> {
|
||||
return apiService.post('/api/download/artworks', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载作者作品
|
||||
*/
|
||||
async downloadArtistArtworks(id: number, params: DownloadArtistRequest = {}): Promise<ApiResponse<any>> {
|
||||
return apiService.post(`/api/download/artist/${id}`, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载进度
|
||||
*/
|
||||
async getDownloadProgress(taskId: string): Promise<ApiResponse<DownloadTask>> {
|
||||
return apiService.get<DownloadTask>(`/api/download/progress/${taskId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消下载任务
|
||||
*/
|
||||
async cancelDownload(taskId: string): Promise<ApiResponse> {
|
||||
return apiService.delete(`/api/download/cancel/${taskId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载历史
|
||||
*/
|
||||
async getDownloadHistory(offset: number = 0, limit: number = 20): Promise<ApiResponse<{ tasks: DownloadTask[]; total: number; offset: number; limit: number }>> {
|
||||
return apiService.get<{ tasks: DownloadTask[]; total: number; offset: number; limit: number }>(`/api/download/history?offset=${offset}&limit=${limit}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const downloadService = new DownloadService();
|
||||
export default downloadService;
|
||||
Reference in New Issue
Block a user