Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a76f71edd | |||
| e482f52f10 |
@@ -4,6 +4,11 @@ All notable changes to this project are documented here. Format loosely
|
|||||||
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
|
||||||
correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`.
|
correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`.
|
||||||
|
|
||||||
|
## [1.9.0] - 2026-07-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **"Quick Jump" copy-to-clipboard section on the dashboard** — the `uid_-_target` grammar-mode SSH command was documented in the README but nowhere in the UI. A new card gives a one-click-copy command for interactive-picker mode, and every row in "Hosts you can reach" has its own copy button for the exact grammar-mode command to that host, ready to paste and run as-is (uses the logged-in user's own uid).
|
||||||
|
|
||||||
## [1.8.2] - 2026-07-28
|
## [1.8.2] - 2026-07-28
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "t42-jump-host",
|
"name": "t42-jump-host",
|
||||||
"version": "1.8.2",
|
"version": "1.9.0",
|
||||||
"description": "SSH jump host for the theta42 stack — LDAP-authenticated, directory-driven host bridging with audit and metrics",
|
"description": "SSH jump host for the theta42 stack — LDAP-authenticated, directory-driven host bridging with audit and metrics",
|
||||||
"author": [
|
"author": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ const values = {
|
|||||||
titleIcon: conf.environment !== 'production' ? '<i class="fa-brands fa-dev"></i>' : '',
|
titleIcon: conf.environment !== 'production' ? '<i class="fa-brands fa-dev"></i>' : '',
|
||||||
name: conf.name,
|
name: conf.name,
|
||||||
logo: conf.logo,
|
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,
|
...buildInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3 mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header"><i class="fa-solid fa-terminal me-1"></i> Quick Jump</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="text-muted small mb-2">
|
||||||
|
Skip the picker: <code>ssh <your-username>_-_<host-slug>@<this-jump-host></code>
|
||||||
|
connects straight to a host. Or just <code>ssh <your-username>@<this-jump-host></code>
|
||||||
|
for the interactive picker.
|
||||||
|
</p>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control font-monospace" id="quick-jump-cmd" readonly>
|
||||||
|
<button class="btn btn-outline-secondary" onclick="copySshCommand('#quick-jump-cmd')" title="Copy">
|
||||||
|
<i class="fa-solid fa-copy"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
@@ -79,14 +100,38 @@
|
|||||||
$b.append('<tr><td>' + app.jump.esc(x.name) + '</td><td class="text-end">' + x.count + '</td></tr>');
|
$b.append('<tr><td>' + app.jump.esc(x.name) + '</td><td class="text-end">' + x.count + '</td></tr>');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 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){
|
function hostRows(sel, hosts){
|
||||||
var $b = $(sel).empty();
|
var $b = $(sel).empty();
|
||||||
if(!hosts || !hosts.length){ $b.append('<tr><td class="text-muted">No hosts reachable.</td></tr>'); return; }
|
if(!hosts || !hosts.length){ $b.append('<tr><td class="text-muted">No hosts reachable.</td></tr>'); return; }
|
||||||
hosts.forEach(function(h){
|
hosts.forEach(function(h){
|
||||||
var addr = (h.metadata && (h.metadata.ip || h.metadata.address)) || '';
|
var addr = (h.metadata && (h.metadata.ip || h.metadata.address)) || '';
|
||||||
|
var rowId = 'host-cmd-' + h.slug.replace(/[^a-zA-Z0-9_-]/g, '');
|
||||||
$b.append('<tr><td>' + app.jump.esc(h.displayName || h.name || h.slug) + '</td>'
|
$b.append('<tr><td>' + app.jump.esc(h.displayName || h.name || h.slug) + '</td>'
|
||||||
+ '<td class="text-muted small">' + app.jump.esc(h.slug) + '</td>'
|
+ '<td class="text-muted small">' + app.jump.esc(h.slug) + '</td>'
|
||||||
+ '<td class="text-end text-muted small">' + app.jump.esc(addr) + '</td></tr>');
|
+ '<td class="text-end text-muted small">' + app.jump.esc(addr) + '</td>'
|
||||||
|
+ '<td class="text-end">'
|
||||||
|
+ '<input type="hidden" id="' + rowId + '" value="' + app.jump.esc(sshCommand(h.slug)) + '">'
|
||||||
|
+ '<button class="btn btn-sm btn-outline-secondary" onclick="copySshCommand(\'#' + rowId + '\')" title="Copy quick-jump command"><i class="fa-solid fa-copy"></i></button>'
|
||||||
|
+ '</td></tr>');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function tokenRows(tokens){
|
function tokenRows(tokens){
|
||||||
@@ -185,6 +230,7 @@
|
|||||||
});
|
});
|
||||||
await app.auth.loadUser();
|
await app.auth.loadUser();
|
||||||
if(app.auth.isAdmin()) $('#my-hosts-title').text('All hosts');
|
if(app.auth.isAdmin()) $('#my-hosts-title').text('All hosts');
|
||||||
|
$('#quick-jump-cmd').val(sshCommand());
|
||||||
app.jump.hosts(function(error, data){
|
app.jump.hosts(function(error, data){
|
||||||
if(error) return hostRows('#my-hosts', []);
|
if(error) return hostRows('#my-hosts', []);
|
||||||
hostRows('#my-hosts', data && data.results);
|
hostRows('#my-hosts', data && data.results);
|
||||||
|
|||||||
Reference in New Issue
Block a user