const API_BASE = '/api/v1'; let _accessToken = null; let _onUnauthorized = null; export function setAccessToken(token) { _accessToken = token; } export function getAccessToken() { return _accessToken; } export function setOnUnauthorized(fn) { _onUnauthorized = fn; } async function request(method, path, body = null, opts = {}) { const headers = { 'Content-Type': 'application/json' }; if (_accessToken) headers['Authorization'] = `Bearer ${_accessToken}`; const res = await fetch(`${API_BASE}${path}`, { method, headers, credentials: 'include', // send cookies (refresh token) body: body ? JSON.stringify(body) : null, ...opts, }); if (res.status === 401 && !path.includes('/auth/')) { // Try to refresh const refreshed = await refreshToken(); if (refreshed) { headers['Authorization'] = `Bearer ${_accessToken}`; const retry = await fetch(`${API_BASE}${path}`, { method, headers, credentials: 'include', body: body ? JSON.stringify(body) : null, }); if (retry.ok) return retry.json(); } _onUnauthorized?.(); throw new Error('Unauthorized'); } if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error || `HTTP ${res.status}`); } return res.json(); } async function refreshToken() { try { const res = await fetch(`${API_BASE}/auth/refresh`, { method: 'POST', credentials: 'include', }); if (!res.ok) return false; const data = await res.json(); _accessToken = data.accessToken; return true; } catch { return false; } } // Auth export const auth = { register: (email, username, password) => request('POST', '/auth/register', { email, username, password }), login: (email, password) => request('POST', '/auth/login', { email, password }), logout: () => request('POST', '/auth/logout'), refresh: refreshToken, }; // Users export const users = { me: () => request('GET', '/users/me'), update: (data) => request('PATCH', '/users/me', data), }; // Workshop export const workshop = { browse: (params = '') => request('GET', `/workshop${params ? '?' + params : ''}`), get: (id) => request('GET', `/workshop/${id}`), share: (data) => request('POST', '/workshop', data), remove: (id) => request('DELETE', `/workshop/${id}`), like: (id) => request('POST', `/workshop/${id}/like`), unlike: (id) => request('DELETE', `/workshop/${id}/like`), report: (id) => request('POST', `/workshop/${id}/report`), }; // Admin Levels export const levels = { list: () => request('GET', '/admin/levels'), create: (data) => request('POST', '/admin/levels', data), update: (id, data) => request('PATCH', `/admin/levels/${id}`, data), remove: (id) => request('DELETE', `/admin/levels/${id}`), importPatch: (id, patchData) => request('POST', `/admin/levels/${id}/import-patch`, patchData), }; // Admin export const admin = { stats: () => request('GET', '/admin/stats'), users: (params = '') => request('GET', `/admin/users${params ? '?' + params : ''}`), updateUser: (id, data) => request('PATCH', `/admin/users/${id}`, data), patches: (params = '') => request('GET', `/admin/patches${params ? '?' + params : ''}`), updatePatch: (id, data) => request('PATCH', `/admin/patches/${id}`, data), };