diff --git a/nodejs/routes/render.js b/nodejs/routes/render.js index 037382b..78ce405 100644 --- a/nodejs/routes/render.js +++ b/nodejs/routes/render.js @@ -14,6 +14,10 @@ const values = { titleIcon: conf.environment !== 'production' ? '' : '', name: conf.name, logo: conf.logo, + // The SSH front door's port -- the dashboard's "quick jump" copy buttons + // need this to build a real, working `ssh ...` command (the web UI and + // SSH front door share a hostname but not a port). + sshPort: (conf.ssh && conf.ssh.listenPort) || 22, ...buildInfo, }; diff --git a/nodejs/views/dashboard.ejs b/nodejs/views/dashboard.ejs index de64525..8366985 100644 --- a/nodejs/views/dashboard.ejs +++ b/nodejs/views/dashboard.ejs @@ -28,6 +28,27 @@ +
+
+
+
Quick Jump
+
+

+ Skip the picker: ssh <your-username>_-_<host-slug>@<this-jump-host> + connects straight to a host. Or just ssh <your-username>@<this-jump-host> + for the interactive picker. +

+
+ + +
+
+
+
+
+
@@ -79,14 +100,38 @@ $b.append('' + app.jump.esc(x.name) + '' + x.count + ''); }); } + // The web UI and the SSH front door share a hostname, just not a port. + var SSH_PORT = <%- JSON.stringify(sshPort) %>; + function sshCommand(target){ + var uid = app.auth.user && app.auth.user.username; + if(!uid) return ''; + var portFlag = SSH_PORT === 22 ? '' : ' -p ' + SSH_PORT; + return 'ssh ' + uid + (target ? '_-_' + target : '') + '@' + location.hostname + portFlag; + } + function copySshCommand(sel){ + var $el = $(sel); + var text = $el.val(); + if(!text) return; + navigator.clipboard.writeText(text).then(function(){ + app.messages.toast('Copied to clipboard', 'success'); + }, function(){ + app.messages.toast('Could not copy — select and copy manually', 'danger'); + }); + } + function hostRows(sel, hosts){ var $b = $(sel).empty(); if(!hosts || !hosts.length){ $b.append('No hosts reachable.'); return; } hosts.forEach(function(h){ var addr = (h.metadata && (h.metadata.ip || h.metadata.address)) || ''; + var rowId = 'host-cmd-' + h.slug.replace(/[^a-zA-Z0-9_-]/g, ''); $b.append('' + app.jump.esc(h.displayName || h.name || h.slug) + '' + '' + app.jump.esc(h.slug) + '' - + '' + app.jump.esc(addr) + ''); + + '' + app.jump.esc(addr) + '' + + '' + + '' + + '' + + ''); }); } function tokenRows(tokens){ @@ -185,6 +230,7 @@ }); await app.auth.loadUser(); if(app.auth.isAdmin()) $('#my-hosts-title').text('All hosts'); + $('#quick-jump-cmd').val(sshCommand()); app.jump.hosts(function(error, data){ if(error) return hostRows('#my-hosts', []); hostRows('#my-hosts', data && data.results);