feat: SSH key management on dev pod page — list, add, remove keys
Some checks failed
Build & Release / build (push) Has been cancelled
Some checks failed
Build & Release / build (push) Has been cancelled
This commit is contained in:
@@ -1067,8 +1067,19 @@
|
||||
<span class="stat-value" style="font-family:monospace;font-size:0.8rem">dev</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card stat-card-full" id="devpod-keys-card">
|
||||
<div class="card-title">SSH Keys</div>
|
||||
<div id="devpod-keys-list" style="color:#8b949e;font-size:0.845rem">Loading…</div>
|
||||
<div style="display:flex;gap:0.5rem;margin-top:1rem">
|
||||
<input id="devpod-key-input" type="text" placeholder="ssh-ed25519 AAAA… user@host"
|
||||
style="flex:1;background:#161b22;border:1px solid #30363d;border-radius:6px;padding:0.4rem 0.6rem;color:#e6edf3;font-family:monospace;font-size:0.78rem" />
|
||||
<button class="btn btn-primary btn-sm" onclick="addSshKey()">Add Key</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
loadSshKeys();
|
||||
|
||||
// Auto-refresh while starting
|
||||
clearInterval(devpodRefreshTimer);
|
||||
if (state === 'starting') {
|
||||
@@ -1095,6 +1106,60 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSshKeys() {
|
||||
const el = document.getElementById('devpod-keys-list');
|
||||
if (!el) return;
|
||||
try {
|
||||
const res = await apiFetch('/api/devpod/keys');
|
||||
const { keys } = await res.json();
|
||||
renderSshKeys(keys);
|
||||
} catch {
|
||||
if (el) el.innerHTML = '<span style="color:#f85149">Failed to load keys</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderSshKeys(keys) {
|
||||
const el = document.getElementById('devpod-keys-list');
|
||||
if (!el) return;
|
||||
if (!keys.length) { el.innerHTML = '<span style="color:#484f58">No SSH keys configured</span>'; return; }
|
||||
el.innerHTML = keys.map(k => {
|
||||
const parts = k.split(' ');
|
||||
const label = parts.slice(2).join(' ') || parts[0];
|
||||
const short = parts[1] ? parts[1].slice(0, 20) + '…' : k;
|
||||
return `<div style="display:flex;justify-content:space-between;align-items:center;padding:0.35rem 0;border-bottom:1px solid #21262d">
|
||||
<span style="font-family:monospace;font-size:0.78rem;color:#e6edf3">${label} <span style="color:#484f58">${short}</span></span>
|
||||
<button class="btn btn-danger btn-sm" onclick="removeSshKey(${JSON.stringify(k)})">Remove</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function addSshKey() {
|
||||
const input = document.getElementById('devpod-key-input');
|
||||
const key = input.value.trim();
|
||||
if (!key) return;
|
||||
try {
|
||||
const res = await apiFetch('/api/devpod/keys', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ key }) });
|
||||
if (!res.ok) throw new Error((await res.json()).message);
|
||||
const { keys } = await res.json();
|
||||
renderSshKeys(keys);
|
||||
input.value = '';
|
||||
} catch (err) {
|
||||
alert('Failed to add key: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function removeSshKey(key) {
|
||||
if (!confirm('Remove this SSH key?')) return;
|
||||
try {
|
||||
const res = await apiFetch('/api/devpod/keys', { method: 'DELETE', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ key }) });
|
||||
if (!res.ok) throw new Error((await res.json()).message);
|
||||
const { keys } = await res.json();
|
||||
renderSshKeys(keys);
|
||||
} catch (err) {
|
||||
alert('Failed to remove key: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Account ───────────────────────────────────────────────────────────────
|
||||
|
||||
async function changePassword() {
|
||||
|
||||
Reference in New Issue
Block a user