Мастер первичной настройки и nodectl: добавлены пошаговый /setup, управление узлом через virtuality-ctl и тесты

- web/nodectl.py: факты об узле (сеть, диски, часовые пояса, KVM) и действия через virtuality-ctl
- web/features/setup.py + шаблоны setup*: мастер из 8 шагов (установка, часовой пояс, пароль, HTTPS, сеть, хранилище, обновления, итог)
- безопасное переключение на мост br0 с авто-откатом и подтверждением из браузера
- virtuality-ctl storage-use: перенос данных на отдельный диск
- панель перенаправляет на мастер до его завершения; демо-режим VIRTUALITY_DEV_SETUP
- tests/test_setup.py: 8 тестов на маршруты мастера

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Этот коммит содержится в:
Claude
2026-09-25 14:36:32 +00:00
родитель 7bf7a0f6b7
Коммит 8a4474a305
17 изменённых файлов: 1217 добавлений и 0 удалений
+30
Просмотреть файл
@@ -0,0 +1,30 @@
{% extends "setup_base.html" %}
{% from "_ui.html" import icon, alert %}
{% block rail %}{% endblock %}
{% block step %}
<div class="card-header"><div><h2>Применяем настройки</h2><p>Панель перезапускается — это занимает несколько секунд.</p></div></div>
<div class="card-body stack" data-applying data-url="{{ final_url }}">
<div class="row" style="gap:14px"><span class="spinner" style="width:22px;height:22px;color:var(--brand)"></span><strong data-applying-status>Перезапускаем панель…</strong></div>
<div class="progress indeterminate"><span></span></div>
<div class="kv"><div><span>Новый адрес панели</span><strong class="mono"><a href="{{ final_url }}">{{ final_url }}</a></strong></div></div>
{% if tls %}
<div class="alert info">{{ icon('shield-check') }}<div><strong>Браузер предупредит о сертификате</strong>Сертификат создан этим сервером, поэтому браузер его не знает. Нажмите «Дополнительно» → «Перейти на сайт» (Chrome) или «Принять риск и продолжить» (Firefox). Дальше предупреждение не появится.</div></div>
{% endif %}
<p class="subtle small">Сохраните новый адрес в закладки. Если страница не открылась сама через 15 секунд, перейдите по ссылке выше.</p>
</div>
<div class="card-footer"><a class="btn btn-primary btn-lg" href="{{ final_url }}">Открыть панель{{ icon('arrow-right') }}</a></div>
{% endblock %}
{% block scripts %}
<script>
(function () {
var url = document.querySelector('[data-applying]').dataset.url;
var status = document.querySelector('[data-applying-status]');
var left = 8;
var timer = setInterval(function () {
left -= 1;
status.textContent = 'Открываем панель по новому адресу через ' + left + ' с…';
if (left <= 0) { clearInterval(timer); location.href = url; }
}, 1000);
})();
</script>
{% endblock %}