From 32bf502ffaef831f957d52bfea633c021238c042 Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 26 Sep 2026 15:03:55 +0200 Subject: [PATCH] Web: fix SSH, SFTP and all other sessions failing over plain http:// (crypto.randomUUID only exists in secure contexts), add a clipboard fallback without HTTPS Co-Authored-By: Claude Opus 5.5 --- src/renderer/app.js | 3 ++- src/web/public/web-api.js | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/renderer/app.js b/src/renderer/app.js index 0650485..0460dd8 100644 --- a/src/renderer/app.js +++ b/src/renderer/app.js @@ -5,7 +5,8 @@ const $ = (sel, root = document) => root.querySelector(sel); const $$ = (sel, root = document) => [...root.querySelectorAll(sel)]; const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); -const uid = () => crypto.randomUUID(); +// crypto.randomUUID gibt es nur in sicheren Kontexten (HTTPS/localhost) – die Web-Version läuft oft per http:// +const uid = () => (crypto.randomUUID ? crypto.randomUUID() : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16))); function h(html) { const t = document.createElement('template'); t.innerHTML = html.trim(); return t.content.firstElementChild; } const T = I18N.t; // Web-Version (Docker, src/web): gleiche Oberfläche im Browser; ohne RDP, VPN, Port-Forwarding und lokale Dateien diff --git a/src/web/public/web-api.js b/src/web/public/web-api.js index 1fd2c42..49f8a81 100644 --- a/src/web/public/web-api.js +++ b/src/web/public/web-api.js @@ -67,8 +67,17 @@ // Kanäle, die im Browser selbst erledigt werden const local = { - 'clipboard:write': (t) => navigator.clipboard.writeText(String(t)), - 'clipboard:read': () => navigator.clipboard.readText(), + // Ohne HTTPS gibt es navigator.clipboard nicht: Kopieren per execCommand, Einfügen dann nur per Strg+V + 'clipboard:write': (t) => { + if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(String(t)); + const ta = Object.assign(document.createElement('textarea'), { value: String(t) }); + ta.style.cssText = 'position:fixed;opacity:0'; + document.body.append(ta); ta.select(); document.execCommand('copy'); ta.remove(); + }, + 'clipboard:read': () => { + if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.readText(); + throw new Error('Pasting from the menu needs HTTPS. Use Ctrl+Shift+V or Ctrl+V instead.'); + }, 'shell:open': (url) => { if (/^https?:\/\//i.test(url)) window.open(url, '_blank', 'noopener'); }, 'vault:export': async () => { downloadText('mrterm-backup.json', JSON.stringify(await remote('vault:exportData', []), null, 2)); return 'mrterm-backup.json'; }, 'vault:import': async () => { const f = await pickFile('.json,application/json'); if (!f) return false; return remote('vault:importData', [JSON.parse(f.content)]); },