Bring back the external API keys
Commit0dbb13fremoved the whole feature: the apikeys tab, the /ui/apikeys routes, requireApiKey, the /api/v1 router, the ApiKey type, the better-sqlite3 dependency and UI_DB_FILE in the CLI and the installer. Only the untracked awg-ui/API.md survived, so the README went on to claim there was no key-based API at all. Restores all of it: ui.db with the api_keys table, awgk_ key generation storing only the SHA-256 and a display prefix, the JWT-guarded CRUD, and the /api/v1 router mounted before the JWT proxy on /api so the key auth is not swallowed. The tab comes back as its own folder and the registry picks it up on its own. UI_DB_FILE points at /etc/amnezia/amneziawg/ui.db, outside the install tree, so reinstalling keeps the keys; a fresh install moves the old one aside with its WAL sidecars. better-sqlite3 takes the slot of jsonwebtoken, which arrived with the MA7 integration and has been unused since068cab4removed it. One defect is not reproduced: the /api/v1 handlers called ctrl() without a try/catch, and Express 4 does not catch async rejections, so a single request with awg-ctrl down killed the panel process. They now answer 502 like proxy() already did. The API contract is documented in the README rather than linked: .gitignore excludes every markdown but README.md, so awg-ui/API.md is not in the repo and the link would break on a clone. Checked against a running server: login, key create/list/delete, 400 on a bad label, 401 without a JWT, "API key required" rather than "Unauthorized" on /api/v1, "Invalid API key" for a deleted key, last_used updating, and all three /api/v1 routes returning 502 with the process still alive.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (c) 2026 Ivan Vasilev
|
||||
This source code is licensed under the MIT license found in the
|
||||
LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
/* Стили вкладки «API-ключи». Общая дизайн-система — в App.css. */
|
||||
|
||||
/* Открытый API-ключ (показывается один раз при создании) */
|
||||
.apikey-value {
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
color: var(--on-surface);
|
||||
background: var(--surface-container);
|
||||
border: 1px solid var(--outline-variant);
|
||||
border-radius: 12px;
|
||||
padding: 12px 14px;
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.apikey-cards-list { display: none; flex-direction: column; gap: 12px; }
|
||||
|
||||
.apikey-count { font-size: 12px; color: var(--neutral-text); }
|
||||
.apikey-empty {
|
||||
font-size: 14px;
|
||||
color: var(--neutral-text);
|
||||
text-align: center;
|
||||
padding: 48px 0;
|
||||
}
|
||||
.apikey-card {
|
||||
background: var(--surface-bright);
|
||||
border: 1px solid var(--outline-variant);
|
||||
border-radius: var(--r-card);
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.apikey-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
.apikey-card-title {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.apikey-card-num { font-size: 12px; color: var(--neutral-dot); flex-shrink: 0; }
|
||||
.apikey-card-label {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--on-surface);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.apikey-card-prefix {
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
color: var(--neutral-text);
|
||||
word-break: break-all;
|
||||
}
|
||||
.apikey-card-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--neutral-text);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.apikey-cards-list { display: flex; }
|
||||
}
|
||||
Reference in New Issue
Block a user