// SSH Access page — manage inbound admin SSH to this host: authorize public // keys (paste to grant access), remove them, and toggle password login behind // the backend's lockout guard. Reads /data/ssh/access.json; all mutations run // as `libreportal ssh ...` tasks. LibrePortal never handles a private key here. class SshPage { constructor() { this.taskManager = (typeof TaskManager !== 'undefined') ? new TaskManager() : null; this.data = null; this._bound = false; } async init() { this.bindEvents(); await this.refresh(); this.render(); } async refresh() { this.data = await this.fetchJson(`/data/ssh/access.json?t=${Date.now()}`); } async fetchJson(url) { try { const r = await fetch(url); if (!r.ok) return null; return await r.json(); } catch { return null; } } escape(s) { return String(s ?? '').replace(/[&<>"']/g, c => ( { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c] )); } notify(msg, type) { if (window.notificationSystem) window.notificationSystem.show(msg, type || 'info'); else console.log(`[ssh ${type || 'info'}] ${msg}`); } bindEvents() { if (this._bound) return; this._bound = true; document.addEventListener('click', (e) => { if (e.target.closest('[data-action="ssh-add-key"]')) { this.addKey(); return; } const rm = e.target.closest('[data-action="ssh-remove-key"]'); if (rm) { this.removeKey(rm.dataset.fp); return; } const tog = e.target.closest('[data-action="ssh-toggle-password"]'); if (tog) { this.togglePassword(tog.dataset.next); return; } }); } render() { const root = document.getElementById('ssh-page-root'); if (!root) return; if (!this.data) { root.innerHTML = `
Logging in as ${this.escape(d.user)} · ${keys.length} authorized key${keys.length === 1 ? '' : 's'}.
Password login is disabled — only the keys below can connect.
`}Paste a public key (the .pub from your machine) to grant it SSH access: