Conversation
…), новый маршрут для теле2 в белых списках, обновил SNI
- BUG-1: tcp-xudp нормализация в change_port_menu (ложная ошибка "порт занят") - BUG-2: open_firewall_port при добавлении клиента в existing inbound - BUG-3: open_firewall_port активирует UFW если он неактивен - BUG-4: change_port_menu извлекает клиента при shared inbound вместо переноса всего - BUG-5: delete_profile удаляет UUID только из конкретного inbound по порту - BUG-6: close_firewall_port не закрывает порт при ошибке jq - BUG-7: add_inbound проверяет совместимость транспорта - BUG-8: install.sh проверяет ошибки UFW и валидирует активность - BUG-9: change_port_menu обновляет fingerprint при переносе в existing inbound Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
CRLF в Windows-чекауте ломал bash-скрипты на сервере (символ \r). .gitattributes нормализует LF для всех скриптов. .gitignore дополнен артефактами сборки (exe/dmg) и служебными файлами.
На Windows-чекауте core.autocrlf превращает LF→CRLF; sftp.put заливал их бинарно, и bash на сервере падал (символ \r, сломанный shebang). Добавлен upload_text() который стрипает \r; deploy.py загружает install.sh/xrayebator через него. TestDeploy FakeSSH расширен upload_text (обновлён контракт).
Unix-only модули grp/pwd импортировались на верхнем уровне service.py, из-за чего любой импорт пакета падал на Windows. Импорты перенесены в authorized_peer() и main() — helper всё равно работает только на Linux, но импорт пакета/тестирование/пакAGING теперь валидны и на Windows.
…/opt) CORE_BINARY ссылался на /usr/lib/xrayebator-gui/xray, но install-helper.sh ставит ядро в /opt/xrayebator-gui. unit явно передаёт --core, но ручной запуск helper без --core падал. Теперь константа = /opt.
…сле деплоя Если D-Bus secret service недоступен (headless Linux), set_password бросал исключение в слоте _deployment_finished — сервер попадал в JSON, но _reload_servers не вызывался, пользователь не видел сервер. Теперь ошибка keyring молча глотается (сервер добавлен, пароль запомнится позже).
result[subscription_url] падал с KeyError если сервер вернул другой JSON-контракт. Теперь .get() — если URL пустой, сервер не добавляется, в лог пишется понятное сообщение.
…ll при фейле загрузки
Если curl для update.sh/uninstall.sh падал, mv не выполнялся, но ln -sf создавал symlink указывающий на несуществующий /usr/local/etc/xray/scripts/{update,uninstall}.sh. Теперь symlink создаётся только если файл есть.
curl|bash @ remove не проверялся — при недоступности GitHub выводило «✓ Xray-core удален» хотя бинарь оставался. Теперь: -c на curl, проверка rc, явное удаление /usr/local/bin/xray + share, drop-in /etc/systemd/system/xray.service.d, symlink xrayebator-update/uninstall, subhttp.sh, xrayebator-sub.service, /var/log/xray, пользователь xray, UFW-правила. НЕ трогаем весь системный журнал — только -u xray.
…н; стирать .warned при отмене; бэкап в backups/ Тавтологичная проверка (-L || -f) приводила к dangling symlink, если stub отсутствовал. Теперь: стираем только если systemd-resolved активен, stub существует и resolv.conf выглядит AdGuard-managed. При отмене чистим и .session, и .warned. BACKUP_DIR -> backups/update_<ts> (как у xrayebator).
Было: quickstart создавал одномаршрутный профиль happ (1 xhttp маршрут), HAPP показывал "route not shown / country n/a". Стало: используется _happ_ensure_default_multiroute_profile() — тот же механизм что happ-setup (общий источник истины). Возвращаем routes=N в JSON.
Собственный Quick Start в README:224 уже говорил про пункт 6, но раздел 45 упоминал 9 (self-steal stub). Исправлено в трёх языках одновременно.
Было: /usr/local/bin/subhttp и backups/<timestamp>/ (подпапки). Стало: /usr/local/bin/subhttp.sh и backups/config_<ts>_<op>.json (плоские файлы) — соответствует реальному поведению backup_config().
test-bbr-removal-migration.sh, test-legacy-udp443-migration.sh, test-main-menu-numbering.sh существовали, но не были задокументированы.
…setup quickstart используется десктопным GUI (one-shot deploy); happ-setup — повторный вход для HAPP multi-route профиля. Оба возвращают JSON с subscription_url.
Размер 2500→9341 строк. Было «no automated tests» — фактически есть validation/ (16 bash-тестов) и gui/tests/ (15 pytest). Описаны CLI-команды (quickstart/happ-setup). Исправлена ошибка про jq -s (это bash -s, не jq -s).
… quit - Добавлен QProgressBar (indeterminate marquee) — показывается при всех busy-стадиях, чтобы UI не выглядел зависшим на долгих шагах деплоя. - Статус-метка получила цветовую индикацию (зелёный=connected, красный=error, жёлтый=busy). - Логи теперь с таймстампами и цветовой маркировкой по ключевым словам. - Race condition: _toggle_connection/_switch_route/_switch_profile мгновенно блокируют UI до старта OperationThread — двойной клик не создаст два потока. - _quit() больше не блокирует GUI на 5+ секунд — disconnect идёт в OperationThread. - Traceback добавляется в error messages (OperationThread.run). - Убраны утечки секретов при ошибке деплоя (redact_log_line применяется)
- Preflight: проверка bash (sh-only без *{}*), прав root (вывод), ОС (apt-based only), systemd (ясная ошибка для OpenVZ/LXC/Docker).
- apt: ошибки не молча подавляются, предлагается DNS-fix, вывод tail.
- Reality ключи: не перегенерируются если уже существуют (resume-safe).
Добавлена state-машина с маркерами /usr/local/etc/xray/.install_step_N_ok. При повторном запуске после прерывания install.sh продолжает с того самого шага, не ломая уже созданное (Reality/VLESS ключи, config.json, Xray-core). Команды: bash install.sh --check — показать статус всех 10 шагов bash install.sh — auto-resume если половина установлена bash install.sh --resume — принудительно resume без диалога bash install.sh --fresh — сбросить маркеры, начать с нуля
QTextEdit.appendHtml() does not exist in PySide6 (verified empirically: hasattr == False; only QTextBrowser.append() supports HTML strings). Any call to _append_log (16 places) would crash with AttributeError on the very first log message. Also: text was inserted into HTML unescaped, so any <>& in bash/ssh/ paramiko output (e.g. vless:// URL with query params) would be parsed as rich-text by Qt and mangled visually. html.escape() fixes both. Impact: GUI was unusable before this commit — crashed on first deploy.
Leftover single-letter command from earlier auto-edit session. Bash treats 'c' as a (non-existent) command; with no set -e this only logs 'bash: c: command not found' to stderr, but pollutes CI logs and masks real error signals when grep'ing for failures.
Reality keys (.private_key/.public_key) at :618 already had the guard. VLESS Encryption keys at :733 did NOT — so re-running install.sh (--resume / --fresh / step 5 retry) would regenerate them and silently orphan every existing PQ-enabled profile (client encryption string no longer matches server decryption). Symmetric with the Reality guard: skip when both files exist and decryption matches the expected mlkem768x25519plus. prefix.
main_menu runs 17 marker-driven migrations on first interactive launch.
quickstart previously ran only 8 + skipped mlkem_keys_generated,
xhttp_default_2026, subscription_tokens_2026, happ_legacy_xhttp_route_2026,
xhttp_route_path_repair_2026, dead_stealth_routes_pruned_2026,
sni_list_2026, bypass_routing_2026, and all 6 subhttp_* migrations.
Impact: on an EXISTING VPS (no fresh install.sh):
- mlkem_keys_generated missing → add_inbound with pq_enabled=true fails
→ _happ_ensure_default_multiroute_profile partially creates 3-4 of 7
routes, leaving profile in inconsistent state.
- subscription_tokens_2026 missing → profiles have no sub_token,
subscription_url broken.
Now quickstart runs the full 17-migration set (still ignores
bypass_routing_2026 + sni_list_2026 since they require interaction,
and the remaining subhttp_* family which is menu-driven).
subscription_url was hardcoded to :8443 without checking the actual subscription markers. If the VPS had previously been set up with install_subscription_ip_tls on :443 (or any other port via _select_subscription_port), quickstart would emit a broken URL — client gets connection refused. Now uses the canonical helper _subscription_base_url() that reads .subscription_port and .subscription_domain, only falling back to https://<ip>:8443 when markers are absent.
Adds named helpers to install.sh, update.sh, xrayebator. Three identical inline blocks across install.sh:751, update.sh:44, update.sh:884 are replaced with \ — kills the naming drift (local query_strategy vs global QUERY_STRATEGY) and gives future migrations a single canonical way to query IPv6 availability. Behavior unchanged — only refactor. Locked separately from the uninstall_adguard_home / migrate_config_optimization fix to allow independent revert if the helper itself regresses.
…ator Mirrors the helpers already added to install.sh/update.sh. Other xrayebator callers (next commits) will use these instead of hardcoding UseIPv4, so IPv6-only VPS doesn't break DNS for AdGuard-removal path or future config migrations.
…не вызывались ни из preload, ни из renderer
…bscriptionFetchOptions
…неиспользуемый .sectionTitleIcon
…вый routes_one в zh
… app.onUpdateAvailable (preload+handlers+types)
# Conflicts: # xrayebator
…uickstart добирает миграцию subhttp_managed_happ_routing_2026
Вклад в dev: Electron GUI, CLI-команды, аудит и актуализация под new-main владельца
Синхронизация main с dev: влит PR #18 и последующий харднинг SSH-доступа из upstream/dev (shell-command, ssh-access, SshAccessForm, vless + unit-тесты). Содержимое main после мержа побайтово равно dev.
mktemp даёт имя config.json.tmp.XXXXXX, а Xray определяет формат конфига по последнему расширению файла — там случайный суффикс, не json. Ядро отвечает «Failed to get format», grep не находит «Configuration OK.», и шаг 6 падает с кодом 1 на ЛЮБОЙ чистой установке. Проверено на живом сервере: те же байты с расширением .json проходят, без — нет; поведение одинаково на 25.9.5 и 26.3.27, то есть это не регрессия нового ядра. Флаг -format json работает на обоих ядрах и уже является соглашением проекта: xrayebator → safe_restart_xray (строка 1670) вызывает так же. Затрагивает только dev: блок предварительной валидации внесён c52d863 («close dev-to-main readiness blockers», 2026-08-11) и отсутствует в main.
Deployer при коде != 0 брал только stderr. Но install.sh печатает статус и все «✗ …» в stdout, а в stderr попадает шум: прогресс-бар curl и «TERM environment variable not set.» от clear(). В итоге вместо «config.json не прошёл валидацию» пользователь видел бар на 100% и не мог понять причину. extractInstallFailure() снимает ANSI, ищет последнюю строку с «✗» в объединённых stdout+stderr, иначе берёт хвост вывода. Путь quickstart так уже делал — теперь оба согласованы.
Включает Electron GUI, CLI, Bash hardening, тесты и синхронизированную документацию.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Резюме
Promotion PR после принятия PR #24 в
howdeploy/dev. Переносит актуальное состояние dev в стабильнуюhowdeploy/mainи объединяет Bash hardening, Electron Desktop GUI, CLI, тесты, CI и синхронизированную документацию.Источник:
howdeploy/dev(42908e0, merge PR #24)Цель:
howdeploy/main(0451a7eна момент подготовки promotion)Масштаб: 193 commits, 171 files changed относительно старого main.
Что добавлено относительно старого
howdeploy/mainBash/Xray runtime
--check,--resume,--fresh;xhttp-legacy, шесть публикуемых VLESS маршрутов;Electron Desktop GUI
src/;gui-legacy/with separate tests and packaging.Tests and CI
Documentation
docs/desktop-gui.md,docs/ru/desktop-gui.md,docs/zh-CN/desktop-gui.md;howdeploy/Xrayebator;Compatibility and operational notes
8443; IPv6-only VPS should use domain mode.xrayebator update,xrayebator update <branch>andxrayebator-update [branch]are distinct commands. Electron GUI uses the manager self-update path, not the full terminal updater.happ-setupis a reduced existing-install path and does not have quickstart's broad migration coverage. Existing subscription markers may be reused and require operator verification.gui-legacy/is archival;src/is the active desktop app.gui-v*andv*workflows are distinct.Verification
Local verification on the promotion source:
bash -n xrayebator install.sh update.sh uninstall.sh— PASS;validation/test-*.sh— PASS locally;npm run typecheck— PASS;npm run build— PASS;npm test: 38/39 due to known POSIX/bin/shtest; Linux workflow is source of truth;git diff --checkand stale-claim scan — PASS;This PR promotes the already-reviewed
devbranch tomain. It intentionally does not merge automatically; maintainer review is required.Что это добавляет обычному пользователю
Если объяснять без технических терминов, этот PR превращает Xrayebator из большого терминального Bash-скрипта в более цельный продукт, которым можно пользоваться и без постоянной работы в консоли.
Появляется новое полноценное GUI
Добавляется новое десктопное приложение на Electron. В нём можно:
Старое PySide6-приложение не исчезает бесследно, но теперь считается legacy-версией. Основным приложением становится новое Electron GUI.
Сервер становится проще установить и восстановить
Установка теперь лучше переживает реальные проблемы: временный обрыв, повторный запуск, IPv6-only VPS, занятый SSH-порт, недоступный GitHub или сбой выпуска сертификата.
Появились безопасные режимы продолжения установки (
--check,--resume,--fresh), проверки конфигурации Xray перед рестартом, резервные копии и возврат к рабочему состоянию при ошибке.Подписка сразу содержит набор запасных маршрутов
Вместо одной ссылки пользователь получает одну подписку HAPP, внутри которой есть несколько вариантов подключения: XHTTP, TCP Vision, uTLS, gRPC и другие совместимые маршруты.
Если один транспорт заблокирован конкретной сетью, можно переключиться на другой маршрут, не разворачивая сервер заново. Профиль содержит семь маршрутов, а обычная HAPP-выдача публикует шесть наиболее совместимых вариантов.
Исправлены практические баги
В этот promotion входят исправления, которые заметны не только разработчику:
Безопасность стала понятнее и строже
Конфигурация, ключи, профили и управляющие скрипты защищены от записи сервисным пользователем Xray. В GUI поддерживается проверка SSH host key: если ключ сервера неожиданно изменился, приложение остановит операцию и попросит подтвердить замену.
Пароли SSH, sudo-пароли и содержимое приватного ключа не сохраняются. При этом сама ссылка подписки и VLESS-ссылки являются чувствительными данными: их нужно защищать, а после утечки отзывать подписку.
Что всё ещё остаётся терминальным
Новое GUI не пытается скрыть все возможности Bash. Расширенные функции — bypass, probe-test, revoke подписки, HAPP setup, cascade, self-steal, сервисные логи и интерактивное меню — по-прежнему выполняются через терминал.
Также остаются операционные ограничения: IP-TLS quickstart рассчитан на IPv4, а после серьёзного lifecycle-обновления рекомендуется проверить состояние Xray, DNS и подписки.
Иными словами, PR #25 приносит в
mainне только «ещё несколько исправлений», а новый пользовательский слой, более предсказуемую установку, набор резервных маршрутов, управление сервером из GUI и значительно более полную документацию. При этом promotion оставлен открытым для финального review владельца HowDeploy.