import { getEnvValue, getConfig } from '../config/env.js' const API_BASE_URL = getEnvValue('API_BASE_URL') const AUTH_PATH_PREFIX = '/auth/' const isAuthPath = (path = '') => path.startsWith(AUTH_PATH_PREFIX) const createRequestError = (message, meta = {}) => { const error = new Error(message || 'Request failed') Object.assign(error, meta) return error } const clearAuthStorage = () => { uni.removeStorageSync('access_token') uni.removeStorageSync('refresh_token') } const logApi = (level, label, payload, force = false) => { const debug = getEnvValue('DEBUG') if (!debug && !force) return const logger = level === 'error' ? console.error : console.log logger(`[API] ${label}`, payload) } export const logRuntimeEnv = (source = 'startup') => { const config = getConfig() console.log('[ENV] runtime', { source, rawEnv: import.meta.env.VITE_APP_ENV, mode: import.meta.env.MODE, apiBaseUrl: config.API_BASE_URL, wsUrl: config.WS_URL, debug: config.DEBUG, platform: typeof uni !== 'undefined' ? uni.getSystemInfoSync?.()?.platform : 'unknown' }) } const getHeaders = () => { const token = uni.getStorageSync('access_token') const headers = { 'Content-Type': 'application/json' } if (token) { headers.Authorization = `Bearer ${token}` } return headers } const request = (options) => { const method = options.method || 'GET' const fullUrl = `${API_BASE_URL}${options.url}` const forceLog = isAuthPath(options.url) logApi('log', 'request', { method, url: fullUrl, path: options.url, env: import.meta.env.VITE_APP_ENV || import.meta.env.MODE }, forceLog) return new Promise((resolve, reject) => { uni.request({ url: fullUrl, method, data: options.data, header: getHeaders(), timeout: 30000, success: (res) => { const { data, statusCode } = res const success = statusCode >= 200 && statusCode < 300 && (data?.code === 200 || data?.code === 0) logApi('log', 'response', { path: options.url, status: statusCode, code: data?.code, success }, forceLog || statusCode >= 400) if (statusCode === 401) { if (!forceLog) { clearAuthStorage() uni.reLaunch({ url: '/pages/login/index' }) } reject(createRequestError('Login expired, please login again', { statusCode, code: data?.code, path: options.url, isAuthRejected: true })) return } if (success) { resolve(data) return } reject(createRequestError(data?.message || 'Request failed', { statusCode, code: data?.code, path: options.url, response: data, isAuthRejected: statusCode === 403 || data?.code === 401 || data?.code === 403 })) }, fail: (err) => { logApi('error', 'fail', { path: options.url, url: fullUrl, error: err.errMsg, env: import.meta.env.VITE_APP_ENV || import.meta.env.MODE }, true) reject(createRequestError(err.errMsg || 'Network request failed', { path: options.url, isNetworkError: true, originalError: err })) } }) }) } export const get = (url, params = {}) => { const queryString = Object.keys(params) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&') const fullUrl = queryString ? `${url}?${queryString}` : url return request({ url: fullUrl, method: 'GET' }) } export const post = (url, data = {}) => { return request({ url, method: 'POST', data }) } export const put = (url, data = {}) => { return request({ url, method: 'PUT', data }) } export const del = (url, params = {}) => { const queryString = Object.keys(params) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&') const fullUrl = queryString ? `${url}?${queryString}` : url return request({ url: fullUrl, method: 'DELETE' }) } export default { get, post, put, del, logRuntimeEnv }