Commit Graph

11 Commits

Author SHA1 Message Date
grendervill 2d013df0bb fix(make): не передавать Tauri CLI недопустимый --bundles all
`make desktop-build` не запускался вовсе: Tauri CLI отвергает `--bundles all`
(«possible values: ios, app, dmg»), а значение по умолчанию в Makefile было
именно `all`. Теперь при `all` флаг не передаётся, и список бандлов берётся из
`bundle.targets` в tauri.conf.json — то есть собирается всё, что положено для
текущей ОС. `make desktop-build-app` (`--bundles app`) работает как раньше.

Обнаружено при попытке собрать `.dmg` (make desktop-build).
2026-09-26 18:12:38 +03:00
grendervill 72a51f083d test(desktop): замеры холодного старта и памяти обёртки
Требование спецификации §1/§7 (idle RAM ≤ 350 МБ, холодный старт ≤ 2 с) до сих
пор не было измерено. `scripts/desktop-perf.py` запускает собранный `.app`,
меряет время от `open` до строки «загружена страница» в логе обёртки и память
после того, как клиент успокоился.

`ps` и `top` в песочнице агента недоступны, поэтому на macOS память и список
процессов читаются через `libproc` (`proc_pid_rusage`, `proc_listpids`), а для
Linux есть ветка на `/proc`. Помощники WebKit (`WebContent`, `GPU`,
`Networking`) запускаются через launchd, их родитель — не приложение, поэтому
«свои» определяются сравнением списка процессов до и после запуска: иначе RSS
главного процесса занижал бы потребление втрое.

Замеры (macOS arm64, Mac на M-серии, инстанс через внешний прокси):
холодный старт 708–740 мс, RSS открытого клиента 252 МБ (обёртка 117 + WebKit
135), в трее 251 МБ, физический след 103 МБ. Результаты — в `docs/PERF.md` и
`desktop/README.md`.

В Makefile добавлена цель `desktop-build-app` (`tauri build --bundles app`):
артефакты обновления собираются без `.dmg`, который в песочнице и headless-
окружении падает на `bundle_dmg.sh` (монтирование образа + AppleScript).
2026-09-22 23:17:39 +03:00
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 a7b4e3318e build(desktop): цели make, README и устойчивость обёртки к правам на логи
- Makefile: `desktop-check`, `desktop-build` (с ключом подписи обновлений,
  без ключа — сборка без артефактов обновления) и `desktop-run`; кэши
  cargo и npm держатся в .cache/, как у остальных инструментов проекта.
- desktop/README.md: сборка, запуск, аргументы командной строки, настройки,
  deep links и открытые пункты Фазы 6.
- bundle.targets = all: бандлы по умолчанию для текущей ОС (macOS: .app/.dmg).
- Логирование подключается в setup и больше не роняет приложение, если
  каталог логов недоступен на запись (песочница, домашний каталог только
  для чтения).
- Разбор аргументов вынесен в Cli::from_args и покрыт тестами
  (--instance, --minimized, --settings, --autostart on|off).
- cargo fmt по модулям обёртки.
2026-09-22 21:55:19 +03:00
grendervill c7446a5d4c feat(security): security.txt отдаётся, npm audit и SBOM в AppSec
- /.well-known/security.txt уходил в SPA-заглушку (200 text/html): добавлена
  копия в web/public и явный тип text/plain в статике (RFC 9116, AGENT.md 11.3);
- make security дополнен `npm audit --omit=dev --audit-level=high`;
- новая цель `make sbom` — CycloneDX-отчёт зависимостей (trivy, если есть).
2026-09-22 20:39:44 +03:00
grendervill d2712c4c99 feat(ops): сверка .env инстанса с таблицей профилей
- scripts/test-profiles.sh --env <файл>: профиль берётся из .env, значения
  таблицы 10.10 пересчитываются и сверяются с производными ключами
  (SQLITE_MMAP_SIZE, MAX_UPLOAD_SIZE, APP_MEMORY_LIMIT, SHARE_SOFT_LIMIT,
  BACKUP_KEEP_DAYS и т. д.), offsite-ключи проверяются на наличие.
- make profiles-env — тот же прогон для /opt/glchat/.env.
- На стенде: профиль standard совпал (67 проверок, 0 ошибок); .env дополнен
  ключами offsite, установленный backup.sh обновлён, выгрузка и недоступный
  приёмник проверены вживую.
