// composables/useApi.js import { ref } from 'vue' export const ADMIN_TOKEN_COOKIE = 'admin_token' export class ApiError extends Error { status: number constructor(message: string, status: number) { super(message) this.status = status } } function normalizeUrl(url: string): string { return url.replace(/\/+$/, '') } export default function useApi() { const config = useRuntimeConfig() const baseUrl = `${normalizeUrl(config.public.apiBaseUrl)}/api` // Generic GET request const get = async (endpoint) => { const res = await fetch(`${baseUrl}${endpoint}`) if (!res.ok) throw new ApiError(`API GET ${endpoint} failed`, res.status) return res.json() } // Generic POST request const post = async (endpoint, payload) => { const res = await fetch(`${baseUrl}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) if (!res.ok) throw new ApiError(`API POST ${endpoint} failed`, res.status) return res.json() } // Authenticated requests (JWT stored in cookie by useAuth) const authHeaders = (): Record => { const token = useCookie(ADMIN_TOKEN_COOKIE).value return token ? { Authorization: `Bearer ${token}` } : {} } const getAuth = async (endpoint) => { const res = await fetch(`${baseUrl}${endpoint}`, { headers: authHeaders() }) if (!res.ok) throw new ApiError(`API GET ${endpoint} failed`, res.status) return res.json() } const postAuth = async (endpoint, payload) => { const res = await fetch(`${baseUrl}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeaders() }, body: JSON.stringify(payload) }) if (!res.ok) throw new ApiError(`API POST ${endpoint} failed`, res.status) return res.json() } return { get, post, getAuth, postAuth } }