/** * AKSRT Admin API Client * * Communicates with the WordPress REST API at /wp-json/aksrt/v1. * Methods are organized into domain modules while the client keeps the shared * request pipeline and schema-domain normalization contract. */ import { apiMethods } from './modules/index.js' const API_ROOT = window.aksrtAdminData?.apiUrl || '/wp-json/aksrt/v1' const NONCE = window.aksrtAdminData?.nonce || '' function normalizeDomainKey(domain) { return String(domain || 'global').trim().replace(/-/g, '_') } class ApiClient { constructor() { this.baseUrl = API_ROOT } normalizeDomainKey(domain) { return normalizeDomainKey(domain) } async request(endpointOrConfig, options = {}) { // Dual-signature support: // request('/forum/overview') // request({ url: '/aksrt/v1/forum/topics', method: 'GET', params: { limit: 20 } }) // request({ url: '/aksrt/v1/verify/review', method: 'POST', data: { user_id: 1 } }) let endpoint, method, headers, params, body, rest if (typeof endpointOrConfig === 'object' && endpointOrConfig !== null) { const cfg = endpointOrConfig endpoint = cfg.url || cfg.endpoint || '' method = cfg.method || 'GET' params = cfg.params body = cfg.data headers = cfg.headers rest = { ...cfg } delete rest.url; delete rest.endpoint; delete rest.method delete rest.params; delete rest.data; delete rest.headers } else { endpoint = endpointOrConfig method = options.method || 'GET' params = options.params body = options.body !== undefined ? options.body : options.data headers = options.headers rest = { ...options } delete rest.method; delete rest.params; delete rest.body delete rest.data; delete rest.headers } if (endpoint.startsWith('/wp-json/aksrt/v1')) { endpoint = endpoint.replace('/wp-json/aksrt/v1', '') || '/' } else if (endpoint.startsWith('/aksrt/v1')) { endpoint = endpoint.replace('/aksrt/v1', '') || '/' } let url = `${this.baseUrl}${endpoint}` if (params && typeof params === 'object') { const qs = new URLSearchParams(params).toString() if (qs) url += (url.includes('?') ? '&' : '?') + qs } let finalBody = body if (body !== undefined && typeof body === 'object' && !(body instanceof FormData)) { finalBody = JSON.stringify(body) } const config = { method, headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': NONCE, ...headers, }, ...rest, } if (finalBody !== undefined && method !== 'GET') { config.body = finalBody } try { const response = await fetch(url, config) const text = await response.text() let data = {} try { data = text ? JSON.parse(text) : {} } catch { throw new Error( `REST 返回非 JSON,endpoint=${endpoint},status=${response.status},body=${text.slice(0, 160)}` ) } if (!response.ok || data.success === false) { const msg = data.message || data.data?.message || `HTTP ${response.status}` if (response.status === 403 || response.status === 401) { console.warn(`[API Auth] ${endpoint}: ${msg} — 可能需要刷新页面重新登录`) } throw new Error(msg) } return data } catch (error) { console.error(`[API Error] ${endpoint}:`, error) throw error } } } Object.assign(ApiClient.prototype, apiMethods) export { normalizeDomainKey } export default new ApiClient()