Files
dashfront/src/api/index.js
2026-06-23 11:04:28 +02:00

70 lines
1.6 KiB
JavaScript

// Single source of truth for all backend communication.
async function http(path, { method = 'GET', body } = {}) {
const headers = { 'Content-Type': 'application/json' };
const res = await fetch(path, {
method,
headers,
credentials: 'include',
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
let message = res.statusText;
let data;
try {
data = await res.json();
message = data.error || data.message || message;
} catch {
message = (await res.text().catch(() => message)) || message;
}
const err = new Error(message || `Request failed (${res.status})`);
err.status = res.status;
err.data = data;
throw err;
}
if (res.status === 204) return null;
return res.json();
}
export const auth = {
async me() {
try {
const data = await http('/api/auth/me');
return {
user: data.user ?? null,
googleClientId: data.googleClientId ?? null,
};
} catch (err) {
if (err.status === 401 || err.status === 403) {
return {
user: null,
googleClientId: err.data?.googleClientId ?? null,
forbidden: err.status === 403,
message: err.message,
};
}
throw err;
}
},
async loginWithGoogle(idToken) {
const data = await http('/api/auth/login', {
method: 'POST',
body: { idToken },
});
return { user: data.user };
},
async logout() {
try {
await http('/api/auth/logout', { method: 'POST' });
} catch (err) {
if (err.status !== 401) throw err;
}
},
};
export default { auth };