498201b413
Обёртка с первого инкремента Фазы 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::манифест_разбирается_плагином_обновлений`).
138 lines
4.6 KiB
Smarty
138 lines
4.6 KiB
Smarty
# Сгенерировано deploy/install.sh — не редактируйте вручную, изменения перезапишутся.
|
|
# Шаблон: deploy/compose.yaml.tpl. Профиль: ${PROFILE}
|
|
name: glchat
|
|
|
|
x-logging: &default-logging
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
x-app-env: &app-env
|
|
DOMAIN: "${DOMAIN}"
|
|
FILES_DOMAIN: "${FILES_DOMAIN}"
|
|
INSTANCE_NAME: "${INSTANCE_NAME}"
|
|
APP_VERSION: "${IMAGE_TAG}"
|
|
LISTEN_ADDR: "0.0.0.0:8080"
|
|
DATA_DIR: "/app/data"
|
|
WEB_ROOT: "/app/web"
|
|
# Каталог обновлений desktop-обёртки: манифесты и артефакты релиза
|
|
# (docs/client-tauri.md §4); смонтирован только для чтения.
|
|
UPDATES_DIR: "/app/updates"
|
|
LOG_LEVEL: "${LOG_LEVEL}"
|
|
LOG_FORMAT: "json"
|
|
SQLITE_READ_POOL: "${SQLITE_READ_POOL}"
|
|
SQLITE_CACHE_SIZE: "${SQLITE_CACHE_SIZE}"
|
|
SQLITE_MMAP_SIZE: "${SQLITE_MMAP_SIZE}"
|
|
GOMAXPROCS: "${GOMAXPROCS}"
|
|
MAX_UPLOAD_SIZE: "${MAX_UPLOAD_SIZE}"
|
|
SHUTDOWN_TIMEOUT_SECONDS: "20"
|
|
# LiveKit: приложение выдаёт клиентам токены доступа к комнатам.
|
|
LIVEKIT_API_KEY: "${LIVEKIT_API_KEY}"
|
|
LIVEKIT_API_SECRET: "${LIVEKIT_API_SECRET}"
|
|
LIVEKIT_URL: "${LIVEKIT_PUBLIC_URL}"
|
|
# Внутренний адрес SFU для RoomService (модерация голоса).
|
|
LIVEKIT_API_URL: "http://livekit:${LIVEKIT_WS_PORT}"
|
|
|
|
services:
|
|
app:
|
|
image: ${APP_IMAGE}
|
|
container_name: glchat-app
|
|
restart: unless-stopped
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
<<: *app-env
|
|
ports:
|
|
- "${GLCHAT_APP_BIND}:${APP_PORT}:8080"
|
|
volumes:
|
|
- "${GLCHAT_DATA_DIR}/db:/app/data/db"
|
|
- "${GLCHAT_DATA_DIR}/files:/app/data/files"
|
|
- "${GLCHAT_DATA_DIR}/backups:/app/data/backups"
|
|
- "${GLCHAT_UPDATES_DIR}:/app/updates:ro"
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "${APP_CPUS}"
|
|
memory: ${APP_MEMORY_LIMIT}
|
|
healthcheck:
|
|
test: ["CMD", "/usr/local/bin/glchat", "healthcheck"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 20s
|
|
retries: 3
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
cap_drop:
|
|
- ALL
|
|
|
|
livekit:
|
|
image: livekit/livekit-server:${LIVEKIT_IMAGE_TAG}
|
|
container_name: glchat-livekit
|
|
restart: unless-stopped
|
|
command: ["--config", "/etc/livekit.yaml"]
|
|
environment:
|
|
LIVEKIT_KEYS: "${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
|
|
ports:
|
|
- "${GLCHAT_LIVEKIT_BIND}:${LIVEKIT_WS_PORT}:7880"
|
|
# Медиапорты: тот же адрес привязки (для инстанса за NAT это адрес туннеля).
|
|
- "${GLCHAT_LIVEKIT_BIND}:${LIVEKIT_TCP_PORT}:7881"
|
|
- "${GLCHAT_LIVEKIT_BIND}:${LIVEKIT_UDP_PORT}:7882/udp"
|
|
# TURN: UDP и TURN/TLS. На хосте без белого IP эти порты доступны только
|
|
# из туннеля (внешний прокси/релей публикует их на своём адресе).
|
|
- "${GLCHAT_LIVEKIT_BIND}:${TURN_UDP_PORT}:${TURN_UDP_PORT}/udp"
|
|
- "${GLCHAT_LIVEKIT_BIND}:${TURN_TLS_PORT}:${TURN_TLS_PORT}/tcp"
|
|
# Диапазон релея TURN (медиа через relay).
|
|
- "${GLCHAT_LIVEKIT_BIND}:${TURN_RELAY_START}-${TURN_RELAY_END}:${TURN_RELAY_START}-${TURN_RELAY_END}/udp"
|
|
volumes:
|
|
- ./livekit/livekit.yaml:/etc/livekit.yaml:ro
|
|
- ${GLCHAT_TURN_CERT_DIR}:/etc/certs:ro
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "${LIVEKIT_CPUS}"
|
|
memory: ${LIVEKIT_MEMORY_LIMIT}
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:7880"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 15s
|
|
retries: 3
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
cap_drop:
|
|
- ALL
|
|
cap_add:
|
|
- NET_BIND_SERVICE
|
|
|
|
caddy:
|
|
image: caddy:${CADDY_IMAGE_TAG}
|
|
container_name: glchat-caddy
|
|
restart: unless-stopped
|
|
profiles: ["${GLCHAT_CADDY_PROFILE}"]
|
|
ports:
|
|
- "${HTTP_PORT}:80"
|
|
- "${HTTPS_PORT}:443"
|
|
- "${HTTPS_PORT}:443/udp"
|
|
volumes:
|
|
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- ./caddy/data:/data
|
|
- ./caddy/config:/config
|
|
- ./caddy/logs:/var/log/caddy
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: "${CADDY_CPUS}"
|
|
memory: ${CADDY_MEMORY_LIMIT}
|
|
healthcheck:
|
|
test: ["CMD", "caddy", "version"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 10s
|
|
retries: 3
|
|
security_opt:
|
|
- no-new-privileges:true
|