/** * 表情包管理 API 客户端 */ import { fetchWithAuth } from '@/lib/fetch-with-auth' import type { EmojiListResponse, EmojiDetailResponse, EmojiUpdateRequest, EmojiUpdateResponse, EmojiDeleteResponse, EmojiStatsResponse, } from '@/types/emoji' const API_BASE = '/api/webui/emoji' /** * 获取表情包列表 */ export async function getEmojiList(params: { page?: number page_size?: number search?: string is_registered?: boolean is_banned?: boolean format?: string sort_by?: string sort_order?: 'asc' | 'desc' }): Promise { const query = new URLSearchParams() if (params.page) query.append('page', params.page.toString()) if (params.page_size) query.append('page_size', params.page_size.toString()) if (params.search) query.append('search', params.search) if (params.is_registered !== undefined) query.append('is_registered', params.is_registered.toString()) if (params.is_banned !== undefined) query.append('is_banned', params.is_banned.toString()) if (params.format) query.append('format', params.format) if (params.sort_by) query.append('sort_by', params.sort_by) if (params.sort_order) query.append('sort_order', params.sort_order) const response = await fetchWithAuth(`${API_BASE}/list?${query}`, { }) if (!response.ok) { throw new Error(`获取表情包列表失败: ${response.statusText}`) } return response.json() } /** * 获取表情包详情 */ export async function getEmojiDetail(id: number): Promise { const response = await fetchWithAuth(`${API_BASE}/${id}`, { }) if (!response.ok) { throw new Error(`获取表情包详情失败: ${response.statusText}`) } return response.json() } /** * 更新表情包信息 */ export async function updateEmoji( id: number, data: EmojiUpdateRequest ): Promise { const response = await fetchWithAuth(`${API_BASE}/${id}`, { method: 'PATCH', body: JSON.stringify(data), }) if (!response.ok) { throw new Error(`更新表情包失败: ${response.statusText}`) } return response.json() } /** * 删除表情包 */ export async function deleteEmoji(id: number): Promise { const response = await fetchWithAuth(`${API_BASE}/${id}`, { method: 'DELETE', }) if (!response.ok) { throw new Error(`删除表情包失败: ${response.statusText}`) } return response.json() } /** * 获取表情包统计数据 */ export async function getEmojiStats(): Promise { const response = await fetchWithAuth(`${API_BASE}/stats/summary`, { }) if (!response.ok) { throw new Error(`获取统计数据失败: ${response.statusText}`) } return response.json() } /** * 注册表情包 */ export async function registerEmoji(id: number): Promise { const response = await fetchWithAuth(`${API_BASE}/${id}/register`, { method: 'POST', }) if (!response.ok) { throw new Error(`注册表情包失败: ${response.statusText}`) } return response.json() } /** * 封禁表情包 */ export async function banEmoji(id: number): Promise { const response = await fetchWithAuth(`${API_BASE}/${id}/ban`, { method: 'POST', }) if (!response.ok) { throw new Error(`封禁表情包失败: ${response.statusText}`) } return response.json() } /** * 获取表情包缩略图 URL * 注意:使用 HttpOnly Cookie 进行认证,浏览器会自动携带 * @param id 表情包 ID * @param original 是否获取原图(默认返回压缩后的缩略图) */ export function getEmojiThumbnailUrl(id: number, original: boolean = false): string { if (original) { return `${API_BASE}/${id}/thumbnail?original=true` } return `${API_BASE}/${id}/thumbnail` } /** * 获取表情包原图 URL */ export function getEmojiOriginalUrl(id: number): string { return `${API_BASE}/${id}/thumbnail?original=true` } /** * 批量删除表情包 */ export async function batchDeleteEmojis(emojiIds: number[]): Promise<{ success: boolean message: string deleted_count: number failed_count: number failed_ids: number[] }> { const response = await fetchWithAuth(`${API_BASE}/batch/delete`, { method: 'POST', body: JSON.stringify({ emoji_ids: emojiIds }), }) if (!response.ok) { const error = await response.json() throw new Error(error.detail || '批量删除失败') } return response.json() } /** * 获取表情包上传 URL(供 Uppy 使用) */ export function getEmojiUploadUrl(): string { return `${API_BASE}/upload` } /** * 获取批量上传 URL */ export function getEmojiBatchUploadUrl(): string { return `${API_BASE}/batch/upload` } // ==================== 缩略图缓存管理 API ==================== export interface ThumbnailCacheStatsResponse { success: boolean cache_dir: string total_count: number total_size_mb: number emoji_count: number coverage_percent: number } export interface ThumbnailCleanupResponse { success: boolean message: string cleaned_count: number kept_count: number } export interface ThumbnailPreheatResponse { success: boolean message: string generated_count: number skipped_count: number failed_count: number } /** * 获取缩略图缓存统计信息 */ export async function getThumbnailCacheStats(): Promise { const response = await fetchWithAuth(`${API_BASE}/thumbnail-cache/stats`, {}) if (!response.ok) { throw new Error(`获取缩略图缓存统计失败: ${response.statusText}`) } return response.json() } /** * 清理孤立的缩略图缓存 */ export async function cleanupThumbnailCache(): Promise { const response = await fetchWithAuth(`${API_BASE}/thumbnail-cache/cleanup`, { method: 'POST', }) if (!response.ok) { throw new Error(`清理缩略图缓存失败: ${response.statusText}`) } return response.json() } /** * 预热缩略图缓存 * @param limit 最多预热数量 (1-1000) */ export async function preheatThumbnailCache(limit: number = 100): Promise { const response = await fetchWithAuth(`${API_BASE}/thumbnail-cache/preheat?limit=${limit}`, { method: 'POST', }) if (!response.ok) { throw new Error(`预热缩略图缓存失败: ${response.statusText}`) } return response.json() } /** * 清空所有缩略图缓存 */ export async function clearAllThumbnailCache(): Promise { const response = await fetchWithAuth(`${API_BASE}/thumbnail-cache/clear`, { method: 'DELETE', }) if (!response.ok) { throw new Error(`清空缩略图缓存失败: ${response.statusText}`) } return response.json() }