v0.1.29: светлый Nubes-дизайн консоли, читаемая модалка Credentials (env + файл кредов)

This commit is contained in:
“Naeel”
2026-08-14 09:56:18 +04:00
parent a85e086c94
commit cfea73cdea
2 changed files with 92 additions and 52 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
# Registry: Docker Hub (naeel/shared-sqs) # Registry: Docker Hub (naeel/shared-sqs)
IMAGE_REPO=naeel/shared-sqs IMAGE_REPO=naeel/shared-sqs
VERSION=v0.1.28 VERSION=v0.1.29
LDFLAGS=-X shared-sqs/app/models.Version=$(VERSION) LDFLAGS=-X shared-sqs/app/models.Version=$(VERSION)
BINARY=shared-sqs BINARY=shared-sqs
+91 -51
View File
@@ -14,23 +14,25 @@
<title>SQS Console — Nubes</title> <title>SQS Console — Nubes</title>
<link rel="icon" href="https://terra.k8c.ru/docs/nubes/nubes/2.0.2/30_registry/assets/favicon.png"> <link rel="icon" href="https://terra.k8c.ru/docs/nubes/nubes/2.0.2/30_registry/assets/favicon.png">
<style> <style>
/* Nubes palette — идентично iot.kube5s.ru/console */ /* Nubes design system — светлая тема (deck) */
:root { :root {
--bg-page: #001120; --bg-page: #fafafa;
--bg-surface: #001929; --bg-surface: #ffffff;
--bg-navbar: #001C34; --bg-navbar: #ffffff;
--border: #0b2d50; --border: #d1d5db;
--accent: #1a7fd4; --accent: #2563eb;
--accent-hover: #2196f3; --accent-hover: #1d4ed8;
--text-primary: #e2ecf6; --text-primary: #1a1a1a;
--text-secondary: #6b8eaa; --text-secondary: #6b7280;
--danger: #e74c3c; --danger: #dc2626;
--success: #27ae60; --success: #16a34a;
--warning: #f39c12; --warning: #d97706;
--brand-grey-light: #f3f4f6;
} }
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
background: var(--bg-page); background: var(--bg-page);
color: var(--text-primary); color: var(--text-primary);
min-height: 100vh; min-height: 100vh;
@@ -55,7 +57,6 @@ a:hover { color: var(--accent-hover); }
} }
.navbar-brand img { .navbar-brand img {
height: 28px; height: 28px;
filter: brightness(0) invert(1);
} }
.navbar-brand span { .navbar-brand span {
font-size: 16px; font-size: 16px;
@@ -88,7 +89,8 @@ a:hover { color: var(--accent-hover); }
.card { .card {
background: var(--bg-surface); background: var(--bg-surface);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 12px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
padding: 20px; padding: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -111,15 +113,17 @@ a:hover { color: var(--accent-hover); }
.stat-card { .stat-card {
background: var(--bg-surface); background: var(--bg-surface);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 12px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
.stat-value { .stat-value {
font-size: 36px; font-size: 30px;
font-weight: 700; font-weight: 700;
color: var(--accent); color: var(--text-primary);
} }
.stat-value.accent { color: var(--accent); }
.stat-label { .stat-label {
font-size: 12px; font-size: 12px;
color: var(--text-secondary); color: var(--text-secondary);
@@ -149,7 +153,7 @@ tbody td {
padding: 12px 16px; padding: 12px 16px;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
tbody tr:hover { background: rgba(26, 127, 212, 0.05); } tbody tr:hover { background: var(--brand-grey-light); }
tbody tr { cursor: pointer; } tbody tr { cursor: pointer; }
/* Buttons */ /* Buttons */
@@ -168,8 +172,8 @@ tbody tr { cursor: pointer; }
.btn-primary { background: var(--accent); color: #fff; } .btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); } .btn-primary:hover { background: var(--accent-hover); }
.btn-danger { background: var(--danger); color: #fff; } .btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { background: #c0392b; } .btn-danger:hover { background: #b91c1c; }
.btn-sm { padding: 4px 12px; font-size: 12px; } .btn-sm { padding: 4px 12px; font-size: 13px; }
/* Forms */ /* Forms */
.form-group { margin-bottom: 16px; } .form-group { margin-bottom: 16px; }
@@ -182,9 +186,9 @@ tbody tr { cursor: pointer; }
.form-group input { .form-group input {
width: 100%; width: 100%;
padding: 10px 14px; padding: 10px 14px;
background: var(--bg-page); background: #ffffff;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 4px; border-radius: 8px;
color: var(--text-primary); color: var(--text-primary);
font-size: 14px; font-size: 14px;
} }
@@ -208,10 +212,10 @@ tbody tr { cursor: pointer; }
width: 400px; width: 400px;
text-align: center; text-align: center;
} }
.login-box img { height: 40px; filter: brightness(0) invert(1); margin-bottom: 12px; } .login-box img { height: 40px; margin-bottom: 12px; }
.login-box h1 { .login-box h1 {
font-size: 18px; font-size: 22px;
letter-spacing: 2px; letter-spacing: 1px;
margin-bottom: 32px; margin-bottom: 32px;
color: var(--text-primary); color: var(--text-primary);
} }
@@ -225,15 +229,15 @@ tbody tr { cursor: pointer; }
/* Badges */ /* Badges */
.badge { .badge {
display: inline-block; display: inline-block;
padding: 2px 8px; padding: 2px 10px;
border-radius: 10px; border-radius: 999px;
font-size: 11px; font-size: 12px;
font-weight: 600; font-weight: 600;
} }
.badge-active { background: rgba(39,174,96,0.15); color: var(--success); } .badge-active { background: rgba(22,163,74,0.12); color: var(--success); }
.badge-inactive { background: rgba(231,76,60,0.15); color: var(--danger); } .badge-inactive { background: rgba(220,38,38,0.1); color: var(--danger); }
.badge-count { .badge-count {
background: rgba(26,127,212,0.15); background: rgba(37,99,235,0.1);
color: var(--accent); color: var(--accent);
min-width: 24px; min-width: 24px;
text-align: center; text-align: center;
@@ -252,7 +256,7 @@ tbody tr { cursor: pointer; }
.modal-overlay { .modal-overlay {
position: fixed; position: fixed;
inset: 0; inset: 0;
background: rgba(0,0,0,0.6); background: rgba(0,0,0,0.45);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -262,12 +266,15 @@ tbody tr { cursor: pointer; }
background: var(--bg-surface); background: var(--bg-surface);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 12px; border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
padding: 32px; padding: 32px;
width: 480px; width: 560px;
max-width: 90vw; max-width: 92vw;
max-height: 88vh;
overflow-y: auto;
} }
.modal h2 { .modal h2 {
font-size: 18px; font-size: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.modal-actions { .modal-actions {
@@ -304,7 +311,7 @@ tbody tr { cursor: pointer; }
margin-bottom: 16px; margin-bottom: 16px;
} }
.toolbar h2 { .toolbar h2 {
font-size: 18px; font-size: 20px;
font-weight: 600; font-weight: 600;
} }
.auto-refresh { .auto-refresh {
@@ -315,6 +322,36 @@ tbody tr { cursor: pointer; }
color: var(--text-secondary); color: var(--text-secondary);
} }
/* Credentials modal — подсказка */
.creds-hint {
font-size: 13px;
color: #374151;
background: var(--brand-grey-light);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px 14px;
margin-bottom: 16px;
line-height: 1.6;
}
.creds-hint .warn {
color: var(--warning);
font-weight: 600;
}
.creds-hint p { margin: 6px 0; }
.creds-hint .creds-sub { font-weight: 600; margin-top: 10px; color: var(--text-primary); }
.creds-hint pre {
background: #ffffff;
border: 1px solid var(--border);
border-radius: 6px;
padding: 8px 10px;
font-family: ui-monospace, Menlo, Consolas, monospace;
font-size: 12px;
line-height: 1.5;
overflow-x: auto;
white-space: pre;
margin: 6px 0 10px;
}
/* Expandable message rows */ /* Expandable message rows */
td.msg-expand { padding: 0 !important; border-bottom: 1px solid var(--border); } td.msg-expand { padding: 0 !important; border-bottom: 1px solid var(--border); }
.msg-expand-inner { padding: 12px 20px; background: var(--bg-page); } .msg-expand-inner { padding: 12px 20px; background: var(--bg-page); }
@@ -347,7 +384,7 @@ td.msg-expand { padding: 0 !important; border-bottom: 1px solid var(--border); }
<div>Admin API: <span style="font-family:monospace">POST /admin/tenants</span> (Bearer token)</div> <div>Admin API: <span style="font-family:monospace">POST /admin/tenants</span> (Bearer token)</div>
<div>Realm: <span style="font-family:monospace">iot-naeel</span> · Persistence: Managed Redis</div> <div>Realm: <span style="font-family:monospace">iot-naeel</span> · Persistence: Managed Redis</div>
<div>Версия: <span id="svc-version" style="font-family:monospace">—</span></div> <div>Версия: <span id="svc-version" style="font-family:monospace">—</span></div>
<div>Образ: <span style="font-family:monospace">naeel/shared-sqs:v0.1.28</span></div> <div>Образ: <span style="font-family:monospace">naeel/shared-sqs:v0.1.29</span></div>
<div style="margin-top:8px;color:var(--warning)">Статус: тестирование</div> <div style="margin-top:8px;color:var(--warning)">Статус: тестирование</div>
</div> </div>
</div> </div>
@@ -397,16 +434,19 @@ td.msg-expand { padding: 0 !important; border-bottom: 1px solid var(--border); }
<div id="modal-creds" class="modal-overlay hidden" onclick="if(event.target===this)closeCredsModal()"> <div id="modal-creds" class="modal-overlay hidden" onclick="if(event.target===this)closeCredsModal()">
<div class="modal"> <div class="modal">
<h2>Credentials</h2> <h2>Credentials</h2>
<p style="color:var(--warning);font-size:13px;margin-bottom:12px"> <div class="creds-hint">
⚠ Сохраните ключи — это креды вашего тенанта для AWS CLI/SDK. <p class="warn">⚠ Сохраните ключи — это креды вашего тенанта для AWS CLI/SDK.</p>
</p> <p><strong>Зачем:</strong> приложения подключаются к SQS API по протоколу AWS (подпись Signature V4). Токен входа в консоль для этого не подходит.</p>
<div style="font-size:12px;color:var(--text-secondary);background:var(--bg-page);border:1px solid var(--border);border-radius:4px;padding:10px 12px;margin-bottom:16px;line-height:1.8;word-break:break-all"> <p><strong>Что делать:</strong> укажите ключи одним из двух способов:</p>
<strong>Зачем:</strong> приложения подключаются к SQS API по протоколу AWS <p class="creds-sub">Способ 1 — переменные окружения:</p>
(подпись Signature V4). Токен входа в консоль для этого не подходит.<br> <pre>export AWS_ACCESS_KEY_ID=&lt;Access Key&gt;
<strong>Что делать:</strong><br> export AWS_SECRET_ACCESS_KEY=&lt;Secret Key&gt;</pre>
<code>export AWS_ACCESS_KEY_ID=&lt;Access Key&gt;</code><br> <p class="creds-sub">Способ 2 — файл ~/.aws/credentials:</p>
<code>export AWS_SECRET_ACCESS_KEY=&lt;Secret Key&gt;</code><br> <pre>[sqs]
<code>aws sqs list-queues --endpoint-url https://sqs.containerk8s.dev.nubes.ru --region us-east-1</code> aws_access_key_id = &lt;Access Key&gt;
aws_secret_access_key = &lt;Secret Key&gt;</pre>
<p>Дальше выполняйте команды (для способа 2 добавьте <code>--profile sqs</code>):</p>
<pre>aws sqs list-queues --endpoint-url https://sqs.containerk8s.dev.nubes.ru --region us-east-1 --profile sqs</pre>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Access Key</label> <label>Access Key</label>
@@ -428,7 +468,7 @@ td.msg-expand { padding: 0 !important; border-bottom: 1px solid var(--border); }
<h2>Отправить сообщение</h2> <h2>Отправить сообщение</h2>
<div class="form-group"> <div class="form-group">
<label for="send-body">Тело сообщения</label> <label for="send-body">Тело сообщения</label>
<textarea id="send-body" rows="6" style="width:100%;background:var(--bg-page);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);padding:10px 14px;font-family:monospace;font-size:13px;resize:vertical"></textarea> <textarea id="send-body" rows="6" style="width:100%;background:#ffffff;border:1px solid var(--border);border-radius:8px;color:var(--text-primary);padding:10px 14px;font-family:monospace;font-size:13px;resize:vertical"></textarea>
</div> </div>
<div id="send-error" class="login-error"></div> <div id="send-error" class="login-error"></div>
<div class="modal-actions"> <div class="modal-actions">
@@ -448,7 +488,7 @@ td.msg-expand { padding: 0 !important; border-bottom: 1px solid var(--border); }
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Body (нажмите, чтобы скопировать)</label> <label>Body (нажмите, чтобы скопировать)</label>
<textarea id="msg-detail-body" readonly rows="8" style="width:100%;background:var(--bg-page);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);padding:10px 14px;font-family:monospace;font-size:13px;resize:vertical;cursor:pointer" onclick="copyTextarea(this)"></textarea> <textarea id="msg-detail-body" readonly rows="8" style="width:100%;background:#ffffff;border:1px solid var(--border);border-radius:8px;color:var(--text-primary);padding:10px 14px;font-family:monospace;font-size:13px;resize:vertical;cursor:pointer" onclick="copyTextarea(this)"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Отправлено</label> <label>Отправлено</label>
@@ -988,7 +1028,7 @@ function renderQueueMessages(queueName, msgs) {
<tbody> <tbody>
${msgs.map(m => ` ${msgs.map(m => `
<tr onclick="openMsgDetail('${esc(m.id)}')" style="cursor:pointer" <tr onclick="openMsgDetail('${esc(m.id)}')" style="cursor:pointer"
onmouseover="this.style.background='rgba(26,127,212,0.08)'" onmouseout="this.style.background=''"> onmouseover="this.style.background='var(--brand-grey-light)'" onmouseout="this.style.background=''">
<td style="padding:6px 12px;font-family:monospace;color:var(--text-secondary)">${esc(m.id).substring(0,8)}…</td> <td style="padding:6px 12px;font-family:monospace;color:var(--text-secondary)">${esc(m.id).substring(0,8)}…</td>
<td style="padding:6px 12px;max-width:380px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(m.body || '').substring(0,120)}${(m.body||'').length>120?'…':''}</td> <td style="padding:6px 12px;max-width:380px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(m.body || '').substring(0,120)}${(m.body||'').length>120?'…':''}</td>
<td style="padding:6px 12px;color:var(--text-secondary)">${fmtDate(m.sent_at)}</td> <td style="padding:6px 12px;color:var(--text-secondary)">${fmtDate(m.sent_at)}</td>