document.addEventListener("DOMContentLoaded", function () { const apiBase = "/api"; function isTokenExpired(token) { try { const payload = JSON.parse(atob(token.split('.')[1])); const now = Math.floor(Date.now() / 1000); return payload.exp ? now >= payload.exp : false; } catch { return true; } } function getTokenExpirySeconds(token) { try { const payload = JSON.parse(atob(token.split('.')[1])); const now = Math.floor(Date.now() / 1000); return payload.exp ? payload.exp - now : 0; } catch { return 0; } } function redirectToLogin() { localStorage.removeItem("token"); localStorage.removeItem("user"); if (!window.location.pathname.startsWith("/login")) { window.location.href = "/login?expired=1"; } } async function apiFetch(endpoint, options = {}) { const token = localStorage.getItem("token"); if (token && isTokenExpired(token)) { redirectToLogin(); throw new Error("Session expired"); } const headers = { "Content-Type": "application/json", ...options.headers, }; if (token) { headers["Authorization"] = `Bearer ${token}`; } const response = await fetch(`${apiBase}${endpoint}`, { ...options, headers, }); if (response.status === 401) { redirectToLogin(); throw new Error("Authentication required"); } if (!response.ok) { let errorMsg = `HTTP ${response.status}`; try { const error = await response.json(); if (typeof error.detail === 'string') { errorMsg = error.detail; } else if (Array.isArray(error.detail)) { errorMsg = error.detail.map(d => d.msg || d).join(', '); } else if (error.detail && typeof error.detail === 'object') { errorMsg = error.detail.detail || error.detail.msg || JSON.stringify(error.detail); } } catch { // ignore parse error, use default } throw new Error(errorMsg); } return response.json(); } window.LinkSync = { apiFetch, isTokenExpired, getTokenExpirySeconds, async getLinks(params = {}) { const qs = new URLSearchParams(params).toString(); return apiFetch(`/links/?${qs}`); }, async createLink(data) { return apiFetch("/links/", { method: "POST", body: JSON.stringify(data), }); }, async updateLink(id, data) { return apiFetch(`/links/${id}`, { method: "PUT", body: JSON.stringify(data), }); }, async deleteLink(id) { return apiFetch(`/links/${id}`, { method: "DELETE" }); }, async getCollections() { return apiFetch("/collections/"); }, async createCollection(data) { return apiFetch("/collections/", { method: "POST", body: JSON.stringify(data), }); }, async updateCollection(id, data) { return apiFetch(`/collections/${id}`, { method: "PUT", body: JSON.stringify(data), }); }, async deleteCollection(id) { return apiFetch(`/collections/${id}`, { method: "DELETE" }); }, async previewQuery(expression) { return apiFetch(`/queries/preview?expression=${encodeURIComponent(expression)}`); }, async executeQuery(expression, limit = 20) { return apiFetch(`/queries/execute?expression=${encodeURIComponent(expression)}&limit=${limit}`); }, async login(username, password) { const formData = new URLSearchParams(); formData.append("username", username); formData.append("password", password); const response = await fetch(`${apiBase}/auth/login`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: formData.toString(), }); if (!response.ok) throw new Error("Login failed"); const data = await response.json(); localStorage.setItem("token", data.access_token); return data; }, async getMe() { return apiFetch("/auth/me"); }, logout() { localStorage.removeItem("token"); localStorage.removeItem("user"); window.location.href = "/login"; }, async getUsers() { return apiFetch("/admin/users"); }, async createUser(data) { return apiFetch("/admin/users", { method: "POST", body: JSON.stringify(data), }); }, async updateUser(id, data) { return apiFetch(`/admin/users/${id}`, { method: "PUT", body: JSON.stringify(data), }); }, async deleteUser(id) { return apiFetch(`/admin/users/${id}`, { method: "DELETE" }); }, async getApiKeys() { return apiFetch("/auth/api-keys"); }, async createApiKey(name) { return apiFetch(`/auth/api-key?name=${encodeURIComponent(name)}`, { method: "POST", }); }, async deleteApiKey(id) { return apiFetch(`/auth/api-key/${id}`, { method: "DELETE" }); }, }; });