initial commit
This commit is contained in:
105
frontend/js/api.js
Normal file
105
frontend/js/api.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/* FluentGerman.ai — API client with auth */
|
||||
|
||||
const API_BASE = '/api';
|
||||
|
||||
function getToken() {
|
||||
return localStorage.getItem('fg_token');
|
||||
}
|
||||
|
||||
function setToken(token) {
|
||||
localStorage.setItem('fg_token', token);
|
||||
}
|
||||
|
||||
function clearToken() {
|
||||
localStorage.removeItem('fg_token');
|
||||
}
|
||||
|
||||
function getUser() {
|
||||
const data = localStorage.getItem('fg_user');
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
|
||||
function setUser(user) {
|
||||
localStorage.setItem('fg_user', JSON.stringify(user));
|
||||
}
|
||||
|
||||
function clearUser() {
|
||||
localStorage.removeItem('fg_user');
|
||||
}
|
||||
|
||||
async function api(path, options = {}) {
|
||||
const token = getToken();
|
||||
const headers = { ...options.headers };
|
||||
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// Don't set Content-Type for FormData (browser sets it with boundary)
|
||||
if (!(options.body instanceof FormData)) {
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
||||
|
||||
if (response.status === 401) {
|
||||
clearToken();
|
||||
clearUser();
|
||||
window.location.href = '/';
|
||||
return;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function apiJSON(path, options = {}) {
|
||||
const response = await api(path, options);
|
||||
if (!response || !response.ok) {
|
||||
const error = await response?.json().catch(() => ({ detail: 'Request failed' }));
|
||||
throw new Error(error.detail || 'Request failed');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function requireAuth() {
|
||||
if (!getToken()) {
|
||||
window.location.href = '/';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function requireAdmin() {
|
||||
const user = getUser();
|
||||
if (!user?.is_admin) {
|
||||
window.location.href = '/chat.html';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function logout() {
|
||||
clearToken();
|
||||
clearUser();
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
/* Toast notifications */
|
||||
function showToast(message, type = 'success') {
|
||||
let container = document.querySelector('.toast-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'toast-container';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.textContent = message;
|
||||
container.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '0';
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
Reference in New Issue
Block a user