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 и фиксация версий инструментов
This commit is contained in:
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env bash
|
||||
# glchat — служебные команды инстанса (AGENT.md 10.7).
|
||||
# Ставится установщиком в /usr/local/bin/glchat.
|
||||
set -euo pipefail
|
||||
|
||||
GLCHAT_ETC_DIR="${GLCHAT_ETC_DIR:-/opt/glchat}"
|
||||
COMPOSE_FILE="${GLCHAT_ETC_DIR}/compose.yaml"
|
||||
ENV_FILE="${GLCHAT_ETC_DIR}/.env"
|
||||
|
||||
COMPOSE="docker compose -f ${COMPOSE_FILE}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
glchat — управление инстансом
|
||||
|
||||
glchat status состояние контейнеров, версия, здоровье, ресурсы
|
||||
glchat logs [service] логи (follow)
|
||||
glchat restart [service] перезапуск стека или сервиса
|
||||
glchat backup бэкап БД и файлов (age-шифрование)
|
||||
glchat restore <файл> восстановление из бэкапа
|
||||
glchat update [--channel X] обновление (бэкап → pull → up → smoke)
|
||||
glchat reset-password <email> сброс пароля пользователя (фаза 1)
|
||||
glchat make-admin <email> выдать права инстанс-админа (фаза 1)
|
||||
glchat remove-admin <email> снять права инстанс-админа (фаза 1)
|
||||
glchat unban <ip> снять бан fail2ban
|
||||
glchat fail2ban-status статус jails и забаненных IP
|
||||
glchat profile [tier] показать/применить профиль производительности
|
||||
glchat security-check аудит: SSH, fail2ban, порты, лимиты
|
||||
glchat reindex перестроить FTS-индекс (фаза 2)
|
||||
glchat change-domain <domain> сменить домен
|
||||
glchat uninstall [--keep-data] удалить стек
|
||||
glchat version версия CLI и приложения
|
||||
EOF
|
||||
}
|
||||
|
||||
require_stack() {
|
||||
[ -f "$COMPOSE_FILE" ] || {
|
||||
echo "glchat: не найден ${COMPOSE_FILE}; сначала запустите install.sh" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
require_stack
|
||||
local meta version health
|
||||
echo "== compose =="
|
||||
$COMPOSE ps
|
||||
echo
|
||||
meta="$(curl -fsS "http://127.0.0.1:8080/api/v1/meta" 2>/dev/null || true)"
|
||||
version="$(printf '%s' "$meta" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')"
|
||||
health="$(curl -fsS "http://127.0.0.1:8080/readyz" 2>/dev/null || echo '{"status":"unreachable"}')"
|
||||
echo "== app =="
|
||||
echo "version: ${version:-unknown}"
|
||||
echo "readyz: ${health}"
|
||||
echo
|
||||
echo "== ресурсы =="
|
||||
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}' 2>/dev/null || true
|
||||
echo
|
||||
df -h "${GLCHAT_ETC_DIR}/data" 2>/dev/null | tail -1 || true
|
||||
}
|
||||
|
||||
cmd_logs() {
|
||||
require_stack
|
||||
$COMPOSE logs -f --tail=200 "$@"
|
||||
}
|
||||
|
||||
cmd_restart() {
|
||||
require_stack
|
||||
if [ "$#" -gt 0 ]; then
|
||||
$COMPOSE restart "$@"
|
||||
else
|
||||
$COMPOSE restart
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_backup() {
|
||||
require_stack
|
||||
bash "${GLCHAT_ETC_DIR}/backup.sh" "$@"
|
||||
}
|
||||
|
||||
cmd_restore() {
|
||||
require_stack
|
||||
bash "${GLCHAT_ETC_DIR}/restore.sh" "$@"
|
||||
}
|
||||
|
||||
cmd_update() {
|
||||
require_stack
|
||||
bash "${GLCHAT_ETC_DIR}/update.sh" "$@"
|
||||
}
|
||||
|
||||
cmd_unban() {
|
||||
[ "$#" -ge 1 ] || {
|
||||
echo "использование: glchat unban <ip>" >&2
|
||||
exit 1
|
||||
}
|
||||
fail2ban-client unban "$1"
|
||||
}
|
||||
|
||||
cmd_fail2ban_status() {
|
||||
fail2ban-client status
|
||||
for jail in $(fail2ban-client status | sed -n 's/.*Jail list:\s*//p' | tr ',' ' '); do
|
||||
echo
|
||||
fail2ban-client status "${jail// /}"
|
||||
done
|
||||
}
|
||||
|
||||
cmd_profile() {
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "профиль: $(sed -n 's/^PROFILE=//p' "$ENV_FILE" 2>/dev/null || echo unknown)"
|
||||
grep -E '^(SQLITE_|GOMAXPROCS|MAX_UPLOAD_SIZE|APP_CPUS|APP_MEMORY_LIMIT)' "$ENV_FILE" 2>/dev/null || true
|
||||
return
|
||||
fi
|
||||
GLCHAT_ETC_DIR="$GLCHAT_ETC_DIR" bash "${GLCHAT_ETC_DIR}/install.sh" --profile "$1" --reconfigure --yes
|
||||
}
|
||||
|
||||
cmd_security_check() {
|
||||
bash "${GLCHAT_ETC_DIR}/security-check.sh"
|
||||
}
|
||||
|
||||
cmd_change_domain() {
|
||||
[ "$#" -ge 1 ] || {
|
||||
echo "использование: glchat change-domain <domain>" >&2
|
||||
exit 1
|
||||
}
|
||||
GLCHAT_ETC_DIR="$GLCHAT_ETC_DIR" bash "${GLCHAT_ETC_DIR}/install.sh" --domain "$1" --reconfigure --yes
|
||||
}
|
||||
|
||||
cmd_uninstall() {
|
||||
require_stack
|
||||
local keep_data=0
|
||||
[ "${1:-}" = "--keep-data" ] && keep_data=1
|
||||
echo "Контейнеры glchat будут удалены. Данные: $([ "$keep_data" = 1 ] && echo 'сохраняются' || echo 'УДАЛЯЮТСЯ')"
|
||||
read -r -p "Продолжить? (yes/no): " reply
|
||||
[ "$reply" = "yes" ] || {
|
||||
echo "отменено"
|
||||
exit 0
|
||||
}
|
||||
systemctl disable --now glchat.service 2>/dev/null || true
|
||||
$COMPOSE --profile embedded-proxy down --remove-orphans || true
|
||||
if [ "$keep_data" = 0 ]; then
|
||||
rm -rf "${GLCHAT_ETC_DIR}/data"
|
||||
fi
|
||||
rm -f /etc/systemd/system/glchat.service
|
||||
systemctl daemon-reload
|
||||
echo "Стек удалён. Каталог ${GLCHAT_ETC_DIR} оставлен для ручной проверки."
|
||||
}
|
||||
|
||||
cmd_version() {
|
||||
echo "glchat CLI 0.1.0 (phase 0)"
|
||||
[ -f "$ENV_FILE" ] && grep -E '^(APP_VERSION|IMAGE_TAG|DOMAIN|PROFILE)=' "$ENV_FILE" || true
|
||||
}
|
||||
|
||||
cmd_reindex() {
|
||||
echo "glchat reindex появится в фазе 2 (FTS5)." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd="${1:-help}"
|
||||
shift || true
|
||||
case "$cmd" in
|
||||
status) cmd_status "$@" ;;
|
||||
logs) cmd_logs "$@" ;;
|
||||
restart) cmd_restart "$@" ;;
|
||||
backup) cmd_backup "$@" ;;
|
||||
restore) cmd_restore "$@" ;;
|
||||
update) cmd_update "$@" ;;
|
||||
reset-password | make-admin | remove-admin)
|
||||
echo "glchat ${cmd}: команда появится в фазе 1 (аккаунты и инстанс)." >&2
|
||||
exit 1
|
||||
;;
|
||||
unban) cmd_unban "$@" ;;
|
||||
fail2ban-status) cmd_fail2ban_status "$@" ;;
|
||||
profile) cmd_profile "$@" ;;
|
||||
security-check) cmd_security_check "$@" ;;
|
||||
reindex) cmd_reindex "$@" ;;
|
||||
change-domain) cmd_change_domain "$@" ;;
|
||||
uninstall) cmd_uninstall "$@" ;;
|
||||
version) cmd_version "$@" ;;
|
||||
help | --help | -h) usage ;;
|
||||
*)
|
||||
echo "glchat: неизвестная команда '$cmd'" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user