feat(frontend): add apiFetch wrapper for automatic auth token injection

Centralizes auth token management into a reusable apiFetch() helper and getAuthHeaders() utility, eliminating inline Bearer token logic across all frontend files.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-07-06 19:21:34 +01:00
co-authored by Sisyphus
parent e831953e5b
commit 92124158bf
48 changed files with 530 additions and 1190 deletions
@@ -11,6 +11,7 @@
import CardInput from '$lib/components/payments/CardInput.svelte';
import PolicyPopover from '$lib/components/ui/policyPopover.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { apiFetch } from '$lib/utils/api';
const LOYALTY_DISCOUNT_RATE = 0.1;
@@ -351,11 +352,8 @@
async function acquireLock() {
try {
const response = await fetch(`/api/bookings/${booking.id}/payment-lock`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authStore.currentToken}`
}
const response = await apiFetch(`/api/bookings/${booking.id}/payment-lock`, {
method: 'POST'
});
if (response.ok) {
lockAcquired = true;
@@ -370,11 +368,8 @@
lockAcquired = false;
clearLockIntervals();
try {
await fetch(`/api/bookings/${booking.id}/payment-lock`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${authStore.currentToken}`
}
await apiFetch(`/api/bookings/${booking.id}/payment-lock`, {
method: 'DELETE'
});
} catch (_err) {
console.error('Failed to release payment lock:', _err);
@@ -390,11 +385,8 @@
function startRenewal() {
lockInterval = setInterval(async () => {
try {
const response = await fetch(`/api/bookings/${booking.id}/payment-lock`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authStore.currentToken}`
}
const response = await apiFetch(`/api/bookings/${booking.id}/payment-lock`, {
method: 'POST'
});
if (response.ok) {
lockTimer = 300;
@@ -428,11 +420,7 @@
if (!authStore.isAuthenticated) return;
paymentMethodsLoading = true;
try {
const response = await fetch('/api/user/payment-methods', {
headers: {
Authorization: `Bearer ${authStore.currentToken}`
}
});
const response = await apiFetch('/api/user/payment-methods');
if (response.ok) {
paymentMethods = await response.json();
}
@@ -446,11 +434,7 @@
async function fetchLoyaltyData() {
if (!authStore.isAuthenticated) return;
try {
const response = await fetch('/api/user/loyalty', {
headers: {
Authorization: `Bearer ${authStore.currentToken}`
}
});
const response = await apiFetch('/api/user/loyalty');
if (response.ok) {
const data = await response.json();
stamps = data.stamps ?? 0;
@@ -498,12 +482,9 @@
// Apply loyalty redemption before payment
if (useLoyalty) {
try {
const redemptionResponse = await fetch(`/api/bookings/${booking.id}/apply-redemption`, {
const redemptionResponse = await apiFetch(`/api/bookings/${booking.id}/apply-redemption`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authStore.currentToken}`
}
headers: { 'Content-Type': 'application/json' }
});
if (!redemptionResponse.ok) {
const errData = await redemptionResponse.text();
@@ -537,12 +518,9 @@
const idempotencyKey = generateIdempotencyKey();
try {
const response = await fetch(`/api/bookings/${booking.id}/payment`, {
const response = await apiFetch(`/api/bookings/${booking.id}/payment`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authStore.currentToken}`
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: amountCents,
payment_type: paymentType,
@@ -640,9 +618,7 @@
// Fetch eligible campaign discounts
try {
const resp = await fetch(`/api/bookings/${booking.id}/discount-preview`, {
headers: { Authorization: `Bearer ${authStore.currentToken}` }
});
const resp = await apiFetch(`/api/bookings/${booking.id}/discount-preview`);
if (resp.ok) {
discountPreview = await resp.json();
}