| import axios from 'axios'; |
| import type { SearchResponse } from '@/types'; |
|
|
| const api = axios.create({ |
| baseURL: '/api', |
| timeout: 10000 |
| }); |
|
|
| |
| api.interceptors.request.use( |
| (config) => { |
| const token = localStorage.getItem('auth_token'); |
| if (token) { |
| config.headers.Authorization = `Bearer ${token}`; |
| } |
| return config; |
| }, |
| (error) => { |
| return Promise.reject(error); |
| } |
| ); |
|
|
| |
| api.interceptors.response.use( |
| (response) => response, |
| (error) => { |
| if (error.response?.status === 401) { |
| |
| localStorage.removeItem('auth_token'); |
| localStorage.removeItem('auth_username'); |
| |
| |
| window.dispatchEvent(new CustomEvent('auth:required')); |
| } |
| return Promise.reject(error); |
| } |
| ); |
|
|
| |
| export interface SearchParams { |
| kw: string; |
| refresh?: boolean; |
| res?: 'all' | 'results' | 'merge'; |
| src?: 'all' | 'tg' | 'plugin'; |
| plugins?: string; |
| ext?: string; |
| } |
|
|
| |
| interface ApiResponse<T> { |
| code: number; |
| message: string; |
| data: T; |
| } |
|
|
| |
| export interface HealthStatus { |
| status: string; |
| plugins_enabled: boolean; |
| plugin_count: number; |
| plugins: string[]; |
| channels: string[]; |
| auth_enabled?: boolean; |
| } |
|
|
| |
| export interface LoginParams { |
| username: string; |
| password: string; |
| } |
|
|
| |
| export interface LoginResponse { |
| token: string; |
| expires_at: number; |
| username: string; |
| } |
|
|
| |
| export interface AuthStatus { |
| enabled: boolean; |
| authenticated: boolean; |
| } |
|
|
| |
| export const getHealth = async (): Promise<HealthStatus> => { |
| try { |
| const response = await api.get<HealthStatus>('/health'); |
| return response.data; |
| } catch (error) { |
| console.error('获取健康状态失败:', error); |
| |
| return getMockHealthData(); |
| } |
| }; |
|
|
| |
| const getMockHealthData = (): HealthStatus => { |
| return { |
| status: "ok", |
| plugins_enabled: true, |
| plugin_count: 6, |
| plugins: ["pansearch", "hdr4k", "shandian", "muou", "duoduo", "labi"], |
| channels: ["tgsearchers3", "SharePanBaidu", "yunpanxunlei", "tianyifc", "BaiduCloudDisk"] |
| }; |
| }; |
|
|
| |
| export const search = async (params: SearchParams): Promise<SearchResponse> => { |
| |
| const searchParams = { |
| ...params, |
| ext: JSON.stringify({ referer: "https://dm.xueximeng.com" }) |
| }; |
| |
| |
| try { |
| const response = await api.get<ApiResponse<SearchResponse>>('/search', { params: searchParams }); |
| |
| |
| |
| if (response.data && response.data.data) { |
| |
| return response.data.data; |
| } |
| |
| |
| if (response.data && response.data.total !== undefined && response.data.merged_by_type) { |
| return response.data as unknown as SearchResponse; |
| } |
| |
| |
| console.warn('API响应格式不匹配,使用模拟数据'); |
| return getMockData(); |
| } catch (error) { |
| console.error('API错误:', error); |
| |
| |
| |
| return getMockData(); |
| } |
| }; |
|
|
| |
| const getMockData = (): SearchResponse => { |
| return { |
| total: 15, |
| results: [ |
| { |
| message_id: "12345", |
| unique_id: "channel-12345", |
| channel: "tgsearchers3", |
| datetime: "2023-06-10T14:23:45Z", |
| title: "速度与激情全集1-10", |
| content: "速度与激情系列全集,1080P高清...", |
| links: [ |
| { |
| type: "baidu", |
| url: "https://pan.baidu.com/s/1abcdef", |
| password: "1234" |
| } |
| ], |
| tags: ["电影", "合集"] |
| } |
| ], |
| merged_by_type: { |
| baidu: [ |
| { |
| url: "https://pan.baidu.com/s/1abcdef", |
| password: "1234", |
| note: "速度与激情全集1-10", |
| datetime: "2023-06-10T14:23:45Z", |
| source: "tgsearchers3" |
| }, |
| { |
| url: "https://pan.baidu.com/s/1ghijkl", |
| password: "5678", |
| note: "速度与激情9", |
| datetime: "2023-05-15T10:20:30Z", |
| source: "SharePanBaidu" |
| } |
| ], |
| aliyun: [ |
| { |
| url: "https://www.aliyundrive.com/s/abcdef", |
| note: "速度与激情系列合集", |
| datetime: "2023-07-01T08:15:20Z", |
| source: "yunpanxunlei" |
| } |
| ], |
| "115": [ |
| { |
| url: "https://115.com/s/abcdefg", |
| password: "abc123", |
| note: "速度与激情1-10全集高清资源", |
| datetime: "2023-04-22T16:45:12Z", |
| source: "pansearch插件" |
| } |
| ] |
| } |
| }; |
| }; |
|
|
| |
| export const login = async (params: LoginParams): Promise<LoginResponse> => { |
| const response = await api.post<LoginResponse>('/auth/login', params); |
| return response.data; |
| }; |
|
|
| |
| export const verifyToken = async (): Promise<boolean> => { |
| try { |
| await api.post('/auth/verify'); |
| return true; |
| } catch { |
| return false; |
| } |
| }; |
|
|
| |
| export const logout = async (): Promise<void> => { |
| try { |
| await api.post('/auth/logout'); |
| } finally { |
| localStorage.removeItem('auth_token'); |
| localStorage.removeItem('auth_username'); |
| } |
| }; |
|
|
| |
| export const checkAuthStatus = async (): Promise<AuthStatus> => { |
| try { |
| const health = await getHealth(); |
| const authEnabled = health.auth_enabled || false; |
| const token = localStorage.getItem('auth_token'); |
| |
| if (!authEnabled) { |
| return { enabled: false, authenticated: true }; |
| } |
| |
| if (!token) { |
| return { enabled: true, authenticated: false }; |
| } |
| |
| const valid = await verifyToken(); |
| return { enabled: true, authenticated: valid }; |
| } catch { |
| return { enabled: false, authenticated: true }; |
| } |
| }; |
|
|
| export default api; |