mailwolt/resources/js/webserver/websocket.js

415 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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);
});