// ── Global 401 Handler ── (function() { var _origFetch = window.fetch; window.fetch = function(url, opts) { return _origFetch.apply(this, arguments).then(function(resp) { if (resp.status === 401 && typeof url === 'string' && !url.includes('/api/auth/')) { if (typeof currentPage !== 'undefined' && currentPage !== 'login') { navigateTo('login'); } } return resp; }); }; })(); // ── Auth API ── async function apiLogin(username, password) { const resp = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Login failed'); } return resp.json(); } async function apiLogout() { await fetch('/api/auth/logout', { method: 'POST' }); } async function apiMe() { const resp = await fetch('/api/auth/me'); if (!resp.ok) return null; return resp.json(); } // ── API Calls ── async function apiChat(content, sessionId, signal, planMode) { var payload = { content, sessionId }; if (planMode) payload.planMode = true; return fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal }); } async function apiChatChoice(sessionId, choiceToolCallId, choiceResult, signal, planMode) { var payload = { sessionId, choiceToolCallId, choiceResult }; if (planMode) payload.planMode = true; return fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal }); } // ── Agent API ── async function apiGetAgents() { const resp = await fetch('/api/agents'); return resp.json(); } async function apiGetAvailableAgents() { const resp = await fetch('/api/agents/available'); if (!resp.ok) return []; return resp.json(); } async function apiGetAgent(agentId) { const resp = await fetch(`/api/agents/${agentId}`); if (!resp.ok) return null; return resp.json(); } async function apiCreateAgent(data) { const resp = await fetch('/api/agents', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return resp.json(); } async function apiUpdateAgent(agentId, data) { const resp = await fetch(`/api/agents/${agentId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return resp.json(); } async function apiDeleteAgent(agentId) { return fetch(`/api/agents/${agentId}`, { method: 'DELETE' }); } async function apiHistory(sessionId, beforeSortKey, includeDeleted = false) { let url = `/api/history?sessionId=${sessionId}`; if (beforeSortKey) url += `&beforeSortKey=${beforeSortKey}`; if (includeDeleted) url += `&includeDeleted=true`; const resp = await fetch(url); return resp.json(); } async function apiHistoryPoll(sessionId, afterSortKey, includeDeleted = false) { let url = `/api/history/poll?sessionId=${sessionId}&afterSortKey=${afterSortKey}`; if (includeDeleted) url += `&includeDeleted=true`; const resp = await fetch(url); return resp.json(); } async function apiArchive(sessionId) { const resp = await fetch(`/api/archive?sessionId=${sessionId}`, { method: 'POST' }); if (resp.status === 204) return null; return resp.json(); } async function apiListArchives(sessionId) { const resp = await fetch(`/api/archives?sessionId=${sessionId}`); return resp.json(); } async function apiGetArchive(archiveId) { const resp = await fetch(`/api/archive/${archiveId}`); return resp.json(); } // ── Session API ── async function apiGetSessions() { const resp = await fetch('/api/sessions'); return resp.json(); } async function apiCreateSession(data = {}) { const resp = await fetch('/api/sessions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return resp.json(); } async function apiGetSession(sessionId) { const resp = await fetch(`/api/sessions/${sessionId}`); if (!resp.ok) return null; return resp.json(); } async function apiDeleteSession(sessionId) { return fetch(`/api/sessions/${sessionId}`, { method: 'DELETE' }); } async function apiUpdateSession(sessionId, data) { const resp = await fetch(`/api/sessions/${sessionId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return resp.json(); } async function apiExecCommand(sessionId, command, mode = 'background') { const resp = await fetch(`/api/sessions/${sessionId}/exec`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ command, mode }) }); if (mode === 'foreground') return resp.json(); return resp; } async function apiGetSystemPrompt(sessionId) { const resp = await fetch(`/api/sessions/${sessionId}/system-prompt`); if (!resp.ok) throw new Error('System-Prompt konnte nicht geladen werden'); return resp.text(); } async function apiGetSystemPromptBlocks(sessionId, planMode) { const url = `/api/sessions/${sessionId}/system-prompt-blocks` + (planMode ? '?planMode=true' : ''); const resp = await fetch(url); if (!resp.ok) throw new Error('System-Prompt konnte nicht geladen werden'); return resp.json(); } async function apiIsStreaming(sessionId) { const resp = await fetch(`/api/sessions/${sessionId}/streaming`); return resp.json(); } async function apiStopChat(sessionId) { const resp = await fetch('/api/chat/stop', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId }) }); return resp.json(); } async function apiGetChain(chainId) { const resp = await fetch(`/api/chains/${chainId}`); if (!resp.ok) return null; return resp.json(); } async function apiToggleMessageMark(messageId) { const resp = await fetch(`/api/messages/${messageId}/mark`, { method: 'PUT' }); return resp.json(); } async function apiToggleMessageDelete(messageId) { const resp = await fetch(`/api/messages/${messageId}/delete`, { method: 'PUT' }); return resp.json(); } async function apiGitStatus(sessionId) { const resp = await fetch(`/api/sessions/${sessionId}/git-status`); if (!resp.ok) return null; return resp.json(); } // ── Skills API ── async function apiGetSkills() { const resp = await fetch('/api/skills'); return resp.json(); } async function apiGetAgentSkills(agentId) { const resp = await fetch(`/api/agents/${agentId}/skills`); return resp.json(); } async function apiSetAgentSkills(agentId, skills) { const resp = await fetch(`/api/agents/${agentId}/skills`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ skills }) }); return resp.json(); } async function apiAddAgentSkill(agentId, slug) { return fetch(`/api/agents/${agentId}/skills/${slug}`, { method: 'POST' }); } async function apiRemoveAgentSkill(agentId, slug) { return fetch(`/api/agents/${agentId}/skills/${slug}`, { method: 'DELETE' }); } // ── Profile API ── async function apiGetProfile() { const resp = await fetch('/api/profile'); if (!resp.ok) return null; return resp.json(); } async function apiUpdateProfile(data) { const resp = await fetch('/api/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Speichern'); } return resp.json(); } async function apiChangePassword(currentPassword, newPassword) { const resp = await fetch('/api/profile/password', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ currentPassword, newPassword }) }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Ändern'); } return resp.json(); } // ── Telegram Bot API ── async function apiGetTelegramBots() { const resp = await fetch('/api/telegram/bots'); return resp.json(); } async function apiGetTelegramBot(botId) { const resp = await fetch(`/api/telegram/bots/${botId}`); if (!resp.ok) return null; return resp.json(); } async function apiCreateTelegramBot(name, botToken) { const resp = await fetch('/api/telegram/bots', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, botToken }) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Bot erstellen fehlgeschlagen'); } return resp.json(); } async function apiGetAvailableTelegramBots() { const resp = await fetch('/api/telegram/bots/available'); return resp.json(); } async function apiUpdateTelegramBot(botId, name, botToken, isPublic) { const resp = await fetch(`/api/telegram/bots/${botId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, botToken, isPublic }) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Bot speichern fehlgeschlagen'); } return resp.json(); } async function apiDeleteTelegramBot(botId) { const resp = await fetch(`/api/telegram/bots/${botId}`, { method: 'DELETE' }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Bot löschen fehlgeschlagen'); } return resp.json(); } async function apiTelegramBotRegisterWebhook(botId, baseUrl) { const resp = await fetch(`/api/telegram/bots/${botId}/register-webhook`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ baseUrl }) }); if (!resp.ok) throw new Error('Webhook-Registrierung fehlgeschlagen'); return resp.json(); } async function apiTelegramBotWebhookInfo(botId) { const resp = await fetch(`/api/telegram/bots/${botId}/webhook-info`); if (!resp.ok) throw new Error('Webhook-Info fehlgeschlagen'); return resp.json(); } async function apiTelegramBotDeleteWebhook(botId) { const resp = await fetch(`/api/telegram/bots/${botId}/webhook`, { method: 'DELETE' }); if (!resp.ok) throw new Error('Webhook-Löschung fehlgeschlagen'); return resp.json(); } // ── Session Telegram Config ── async function apiSessionTelegramGet(sessionId) { const resp = await fetch(`/api/sessions/${sessionId}/telegram`); if (!resp.ok) return { configured: false }; return resp.json(); } async function apiSessionTelegramSave(sessionId, telegramBotId, chatId) { const resp = await fetch(`/api/sessions/${sessionId}/telegram`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ telegramBotId, chatId }) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Telegram-Config speichern fehlgeschlagen'); } return resp.json(); } async function apiGetSettings() { const resp = await fetch('/api/settings'); if (!resp.ok) return {}; return resp.json(); } async function apiSaveSettings(settings) { const resp = await fetch('/api/settings', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }); return resp.json(); } // ── Cronjob API ── async function apiGetCronjobs() { const resp = await fetch('/api/cronjobs'); return resp.json(); } async function apiGetCronjob(id) { const resp = await fetch(`/api/cronjobs/${id}`); if (!resp.ok) return null; return resp.json(); } async function apiCreateCronjob(data) { const resp = await fetch('/api/cronjobs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Cronjob erstellen fehlgeschlagen'); } return resp.json(); } async function apiUpdateCronjob(id, data) { const resp = await fetch(`/api/cronjobs/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Cronjob speichern fehlgeschlagen'); } return resp.json(); } async function apiDeleteCronjob(id) { return fetch(`/api/cronjobs/${id}`, { method: 'DELETE' }); } async function apiToggleCronjob(id) { const resp = await fetch(`/api/cronjobs/${id}/toggle`, { method: 'POST' }); return resp.json(); } async function apiRunCronjob(id) { const resp = await fetch(`/api/cronjobs/${id}/run`, { method: 'POST' }); return resp.json(); } // ── Shared Message API ── async function apiListShares() { const resp = await fetch('/api/shared'); return resp.json(); } async function apiShareMessage(messageId, includeContext = false) { const resp = await fetch(`/api/messages/${messageId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ includeContext }) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Teilen fehlgeschlagen'); } return resp.json(); } async function apiGetShared(token) { const resp = await fetch(`/api/shared/${token}`); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Geteilte Nachricht nicht gefunden'); } return resp.json(); } async function apiDeleteShare(token) { return fetch(`/api/shared/${token}`, { method: 'DELETE' }); } // ── Admin API ── async function apiAdminGetUsers() { const resp = await fetch('/api/admin/users'); if (!resp.ok) throw new Error('Fehler beim Laden der Benutzer'); return resp.json(); } async function apiAdminCreateUser(data) { const resp = await fetch('/api/admin/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Erstellen'); } return resp.json(); } async function apiAdminUpdateUser(userId, data) { const resp = await fetch('/api/admin/users/' + userId, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Aktualisieren'); } return resp.json(); } async function apiAdminResetPassword(userId, password) { const resp = await fetch('/api/admin/users/' + userId + '/password', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Zurücksetzen'); } return resp.json(); } async function apiAdminDeleteUser(userId) { const resp = await fetch('/api/admin/users/' + userId, { method: 'DELETE' }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Löschen'); } return resp.json(); } async function apiAdminImpersonate(userId) { const resp = await fetch('/api/admin/users/' + userId + '/impersonate', { method: 'POST' }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Anmelden'); } return resp.json(); } async function apiAdminGetLoginLink(userId) { const resp = await fetch('/api/admin/users/' + userId + '/login-link', { method: 'POST' }); if (!resp.ok) { const err = await resp.json(); throw new Error(err.error || 'Fehler beim Erstellen des Login-Links'); } return resp.json(); } // ── Server Settings API (Admin) ── async function apiGetServerSettings() { const resp = await fetch('/api/admin/server-settings'); if (!resp.ok) throw new Error('Fehler beim Laden der Servereinstellungen'); return resp.json(); } async function apiSaveServerSettings(data) { const resp = await fetch('/api/admin/server-settings', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Fehler beim Speichern'); } return resp.json(); } // ── Krea Image API ── async function apiKreaStatus() { const resp = await fetch('/api/images/status'); if (!resp.ok) return { configured: false }; return resp.json(); } async function apiKreaGenerate(prompt, model, width, height) { const resp = await fetch('/api/images/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, model: model || 'flux', width: width || 512, height: height || 512, batchSize: 1 }) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Bildgenerierung fehlgeschlagen'); } return resp.json(); } // ── OpenClaw Import API ── async function apiGetOpenclawSessions() { const resp = await fetch('/api/openclaw/sessions'); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'OpenClaw-Sessions konnten nicht geladen werden'); } return resp.json(); } async function apiImportOpenclawSession(openclawSessionId, sessionId) { const resp = await fetch('/api/openclaw/import', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ openclawSessionId, sessionId }) }); if (!resp.ok) { const err = await resp.json().catch(() => ({})); throw new Error(err.error || 'Import fehlgeschlagen'); } return resp.json(); } // ── Backup API ── async function apiBackupDb() { const resp = await fetch('/api/backup', { method: 'POST', credentials: 'include' }); if (!resp.ok) throw new Error('Backup fehlgeschlagen'); const blob = await resp.blob(); const cd = resp.headers.get('content-disposition') || ''; const filename = cd.match(/filename\*=UTF-8''(.+?)(?:;|$)/i)?.[1] || cd.match(/filename="?([^";]+)"?/)?.[1] || 'gott-backup.tar.gz'; const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }