// AutoDeployer API Client — vanilla JS (ES module) const API_BASE = '/api'; let accessToken = localStorage.getItem('accessToken') || ''; function setToken(token) { accessToken = token; token ? localStorage.setItem('accessToken', token) : localStorage.removeItem('accessToken'); } function getToken() { return accessToken; } class ApiError extends Error { constructor(status, body) { super(body.error || 'API Error'); this.status = status; this.body = body; } } async function request(path, opts = {}) { const url = `${API_BASE}${path}`; const headers = { 'Content-Type': 'application/json', ...opts.headers }; if (accessToken) headers['Authorization'] = `Bearer ${accessToken}`; let res = await fetch(url, { ...opts, headers, credentials: 'include' }); if (res.status === 401 && path !== '/auth/login' && path !== '/auth/refresh') { const refreshed = await refreshToken(); if (refreshed) { headers['Authorization'] = `Bearer ${accessToken}`; res = await fetch(url, { ...opts, headers, credentials: 'include' }); } else { throw new ApiError(401, { error: 'Session expired' }); } } if (!res.ok) { const body = await res.json().catch(() => ({ error: 'Unknown error' })); throw new ApiError(res.status, body); } return res.json(); } async function refreshToken() { try { const res = await fetch(`${API_BASE}/auth/refresh`, { method: 'POST', credentials: 'include' }); if (res.ok) { const d = await res.json(); setToken(d.accessToken); return true; } } catch {} return false; } export const auth = { status: () => request('/auth/status'), login: (u, p) => request('/auth/login', { method: 'POST', body: JSON.stringify({ username: u, password: p }) }).then(d => { setToken(d.accessToken); return d; }), setup: (u, p) => request('/auth/setup', { method: 'POST', body: JSON.stringify({ username: u, password: p }) }).then(d => { setToken(d.accessToken); return d; }), logout: () => request('/auth/logout', { method: 'POST' }).then(() => setToken('')).catch(() => setToken('')), me: () => request('/auth/me'), changePassword: (cur, nw) => request('/auth/password', { method: 'PUT', body: JSON.stringify({ currentPassword: cur, newPassword: nw }) }), }; export const services = { list: () => request('/services'), get: (id) => request(`/services/${id}`), create: (d) => request('/services', { method: 'POST', body: JSON.stringify(d) }), update: (id, d) => request(`/services/${id}`, { method: 'PUT', body: JSON.stringify(d) }), delete: (id) => request(`/services/${id}`, { method: 'DELETE' }), deploy: (id) => request(`/services/${id}/deploy`, { method: 'POST' }), stop: (id) => request(`/services/${id}/stop`, { method: 'POST' }), restart: (id) => request(`/services/${id}/restart`, { method: 'POST' }), inspect: (id) => request(`/services/${id}/inspect`), cleanup: () => request('/services/cleanup', { method: 'POST' }), pruneImages: (keep = 2) => request('/services/prune-images', { method: 'POST', body: JSON.stringify({ keep }) }), traefikPreview: (id) => request(`/services/${id}/traefik-preview`), traefikPreviewBody: (d) => request('/services/traefik-preview', { method: 'POST', body: JSON.stringify(d) }), getEnv: (id) => request(`/services/${id}/env`), setEnv: (id, vars) => request(`/services/${id}/env`, { method: 'PUT', body: JSON.stringify({ vars }) }), }; export const deploys = { list: () => request('/deploys'), get: (id) => request(`/deploys/${id}`), }; export const networks = { list: () => request('/networks'), create: (name, driver, internal) => request('/networks', { method: 'POST', body: JSON.stringify({ name, driver, internal }) }), remove: (name) => request(`/networks/${name}`, { method: 'DELETE' }), }; export const monitoring = { realtime: () => request('/monitoring/realtime'), stats: (name) => request(`/monitoring/${name}/stats`), history: (name, range = '-1h', field = 'cpu_percent') => request(`/monitoring/${name}/history?range=${range}&field=${field}`), }; export const settingsApi = { get: () => request('/settings'), update: (s) => request('/settings', { method: 'PUT', body: JSON.stringify({ settings: s }) }), testGitea: () => request('/settings/test/gitea'), testTelegram: () => request('/settings/test/telegram'), queueStatus: () => request('/settings/queue'), }; export const system = { version: () => request('/system/version'), selfUpdate: () => request('/system/self-update', { method: 'POST' }), updateStatus: () => request('/system/update-status'), }; export function createLogSocket(target) { const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; return new WebSocket(`${proto}//${location.host}/ws/logs/${target}?token=${accessToken}`); } export function createTerminalSocket(containerId) { const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; return new WebSocket(`${proto}//${location.host}/ws/terminal/${containerId}?token=${accessToken}`); } export { getToken, setToken };