Changeset 0.37.7 (#219)
Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
97
src/js/sw/surfaces/settings/gitkeys.js
Normal file
97
src/js/sw/surfaces/settings/gitkeys.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Settings > Git Keys — SSH credential management
|
||||
*/
|
||||
const { html } = window;
|
||||
const { useState, useEffect, useCallback } = hooks;
|
||||
|
||||
function fmtDate(d) {
|
||||
if (!d) return '\u2014';
|
||||
return new Date(d).toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'short' });
|
||||
}
|
||||
|
||||
export default function GitKeysSection() {
|
||||
const [keys, setKeys] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showAdd, setShowAdd] = useState(false);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
try {
|
||||
const data = await sw.api.git.credentials.list();
|
||||
setKeys(Array.isArray(data) ? data : data.data || []);
|
||||
} catch (e) { sw.toast(e.message, 'error'); }
|
||||
finally { setLoading(false); }
|
||||
}, []);
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
async function addKey(e) {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const name = form.name.value.trim();
|
||||
const key = form.key.value.trim();
|
||||
if (!name) { sw.toast('Name is required', 'error'); return; }
|
||||
try {
|
||||
await sw.api.git.credentials.create({ name, public_key: key || undefined });
|
||||
sw.toast('Credential added', 'success');
|
||||
form.reset();
|
||||
setShowAdd(false);
|
||||
load();
|
||||
} catch (e) { sw.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteKey(id) {
|
||||
const ok = await sw.confirm('Delete this SSH credential?', true);
|
||||
if (!ok) return;
|
||||
try {
|
||||
await sw.api.git.credentials.del(id);
|
||||
sw.toast('Credential deleted', 'success');
|
||||
load();
|
||||
} catch (e) { sw.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
if (loading) return html`<div class="settings-placeholder">Loading\u2026</div>`;
|
||||
|
||||
return html`
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||
<span class="text-muted" style="font-size:12px;">${keys.length} credential${keys.length !== 1 ? 's' : ''}</span>
|
||||
<div style="flex:1"></div>
|
||||
<button class="btn-md btn-primary" onClick=${() => setShowAdd(!showAdd)}>
|
||||
${showAdd ? 'Cancel' : '+ Add Key'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
${showAdd && html`
|
||||
<form class="settings-section" style="margin-bottom:16px;" onSubmit=${addKey}>
|
||||
<div class="form-group">
|
||||
<label>Name</label>
|
||||
<input name="name" placeholder="e.g. deploy-key-prod" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Public Key <span class="text-muted" style="font-weight:400;">(paste, or leave blank to generate)</span></label>
|
||||
<textarea name="key" rows="3" placeholder="ssh-ed25519 AAAA..." style="font-family:var(--mono);font-size:12px;" />
|
||||
</div>
|
||||
<button type="submit" class="btn-small btn-primary">Add Credential</button>
|
||||
</form>
|
||||
`}
|
||||
|
||||
<div class="admin-list">
|
||||
${keys.length === 0 && html`<div class="empty-hint">No SSH credentials configured.</div>`}
|
||||
${keys.map(k => html`
|
||||
<div class="admin-surface-row" key=${k.id}>
|
||||
<div style="flex:1;min-width:0;">
|
||||
<strong>${k.name}</strong>
|
||||
${k.fingerprint && html`
|
||||
<span class="text-muted" style="margin-left:8px;font-size:11px;font-family:var(--mono);">
|
||||
${k.fingerprint.substring(0, 24)}\u2026
|
||||
</span>
|
||||
`}
|
||||
</div>
|
||||
<span class="text-muted" style="font-size:11px;">${fmtDate(k.created_at)}</span>
|
||||
<button class="btn-small btn-danger" onClick=${() => deleteKey(k.id)}>Delete</button>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user