5 Commits

Author SHA1 Message Date
grendervill 498201b413 feat(server): инстанс отдаёт манифест автообновления desktop-обёртки
Обёртка с первого инкремента Фазы 6 запрашивает
`GET /updates/{target}/{arch}/{current_version}`, но на инстансе этого пути не
было: запрос попадал в SPA-заглушку, клиент получал HTML с кодом 200 и писал
в лог «error decoding response body».

Что сделано:

- `internal/server/updates.go` — статика автообновления: каталог `UPDATES_DIR`
  (в контейнере `/app/updates:ro`), раскладка `<target>/<arch>/<version>.json` +
  артефакт и `.sig`; версия без расширения отображается на `<version>.json`,
  а если его нет — на `latest.json` (обёртка приходит со своей установленной
  версией, «новее ли релиз» решает клиент сравнением semver). Для имён
  с расширением артефакта запасного варианта нет: иначе вместо архива клиент
  скачал бы манифест и подпись не сошлась бы. Сегменты пути проверяются по
  символам, выход за каталог невозможен; `/updates/` добавлен в reservedPrefixes,
  поэтому отсутствующий манифест — честный 404, а не HTML-шелл;
- `scripts/desktop-release.sh` и цели `make desktop-release` /
  `make desktop-release-upload HOST=…`: берут собранный `glchat.app.tar.gz` + `.sig`,
  определяют target/arch по имени артефакта, копируют их в каталог инстанса и
  собирают манифест (версия из `tauri.conf.json`), объединяя платформы одной
  версии; есть `--dry-run` и загрузка на инстанс по SSH;
- `deploy/install.sh` и шаблоны compose: каталог `${GLCHAT_UPDATES_DIR}`
  (`/opt/glchat/updates`) создаётся установщиком и монтируется в контейнер
  только для чтения; в `.env` добавлен `GLCHAT_UPDATES_DIR`.

Владельцу внешнего прокси добавлять ничего не нужно: `/updates/...` уходит в
catch-all `handle` и доходит до приложения, как статика клиента.

Проверено на стенде: `curl https://gl.mhspx.su/updates/darwin/aarch64/0.1.0`
→ 200 `application/json; charset=utf-8`, артефакт → 200 `application/gzip`,
отсутствующие платформа и версия → 404 (не HTML), `readyz` → 200, обёртка
в логе пишет «обновлений нет» вместо ошибки разбора. Тесты: 6 новых в
`internal/server/updates_test.go`, манифест разбирается типом самого плагина
обновлений (`updates::tests::манифест_разбирается_плагином_обновлений`).
2026-09-22 23:17:22 +03:00
grendervill 1359821799 feat(deploy): адрес привязки для прокси по туннелю и загрузка сертификата TURN с внешнего узла
- compose: app и LiveKit публикуются на GLCHAT_APP_BIND/GLCHAT_LIVEKIT_BIND
  (по умолчанию 127.0.0.1, для схемы «VPS-прокси + домашний сервер» — адрес в WireGuard)
- install.sh: --app-bind-addr/--livekit-bind-addr, --turn-cert-remote* для
  загрузки сертификата по SSH, glchat-cert-refresh умеет тянуть его с внешнего узла
2026-09-19 20:28:29 +03:00
grendervill c5392349cd feat(turn): TURN/TLS с сертификатом, порты релея и автообновление сертификата
- install.sh: --turn-domain/--turn-cert/--turn-key, проверка наличия сертификата,
  копирование в /opt/glchat/certs, UFW открывает диапазон релея
- livekit.yaml.tpl: relay_range_start/end, cert_file/key_file
- compose: сертификаты монтируются в LiveKit read-only
- systemd timer glchat-cert-refresh: ежедневно копирует обновлённый сертификат
  из внешнего ACME-хранилища и перезапускает LiveKit при изменении
- smoke.sh: TURN-порты обязательны при включённом TURN + проверка CN сертификата
2026-09-19 20:22:57 +03:00
grendervill 741bb89bd3 fix(deploy): интерполяция compose без предупреждений о незаданных переменных
- пути томов и профиль Caddy задаются как GLCHAT_DATA_DIR/GLCHAT_CADDY_PROFILE
  (внутренний DATA_DIR=/app/data остаётся в environment сервиса app)
- значения записываются в .env, поэтому docker compose config проходит без
  предупреждений "variable is not set"
2026-09-19 19:35:38 +03:00
grendervill 99e0f72844 fix(deploy): надёжный рендер шаблонов и устойчивый цикл подстановки
- render_template: значения передаются через окружение (envsubst), а не
  подставляются в variadic-аргументы — многострочные значения больше не
  ломают разбор, неполный список не уводит bash в бесконечный цикл
- export "$pair": bash сам разбирает KEY=value (было export "${pair}")
- шаблоны переведены на ${VAR}-синтаксис, добавлен compose.test.yaml.tpl
  для тестового режима установщика (--skip-tls без публикации портов)
- dry-run не требует root и работает вне Linux (проверка ОС пропускается)
- install.sh: учитываются --livekit-*-порты и --turn-*-порты в livekit.yaml,
  исправлено экранирование апострофа в итоговой сводке
- scripts/debug-install.sh: трассировка установщика в контейнере
2026-09-19 19:26:26 +03:00