151 lines
9.0 KiB
HTML
151 lines
9.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>Autotest</title>
|
|
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}" />
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
|
|
<style>
|
|
.topbar { max-width:1200px; margin:0 auto; padding:10px 16px 0; display:flex; align-items:center; gap:12px; }
|
|
.layout { display:flex; gap:12px; max-width:1200px; margin:8px auto; padding:0 16px; }
|
|
.col-infra { width:280px; flex-shrink:0; }
|
|
.col-svc { width:240px; flex-shrink:0; max-height:calc(100vh - 50px); overflow-y:auto; }
|
|
.col-main { flex:1; min-width:0; }
|
|
#inst-list { max-height:calc(100vh - 250px); overflow-y:auto; }
|
|
.svc-item { cursor:pointer; padding:4px 8px; font-size:12px; border-radius:4px; }
|
|
.svc-item:hover, .svc-item.active { background:#dbeafe; font-weight:600; }
|
|
.inst-item { cursor:pointer; display:flex; align-items:center; gap:6px; padding:4px 8px; font-size:12px; border-radius:4px; }
|
|
.inst-item:hover, .inst-item.active { background:var(--brand-grey-light); }
|
|
.inst-ops { display:none; margin-left:16px; }
|
|
.inst-ops.open { display:flex; flex-wrap:wrap; gap:4px; padding:4px 0; }
|
|
.btn-sm { height:24px; font-size:11px; padding:0 8px; }
|
|
.op-btn { border-color:#d1d5db; color:#374151; background:#f8fafc; }
|
|
.op-btn:hover { background:#eef2f7; }
|
|
.op-btn-modify { background:#eef2ff; border-color:#c7d2fe; color:#3730a3; }
|
|
.op-btn-modify:hover { background:#e0e7ff; }
|
|
.op-btn-suspend { background:#fff7ed; border-color:#fed7aa; color:#9a3412; }
|
|
.op-btn-suspend:hover { background:#ffedd5; }
|
|
.op-btn-resume { background:#ecfeff; border-color:#a5f3fc; color:#155e75; }
|
|
.op-btn-resume:hover { background:#cffafe; }
|
|
.op-btn-delete { background:#fef2f2; border-color:#fecaca; color:#b91c1c; }
|
|
.op-btn-delete:hover { background:#fee2e2; }
|
|
.op-btn-redeploy { background:#f5f3ff; border-color:#ddd6fe; color:#6d28d9; }
|
|
.op-btn-redeploy:hover { background:#ede9fe; }
|
|
.op-btn-reconcile { background:#f8fafc; border-color:#dbe4ea; color:#475569; }
|
|
.op-btn-reconcile:hover { background:#eef2f7; }
|
|
.param-row { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
|
|
.param-row label { width:160px; font-size:11px; text-align:right; flex-shrink:0; }
|
|
.param-row label.req { font-weight:600; }
|
|
.param-row label.req-nodfl { font-weight:600; color:var(--destructive); }
|
|
.param-row input,.param-row select { flex:1; height:28px; font-size:12px; border:1px solid var(--brand-gray); border-radius:4px; padding:0 6px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!--
|
|
=== ПЕРЕМЕННЫЕ ШАБЛОНА JINJA2 ===
|
|
config.VERSION — версия приложения (топбар)
|
|
token_info.email — email пользователя (топбар)
|
|
token_info.company — профиль/компания (топбар)
|
|
stand — стенд: dev/test (топбар)
|
|
env_token_masked — маскированный env-токен (placeholder)
|
|
has_user_token — True если пользователь ввёл свой токен
|
|
organization — организация (serviceId=19)
|
|
instance_groups — {svc_name: [instances]} для левой колонки
|
|
error — сообщение об ошибке
|
|
-->
|
|
|
|
<div class="topbar">
|
|
<img src="{{ url_for('static', filename='logo.svg') }}" alt="Nubes" style="height:22px;" />
|
|
<span style="font-size:11px;color:var(--muted);">v{{ config.VERSION }}</span>
|
|
{% if token_info.email %}<span style="font-size:11px;color:var(--muted);">EMail: {{ token_info.email }} | Профиль: {{ token_info.company }} | Стенд: {{ stand.upper() }}</span>{% endif %}
|
|
<span style="flex:1;"></span>
|
|
<form method="post" style="display:flex;align-items:center;gap:6px;">
|
|
<input type="password" name="token" placeholder="env: {{ env_token_masked }}" value="{{ '' if not has_user_token else '••••••••' }}" style="height:28px;width:180px;font-size:12px;border:1px solid var(--brand-gray);border-radius:4px;padding:0 6px;" />
|
|
<button type="submit" name="action" value="save" class="btn" style="height:28px;font-size:12px;padding:0 8px;">OK</button>
|
|
{% if has_user_token %}<button type="submit" name="action" value="clear" class="btn btn-danger" style="height:28px;font-size:12px;padding:0 8px;">Выйти</button>{% endif %}
|
|
</form>
|
|
{% if error %}<span style="color:var(--destructive);font-size:12px;">{{ error }}</span>{% endif %}
|
|
</div>
|
|
|
|
<div class="layout">
|
|
<!-- Инфраструктура -->
|
|
<div class="col-infra">
|
|
{% if organization %}
|
|
<div class="card">
|
|
<div class="card-header">Организация</div>
|
|
<div class="card-body" style="padding:6px 10px;font-size:12px;">
|
|
{{ organization.displayName }}{% if client_id %} ({{ client_id }}){% endif %}<br>
|
|
<span class="badge badge-success" style="font-size:10px;">{{ organization.explainedStatus or "OK" }}</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% for svc_name, items in instance_groups.items() %}
|
|
<div class="card" style="margin-top:6px;">
|
|
<div class="card-header" style="font-size:12px;cursor:pointer;" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display==='none'?'block':'none'">{{ svc_name }} ({{ items|length }})</div>
|
|
<div class="card-body" style="padding:0;max-height:150px;overflow-y:auto;">
|
|
<table>
|
|
{% for i in items %}
|
|
<tr><td style="font-size:11px;padding:2px 8px;">{{ i.displayName }}</td><td><span class="badge badge-success" style="font-size:9px;">{{ i.explainedStatus or '?' }}</span></td></tr>
|
|
{% endfor %}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Сервисы -->
|
|
<div class="col-svc">
|
|
<div class="card">
|
|
<div class="card-header" style="font-size:12px;">Сервисы</div>
|
|
<div class="card-body" style="padding:4px;">
|
|
{% for svc in services %}{% if svc.svcId in config.service_ids %}<div class="svc-item {% if loop.first %}active{% endif %}" onclick="selectService({{ svc.svcId }})">{{ svc.svcId }}. {{ svc.svc }}</div>{% endif %}{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Инстансы + Операции + Параметры -->
|
|
<div class="col-main">
|
|
<div class="card">
|
|
<div class="card-header" style="font-size:12px;">Инстансы</div>
|
|
<div class="card-body" style="padding:4px;" id="inst-list">
|
|
<span style="color:var(--muted);font-size:11px;">Загрузка...</span>
|
|
</div>
|
|
</div>
|
|
<div id="params-area" style="display:none;padding:0 8px 8px;">
|
|
<div id="params-form"></div>
|
|
<div style="padding:4px 0;">
|
|
<button class="btn btn-primary btn-sm" id="btn-test" style="display:none;">Запустить</button>
|
|
<span id="test-status" style="font-size:11px;margin-left:8px;"></span>
|
|
</div>
|
|
</div>
|
|
<div id="create-btn-area" style="padding:0 8px 8px;">
|
|
<div class="inst-item" style="border-top:1px solid var(--brand-gray);padding-top:8px;margin-top:4px;" onclick="startCreate()">
|
|
<span style="flex:1;color:var(--brand-primary);cursor:pointer;">+ Создать новый инстанс</span>
|
|
</div>
|
|
</div>
|
|
<div class="card" id="history-card" style="margin-top:8px;">
|
|
<div class="card-header" style="font-size:12px;cursor:pointer;" onclick="toggleHistory()">История <span style="color:var(--muted);font-size:10px;">(последние 50)</span></div>
|
|
<div class="card-body" style="padding:4px;display:none;max-height:300px;overflow-y:auto;" id="history-body">
|
|
<span style="color:var(--muted);font-size:11px;">Загрузка...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Переменные из Jinja2 для app.js
|
|
window.APP = {
|
|
version: "{{ config.VERSION | tojson }}",
|
|
stand: "{{ stand | tojson }}",
|
|
hasUserToken: {{ has_user_token | tojson }},
|
|
firstServiceId: {{ (config.service_ids[0] if config.service_ids else 1) | tojson }}
|
|
};
|
|
</script>
|
|
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
|
|
|
<div id="log-panel" style="display:none;position:fixed;bottom:0;left:0;right:0;height:180px;background:#1e1e1e;color:#d4d4d4;font:11px monospace;overflow-y:auto;padding:6px 10px;border-top:2px solid #555;z-index:9999;"></div>
|
|
<button onclick="toggleLog()" title="Логи" style="position:fixed;bottom:4px;right:8px;z-index:10000;background:#1e1e1e;color:#888;border:1px solid #555;border-radius:3px;font:10px monospace;padding:2px 6px;cursor:pointer;">log</button>
|
|
|
|
</body>
|
|
</html> |