97 lines
2.9 KiB
JavaScript
97 lines
2.9 KiB
JavaScript
import { API_BASE } from '../lib/apiBase';
|
|
|
|
/** Frontend-only fallback when the Node API is not reachable on the host. */
|
|
const STATIC_USER = 'admin';
|
|
const STATIC_PASS = 'admin123';
|
|
export const STATIC_TOKEN = 'static-admin-session';
|
|
|
|
export function getToken() {
|
|
return localStorage.getItem('admin_token');
|
|
}
|
|
|
|
export function setToken(token) {
|
|
localStorage.setItem('admin_token', token);
|
|
}
|
|
|
|
export function clearToken() {
|
|
localStorage.removeItem('admin_token');
|
|
}
|
|
|
|
export function isStaticSession(token = getToken()) {
|
|
return token === STATIC_TOKEN;
|
|
}
|
|
|
|
function staticLogin(username, password) {
|
|
if (username === STATIC_USER && password === STATIC_PASS) {
|
|
return { token: STATIC_TOKEN, expiresAt: null, mode: 'static' };
|
|
}
|
|
throw new Error('Invalid credentials');
|
|
}
|
|
|
|
async function apiFetch(path, options = {}) {
|
|
const headers = { ...options.headers };
|
|
if (!(options.body instanceof FormData)) {
|
|
headers['Content-Type'] = 'application/json';
|
|
}
|
|
const token = getToken();
|
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
|
|
const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
|
const data = await res.json().catch(() => ({}));
|
|
if (!res.ok) {
|
|
const fallback =
|
|
res.status === 404
|
|
? 'API not found. Deploy server/ and run: npm run server (port 3001)'
|
|
: res.status === 502
|
|
? 'API server unreachable. Start Node on the host (port 3001).'
|
|
: 'Request failed';
|
|
throw new Error(data.error || fallback);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
export const adminApi = {
|
|
login: async (username, password) => {
|
|
// Prefer Node API when it is up; otherwise allow static admin/admin123.
|
|
try {
|
|
return await apiFetch('/api/auth/login', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
} catch {
|
|
return staticLogin(username, password);
|
|
}
|
|
},
|
|
|
|
me: async () => {
|
|
if (isStaticSession()) {
|
|
return { username: STATIC_USER, mode: 'static' };
|
|
}
|
|
return apiFetch('/api/auth/me');
|
|
},
|
|
|
|
getContent: () => apiFetch('/api/content'),
|
|
|
|
updateSection: (section, data) =>
|
|
apiFetch(`/api/content/${section}`, { method: 'PUT', body: JSON.stringify(data) }),
|
|
|
|
changePassword: (currentPassword, newPassword) =>
|
|
apiFetch('/api/auth/password', { method: 'PUT', body: JSON.stringify({ currentPassword, newPassword }) }),
|
|
|
|
upload: async (file) => {
|
|
// Try Node API first; fall back to local data URL when server is unavailable.
|
|
try {
|
|
const form = new FormData();
|
|
form.append('file', file);
|
|
return await apiFetch('/api/upload', { method: 'POST', body: form });
|
|
} catch {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = () => resolve({ url: reader.result, filename: file.name });
|
|
reader.onerror = () => reject(new Error('Failed to read file'));
|
|
reader.readAsDataURL(file);
|
|
});
|
|
}
|
|
},
|
|
};
|