import './connection.js'; // 1) Initial aus Redis laden, damit Toasts auch nach Redirect sichtbar sind // async function bootstrapToasts() { // try { // const res = await fetch('/api/tasks/active', { // credentials: 'same-origin', // headers: { // 'Accept': 'application/json', // 'X-Requested-With': 'XMLHttpRequest', // }, // }); // // // Klarer Fehlerfall statt blind json() zu rufen // const ct = res.headers.get('content-type') || ''; // if (!res.ok) { // const text = await res.text(); // throw new Error(`HTTP ${res.status}: ${text.slice(0, 300)}`); // } // if (!ct.includes('application/json')) { // const text = await res.text(); // console.warn('Initial toast fetch: non-JSON response', text.substring(0, 300)); // return; // } // // const json = await res.json(); // (json.items || []).forEach(renderOrUpdateToast); // } catch (e) { // console.warn('Initial toast fetch failed', e); // } // } // // bootstrapToasts(); // 2) Live via WebSocket // function renderOrUpdateToast(snap) { // const { // id, // type = 'issue-cert', // status = 'queued', // queued|running|done|failed // message = '', // payload = {}, // } = snap || {}; // // const pos = 'bottom-right'; // const badge = (type === 'issue-cert') ? 'Zertifikat' : type; // const domain = payload?.domain ?? ''; // // // Dauer: done/failed => 6s, sonst offen // const duration = (status === 'done' || status === 'failed') ? 6000 : -1; // // // deine Glas-UI // toastraGlass.show({ // id, // wichtig, damit spätere Updates denselben Toast treffen // state: status, // badge, // domain, // message, // position: pos, // duration // }); // } // // async function bootstrapToasts() { // try { // const res = await fetch('/ui/tasks/active', { // credentials: 'same-origin', // headers: { 'Accept': 'application/json' } // }); // // if (!res.ok) { // console.warn('Initial toast fetch HTTP', res.status); // return; // } // // const ct = res.headers.get('content-type') || ''; // if (!ct.includes('application/json')) { // console.warn('Initial toast fetch: unexpected content-type', ct); // return; // } // // const json = await res.json(); // (json.items || []).forEach(renderOrUpdateToast); // } catch (e) { // console.warn('Initial toast fetch failed', e); // } // } // bootstrapToasts(); // // Echo.channel('system.tasks') // .listen('.task.updated', (e) => { // // Optional: nur eigene User-Events zeigen // // if (e.userId !== window.App?.user?.id) return; // // renderOrUpdateToast(e.payload); // }); // const seen = new Map(); // id -> lastState // // async function bootstrapToasts() { // try { // const res = await fetch('/tasks/active', { // credentials: 'same-origin', // headers: { 'Accept': 'application/json' }, // <- erzwinge JSON // }); // if (!res.ok) { // const text = await res.text(); // console.warn('Initial toast fetch non-200:', res.status, text); // return; // } // const { items = [] } = await res.json(); // items.forEach(renderOrUpdateToast); // } catch (e) { // console.warn('Initial toast fetch failed', e); // } // } // bootstrapToasts(); // // function getCsrf() { // const m = document.head.querySelector('meta[name="csrf-token"]'); // return m ? m.content : ''; // } // // async function ack(id) { // try { // await fetch('/tasks/ack', { // method: 'POST', // credentials: 'same-origin', // headers: { // 'Content-Type': 'application/json', // 'X-CSRF-TOKEN': getCsrf(), // <- wichtig bei POST // 'Accept': 'application/json', // }, // body: JSON.stringify({ id }), // }); // } catch (_) {} // } // function renderOrUpdateToast(item) { // const { id, state, badge, domain, message, progress = 0 } = item; // const last = seen.get(id); // if (last === state) return; // dedupe // // seen.set(id, state); // // // dein Glas-Toast // toastraGlass.show({ // id, // state, // queued|running|done|failed // badge, // domain, // message, // progress, // optional für Fortschrittsbalken // position: 'bottom-right', // duration: (state === 'done' || state === 'failed') ? 8000 : -1, // onClose: () => ack(id), // Nutzer schließt → ebenfalls ack // }); // // // Terminalstatus → direkt ack senden (oder erst onClose, wie du magst) // if (state === 'done' || state === 'failed') { // ack(id); // } // } // // // // WebSocket Live-Updates // window.Echo.channel('system.tasks') // .listen('.cert.status', (e) => { // // e: { id, state, message, progress, ... } // renderOrUpdateToast(e); // }); // 3) Renderer (Toastra Glass Bridge) // const known = new Map(); // taskId -> toastUiId // // function renderOrUpdateToast(payload) { // const id = payload.id; // const state = payload.state; // queued|running|done|failed // const msg = payload.message || ''; // // const opts = { // id, // WICHTIG: stabile ID für replace // state: state, // unser Toastra erwartet 'done'|'failed' um Auto-Close zu setzen // badge: payload.badge ?? null, // domain: payload.title ?? 'System', // message: msg, // position: payload.position ?? 'bottom-right', // duration: (state === 'done' || state === 'failed') ? 6000 : 0, // läuft weiter bis Update // }; // // if (known.has(id)) { // window.toastraGlass.update(known.get(id), opts); // } else { // const toastUiId = window.toastraGlass.show(opts); // known.set(id, toastUiId); // } // // // Optional: bei "done|failed" nach n Sekunden als "gesehen" acken // if (state === 'done' || state === 'failed') { // setTimeout(() => { // fetch(`/api/tasks/${encodeURIComponent(id)}/ack`, { method: 'POST', credentials: 'same-origin' }) // .catch(()=>{}); // }, 7000); // } // } // import Echo from 'laravel-echo' // import Pusher from 'pusher-js' // import {wsConfig} from './connector.js' // import {initEvents} from "./events.js"; // // window.Pusher = Pusher // // window.Pusher && (window.Pusher.logToConsole = true); // Debug an // // const host = wsConfig.host || window.location.hostname // const port = Number(wsConfig.port) || 443 // <— port! // const scheme = (wsConfig.scheme || 'https').toLowerCase() // const path = wsConfig.path || '/ws' // const tls = scheme === 'https' // <— boolean // const key = wsConfig.key // // window.Echo = new Echo({ // broadcaster: 'reverb', // key, // wsHost: host, // wsPort: port, // wssPort: port, // forceTLS: tls, // enabledTransports: ['ws', 'wss'], // wsPath: path, // }) // // window.Echo.channel('system.tasks') // .listen('.cert.ping', (e) => { // console.log('Task-Event:', e); // hier sollte die Nachricht aufschlagen // }); // Echo.channel('system.tasks') // .listen('.cert.ping', (e) => { // console.log('Task-Event:', e); // hier sollte die Nachricht aufschlagen // }); // Echo.channel('system.tasks') // .listen('.cert.status', (e) => { // // Mappe Status zu deiner Toast-API // const stateToTitle = { // queued: 'Wartet…', // running: 'Erstellt…', // done: 'Fertig', // failed: 'Fehlgeschlagen', // }; // // // Dauer: während queued/running immer sichtbar (−1), bei final 5s // const duration = (e.state === 'done' || e.state === 'failed') ? 5000 : -1; // // // Optional: Fortschrittsbalken kannst du selbst in toastraGlass implementieren // window.toastraGlass?.show({ // state: e.state, // queued|running|done|failed // badge: 'Zertifikat', // domain: e.key.replace('issue-cert:',''), // message: e.message, // position: 'bottom-right', // duration, // progress: e.progress ?? 0, // }); // }); // const seen = new Map(); // id -> lastState // // function renderOrUpdateToast(o) { // // Toastra expects: {state,badge,domain,message,progress,position,id,duration} // const pos = o.pos || 'bottom-right'; // const id = o.id; // // // Dedupe / Update // const last = seen.get(id); // const key = `${o.status}|${o.progress}|${o.message}`; // if (last === key) return; // // seen.set(id, key); // // // Zeichnen / Aktualisieren // window.toastraGlass.show({ // state: mapState(o.status), // queued|running|done|failed // badge: o.title || 'ZERTIFIKAT', // domain: o.payload?.domain || o.domain || '', // message: o.message || '', // progress: Number(o.progress ?? 0), // position: pos, // id, // duration: (o.status === 'done' || o.status === 'failed') ? 4000 : -1, // }); // // // Bei done/failed sofort aus dem Local-Cache entfernen // if (o.status === 'done' || o.status === 'failed') { // setTimeout(() => { // seen.delete(id); // }, 4500); // } // } // // function mapState(s) { // if (s === 'queued') return 'queued'; // if (s === 'running') return 'running'; // if (s === 'done') return 'done'; // if (s === 'failed') return 'failed'; // return 'info'; // } // // // 1) Initiale Tasks laden – **neue** Route mit web+auth! // async function bootstrapToasts() { // try { // const res = await fetch('/ui/tasks/active', { // credentials: 'same-origin', // headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }, // }); // if (!res.ok) throw new Error(`HTTP ${res.status}`); // const { items = [] } = await res.json(); // items.forEach(renderOrUpdateToast); // } catch (e) { // console.warn('Initial toast fetch failed', e); // } // } // bootstrapToasts(); // // // 2) Echtzeit-Updates // window.Echo.channel('system.tasks') // .listen('.cert.status', (e) => { // // e: { id,status,message,progress,domain,mode } // renderOrUpdateToast({ // id: e.id, // status: e.status, // message: e.message, // progress: e.progress, // title: (e.mode || 'ZERTIFIKAT').toUpperCase(), // domain: e.domain, // }); // }); const seen = new Map(); // id -> lastState function labelForState(state) { switch ((state || '').toLowerCase()) { case 'queued': return 'Wartet…'; case 'running': return 'Läuft…'; case 'done': return 'Erledigt'; case 'failed': return 'Fehlgeschlagen'; default: return state || 'Unbekannt'; } } function renderOrUpdateToast(ev) { const id = ev.id; const state = (ev.state || '').toLowerCase(); const text = ev.message || ''; const prog = typeof ev.progress === 'number' ? ev.progress : null; // Duplikate vermeiden: nur reagieren, wenn sich der State geändert hat const last = seen.get(id); if (last === state) return; seen.set(id, state); // Dein Toastra: window.toastraGlass?.show({ id, state, // queued|running|done|failed (wichtig!) badge: 'ZERTIFIKAT', domain: text, // Sub-Headline message: text, // Main-Text progress: prog, // 0..100 (optional) position: 'bottom-right', duration: (state === 'done' || state === 'failed') ? 5000 : -1, // falls dein Renderer eine Status-Beschriftung braucht: statusLabel: labelForState(state), }); // Final? -> nach kurzer Zeit ausblenden (UI) if (ev.meta && ev.meta.final) { setTimeout(() => { window.toastraGlass?.removeById?.(id); // Alternativ (wenn removeById fehlt): // document.querySelectorAll(`[data-toast-id="${CSS.escape(id)}"]`).forEach(n => n.remove()); seen.delete(id); }, 5200); } } // Initiale Tasks aus dem Backend laden (damit Redirect-Toasts sichtbar bleiben) async function bootstrapToasts() { try { const res = await fetch('/ui/tasks/active', { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }, }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); (data.items || []).forEach(renderOrUpdateToast); } catch (e) { console.warn('Initial toast fetch failed', e); } } bootstrapToasts(); // WebSocket-Listener window.Echo .channel('system.tasks') .listen('.cert.status', (payload) => { renderOrUpdateToast(payload); });