Files
mealplanner/frontend/src/composables/useApi.ts
Claude f58782774b feat: complete mealplanner app (backend + frontend + deployment)
.NET 8 backend with Zitadel JWT auth, TheMealDB integration,
weekly meal plan generation, shopping list aggregation.
Vue 3 + Tailwind 4 frontend with dark emerald theme,
manual OIDC PKCE auth, all views implemented.
Multi-stage Dockerfile with nginx reverse proxy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 19:10:10 +00:00

25 lines
1.1 KiB
TypeScript

import { getAccessToken, login } from '../auth'
const BASE = '/api'
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
const token = getAccessToken()
if (!token) { login(window.location.pathname); throw new Error('Not authenticated') }
const res = await fetch(`${BASE}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, ...options.headers },
})
if (res.status === 401) { login(window.location.pathname); throw new Error('Unauthorized') }
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json() as Promise<T>
}
export function useApi() {
return {
get: <T>(path: string) => request<T>(path),
post: <T>(path: string, body?: unknown) => request<T>(path, { method: 'POST', ...(body !== undefined ? { body: JSON.stringify(body) } : {}) }),
put: <T>(path: string, body?: unknown) => request<T>(path, { method: 'PUT', ...(body !== undefined ? { body: JSON.stringify(body) } : {}) }),
del: <T>(path: string) => request<T>(path, { method: 'DELETE' }),
}
}