type ApiPayload = { data: T; error?: { code: string; message: string } }; async function opsFetch(path: string, init?: RequestInit): Promise { const response = await fetch(path, { ...init, credentials: 'include', headers: { Accept: 'application/json', ...(init?.body ? { 'Content-Type': 'application/json' } : {}), ...(init?.headers ?? {}), }, }); const payload = (await response.json().catch(() => ({}))) as ApiPayload & { message?: string; }; if (!response.ok) { throw new Error(payload?.error?.message ?? payload?.message ?? `请求失败 (${response.status})`); } return payload.data; } export type ReviewPost = { id: string; title: string; summary: string; cover_url: string; author: { display_name: string; slug: string }; category: { name: string; slug: string; icon: string }; published_at: string; sla_warning: boolean; }; export async function fetchReviewQueue(query = 'status=pending_review') { const suffix = query.startsWith('?') ? query : query ? `?${query}` : ''; return opsFetch<{ posts: ReviewPost[]; next_cursor: string | null; has_more: boolean }>( `/api/ops/v1/review/queue${suffix}`, ); } export async function reviewPost(id: string, action: 'approve' | 'reject' | 'hide', reason?: string) { return opsFetch<{ post: { id: string; status: string } }>(`/api/ops/v1/review/posts/${id}`, { method: 'POST', body: JSON.stringify({ action, reason }), }); } export async function batchReviewPosts(postIds: string[], action: 'approve' | 'reject' | 'hide') { return opsFetch<{ posts: Array<{ id: string; status: string }> }>('/api/ops/v1/review/batch', { method: 'POST', body: JSON.stringify({ post_ids: postIds, action }), }); } export async function fetchReports() { return opsFetch<{ reports: Array<{ id: string; target_type: string; target_id: string; reason: string; detail: string; target_report_count: number; created_at: string; }>; }>('/api/ops/v1/reports'); } export async function processReport(id: string, action: 'dismiss' | 'hide_post', actionTaken = '') { return opsFetch<{ report: { id: string; status: string } }>(`/api/ops/v1/reports/${id}/process`, { method: 'POST', body: JSON.stringify({ action, action_taken: actionTaken }), }); } export async function fetchFeatured() { return opsFetch<{ items: Array<{ id: string; post_id: string; position: string; title: string; author: string }>; }>('/api/ops/v1/featured'); } export async function setFeatured(body: { post_id: string; position: string; sort_order?: number; expires_at?: string | null; }) { return opsFetch('/api/ops/v1/featured', { method: 'POST', body: JSON.stringify(body) }); } export async function removeFeatured(id: string) { return opsFetch(`/api/ops/v1/featured/${id}`, { method: 'DELETE' }); } export async function fetchAnalytics() { const data = await opsFetch<{ today: { new_posts: number; plaza_signups: number; pending_review: number }; yesterday: { new_posts: number }; categories: Array<{ name: string; count: number }>; top_creators: Array<{ slug: string; display_name: string; post_count: number; total_likes: number }>; daily_posts: Array<{ day: string; count: number }>; }>('/api/ops/v1/analytics/overview'); return { today: data.today ?? { new_posts: 0, plaza_signups: 0, pending_review: 0 }, yesterday: data.yesterday ?? { new_posts: 0 }, categories: Array.isArray(data.categories) ? data.categories : [], top_creators: Array.isArray(data.top_creators) ? data.top_creators : [], daily_posts: Array.isArray(data.daily_posts) ? data.daily_posts : [], }; } export async function fetchCreators(keyword = '') { const query = keyword ? `?keyword=${encodeURIComponent(keyword)}` : ''; return opsFetch<{ creators: Array<{ user_id: string; slug: string; display_name: string; post_count: number; follower_count: number; verified: boolean; post_banned: boolean; comment_banned: boolean; }>; }>(`/api/ops/v1/creators${query}`); } export async function updateCreator( userId: string, patch: { verified?: boolean; post_banned?: boolean; comment_banned?: boolean }, ) { return opsFetch(`/api/ops/v1/creators/${userId}`, { method: 'PATCH', body: JSON.stringify(patch), }); } export async function fetchAuthStatus() { const response = await fetch('/auth/status', { credentials: 'include' }); return response.json() as Promise<{ authenticated: boolean; user?: { displayName: string } }>; }