2026-09-22 18:46:49 +03:00
grendervill 3151327f6e feat(ops): offsite-бэкапы и проверки профилей
- deploy/backup.sh: BACKUP_OFFSITE_TARGET (rsync или rclone:remote:path)
  выгружает готовый архив после локальной ротации; неудача выгрузки только
  предупреждает. Ротация на приёмнике — опционально
  (BACKUP_OFFSITE_KEEP_DAYS, rclone delete --min-age). Имя архива получает
  суффикс, если бэкап запущен дважды в одну секунду.
- deploy/install.sh: offsite-настройки в .env (BACKUP_OFFSITE_TARGET,
  BACKUP_OFFSITE_KEEP_DAYS) отдельным разделом; детектор незаполненных
  плейсхолдеров учитывает цифры в именах (FAIL2BAN_ENABLED); dry-run больше не
  требует прав на /opt/glchat — явность GLCHAT_ETC_DIR фиксируется до
  подключения common.sh, иначе ветка временного каталога была недостижима.
- scripts/test-profiles.sh: сверка таблицы профилей 10.10 и одинакового набора
  PROFILE_* (46 проверок), make profiles-test / ops-test.
- scripts/test-backup.sh: бэкап и offsite на синтетическом инстансе — локальный
  rsync-приёмник, шим rclone, недоступный хост, нечисловой срок (12 проверок),
  make backup-test.
- scripts/test-install.sh: проверка offsite-ключей в .env после установки.
2026-09-22 18:44:50 +03:00
grendervill 4550bea6cc feat(db): схема Фазы 2 — сообщения, реакции, read states, файлы, инвайты, FTS5
Миграция 00004: таблицы `messages`, `message_reactions`,
`channel_read_states`, `files`, `invites` и виртуальная таблица `messages_fts`
с триггерами синхронизации (AGENT.md 6.1, 7.6, 7.7, 7.9, 7.15, 7.16).

Полнотекстовый поиск требует сборки с тегом `sqlite_fts5`:
- `GO_TAGS ?= sqlite_fts5` в Makefile (build/test/cover) и в Dockerfile;
- build-tags в golangci-lint;
- страж `internal/database/fts5_guard.go`: сборка без тега падает с понятным
  текстом, а не ломается на миграции в рантайме.
2026-09-19 23:29:39 +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
grendervill 2bf7101f2d chore(lint): настроить golangci-lint v2, gofumpt и форматирование в make
- .golangci.yml: набор линтеров под требования §14 (gosec, gocritic, sloglint,
  noctx, revive, depguard для запрета циклических зависимостей internal/*)
- gofumpt вынесен из formatters golangci-lint v2.13 (сообщал о расхождениях,
  которые сам же не устранял) и применяется отдельным инструментом
- make fmt/go-lint: проверка gofumpt + golangci-lint fmt --diff
- scripts/gitleaks-scan.sh: поддержка gitleaks 8.19+ (dir) и старых версий (detect)
- исправлены замечания линтеров: обработка ошибок Close, контекст в логах,
  NewRequestWithContext в тестах, комментарий к blank-import драйвера SQLite
2026-09-19 18:25:20 +03:00
grendervill 4bd1770cb5 feat(deploy): установщик, docker-скелет, шаблоны Caddy/LiveKit и CLI
- deploy/install.sh: идемпотентная установка, режимы встроенного и внешнего
  прокси (§10.13), --dry-run/--skip-tls/--reconfigure, auto-детект профиля (§10.10)
- deploy/compose.yaml.tpl: app, livekit, caddy с лимитами, healthcheck,
  ротацией логов, cap_drop и no-new-privileges (§4.3)
- deploy/caddy: Caddyfile.tpl (встроенный) и external-proxy.conf.tpl (наш стенд)
- deploy/livekit: конфиг с TURN, dynacast и лимитами комнат (§10.6)
- deploy/backup.sh + restore.sh: sqlite .backup, tar, age-шифрование (§10.8)
- deploy/update.sh: бэкап → pull/сборка → up → миграции → smoke (§9.3)
- deploy/smoke.sh: проверки §10.9 (healthz/readyz/meta, порты, UFW, fail2ban, бэкап)
- deploy/security-check.sh: аудит SSH, UFW, fail2ban, прав .env и ключей
- deploy/ufw-docker.sh, nftables и fail2ban-конфиги (sshd, glchat-auth, recidive)
- deploy/glchat-cli.sh: команды §10.7, systemd-юнит glchat.service
- scripts/test-install.sh: прогон установщика в чистом Ubuntu 24.04 (systemd-контейнер)
- Dockerfile: multi-stage (node → golang CGO → debian-slim), non-root, healthcheck
- Makefile: lint/test/build/dev/generate/security/smoke и фиксация версий инструментов
2026-09-19 18:21:02 +03:00