bober-bbq-bot/provision_client.sh
byrsapty 375022e739 Add backup_remote.py and wire up the rest of the rclone rework
Continuation of the previous commit (gdrive_backup.py removal landed
separately by accident) — adds the new module itself, the admin
routes/template using it, generalized Setting keys, updated docs/
provisioning notes, and tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 22:57:59 +03:00

213 lines
7.9 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Provisions a NEW, separate deployment of this codebase for another client
# on a fresh VPS — clone, .env, webapp build, nginx vhost, TLS, systemd
# units. Automates docs/DEPLOYMENT.md steps 2-5; run this instead of doing
# that runbook by hand.
#
# Prerequisite (docs/DEPLOYMENT.md step 1, one-time OS setup, not run by
# this script): python3.11, nginx, certbot, nodejs/npm, rclone already
# installed on this server.
#
# Never touches an existing deployment: refuses to run if --install-dir
# already exists, and every irreversible/system-level step (nginx reload,
# certbot, systemctl enable) asks for confirmation first.
set -euo pipefail
usage() {
cat <<'EOF'
Usage: ./provision_client.sh --slug SLUG --domain DOMAIN --bot-token TOKEN --cafe-name "Name" [--install-dir DIR] [--repo-url URL]
--slug short identifier, lowercase letters/digits/hyphens only
(used for the install dir and systemd unit names)
--domain public domain this instance will be served from
--bot-token Telegram bot token from @BotFather
--cafe-name display name for this client (webapp title, PWA manifest)
--install-dir defaults to /opt/<slug>-bot
--repo-url defaults to this project's own git remote
EOF
}
SLUG=""
DOMAIN=""
BOT_TOKEN=""
CAFE_NAME=""
INSTALL_DIR=""
REPO_URL="https://git.zotac.keenetic.link/zotac/bober-bbq-bot.git"
while [[ $# -gt 0 ]]; do
case "$1" in
--slug) SLUG="$2"; shift 2 ;;
--domain) DOMAIN="$2"; shift 2 ;;
--bot-token) BOT_TOKEN="$2"; shift 2 ;;
--cafe-name) CAFE_NAME="$2"; shift 2 ;;
--install-dir) INSTALL_DIR="$2"; shift 2 ;;
--repo-url) REPO_URL="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "Невідомий аргумент: $1"; usage; exit 1 ;;
esac
done
if [[ -z "$SLUG" || -z "$DOMAIN" || -z "$BOT_TOKEN" || -z "$CAFE_NAME" ]]; then
echo "Не вистачає обов'язкового аргументу."
usage
exit 1
fi
if [[ ! "$SLUG" =~ ^[a-z0-9-]+$ ]]; then
echo "--slug має містити лише малі латинські літери, цифри й дефіс (напр. \"vegcafe\")."
exit 1
fi
INSTALL_DIR="${INSTALL_DIR:-/opt/${SLUG}-bot}"
SERVICE_WEB="${SLUG}-web"
SERVICE_BOT="${SLUG}-bot"
LOG_FILE_WEB="/var/log/${SERVICE_WEB}.log"
LOG_FILE_BOT="/var/log/${SERVICE_BOT}.log"
confirm() {
local reply
read -rp "$1 [y/N] " reply
[[ "$reply" =~ ^[Yy]$ ]]
}
echo "=== Провижнінг нового клієнта ==="
echo "Slug: $SLUG"
echo "Домен: $DOMAIN"
echo "Install dir: $INSTALL_DIR"
echo "Cafe name: $CAFE_NAME"
echo "Юніти: $SERVICE_WEB / $SERVICE_BOT"
echo ""
if ! confirm "Продовжити?"; then
echo "Скасовано."
exit 0
fi
if [[ -e "$INSTALL_DIR" ]]; then
echo "ПОМИЛКА: $INSTALL_DIR вже існує — оберіть інший --install-dir."
echo "(Це також запобігає випадковому запуску поверх існуючого деплою.)"
exit 1
fi
echo "==> 1/8 git clone"
git clone "$REPO_URL" "$INSTALL_DIR"
cd "$INSTALL_DIR"
echo "==> 2/8 venv + залежності"
python3.11 -m venv .venv
.venv/bin/pip install -q -r requirements.txt
echo "==> 3/8 .env"
SECRET_KEY=$(openssl rand -hex 32)
ADMIN_PASSWORD=$(openssl rand -hex 12)
cp .env.example .env
sed -i "s|^BOT_TOKEN=.*|BOT_TOKEN=${BOT_TOKEN}|" .env
sed -i "s|^WEBAPP_URL=.*|WEBAPP_URL=https://${DOMAIN}/webapp/|" .env
sed -i "s|^BACKEND_PUBLIC_URL=.*|BACKEND_PUBLIC_URL=https://${DOMAIN}|" .env
sed -i "s|^MONOBANK_WEBHOOK_URL=.*|MONOBANK_WEBHOOK_URL=https://${DOMAIN}/api/payments/monobank/webhook|" .env
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=${SECRET_KEY}|" .env
sed -i "s|^FLASK_ADMIN_PASSWORD=.*|FLASK_ADMIN_PASSWORD=${ADMIN_PASSWORD}|" .env
{
echo ""
echo "# --- provision_client.sh: deployment identity ---"
echo "SERVICE_WEB=${SERVICE_WEB}"
echo "SERVICE_BOT=${SERVICE_BOT}"
echo "LOG_FILE_WEB=${LOG_FILE_WEB}"
echo "LOG_FILE_BOT=${LOG_FILE_BOT}"
} >> .env
echo "Адмін-логін: admin / пароль: ${ADMIN_PASSWORD} (запишіть — більше ніде не показується)"
echo "==> 4/8 webapp/.env.local"
echo "VITE_CAFE_NAME=${CAFE_NAME}" > webapp/.env.local
echo "==> 5/8 webapp build"
( cd webapp && npm install --silent && npm run build )
echo "==> 6/8 nginx vhost"
if confirm "Створити nginx vhost для ${DOMAIN} і перезавантажити nginx?"; then
NGINX_CONF="/etc/nginx/sites-available/${SLUG}"
sudo tee "$NGINX_CONF" > /dev/null <<EOF
server {
listen 80;
server_name ${DOMAIN};
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
sudo ln -sf "$NGINX_CONF" "/etc/nginx/sites-enabled/${SLUG}"
sudo nginx -t
sudo systemctl reload nginx
else
echo "Пропущено — налаштуйте nginx вручну (docs/DEPLOYMENT.md, крок 4)."
fi
echo "==> 7/8 certbot (TLS)"
if confirm "Запустити certbot для ${DOMAIN}? (потрібен вже налаштований DNS-запис на цей сервер)"; then
sudo certbot --nginx -d "$DOMAIN"
else
echo "Пропущено — без TLS Telegram Mini App і вебхуки не запрацюють."
echo "Запустіть пізніше: sudo certbot --nginx -d ${DOMAIN}"
fi
echo "==> 8/8 systemd-юніти"
if confirm "Створити й запустити systemd-юніти (${SERVICE_WEB}, ${SERVICE_BOT})?"; then
sudo tee "/etc/systemd/system/${SERVICE_WEB}.service" > /dev/null <<EOF
[Unit]
Description=${CAFE_NAME} backend (API + admin)
After=network.target
[Service]
WorkingDirectory=${INSTALL_DIR}
ExecStart=${INSTALL_DIR}/.venv/bin/python run_web.py
Restart=on-failure
EnvironmentFile=${INSTALL_DIR}/.env
StandardOutput=append:${LOG_FILE_WEB}
StandardError=append:${LOG_FILE_WEB}
[Install]
WantedBy=multi-user.target
EOF
sudo tee "/etc/systemd/system/${SERVICE_BOT}.service" > /dev/null <<EOF
[Unit]
Description=${CAFE_NAME} Telegram bot
After=network.target ${SERVICE_WEB}.service
[Service]
WorkingDirectory=${INSTALL_DIR}
ExecStart=${INSTALL_DIR}/.venv/bin/python run_bot.py
Restart=on-failure
EnvironmentFile=${INSTALL_DIR}/.env
StandardOutput=append:${LOG_FILE_BOT}
StandardError=append:${LOG_FILE_BOT}
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now "$SERVICE_WEB" "$SERVICE_BOT"
else
echo "Пропущено — створіть юніти вручну (docs/DEPLOYMENT.md, крок 5)."
fi
cat <<EOF
=== Готово. Залишились ручні кроки (свідомо не автоматизуються) ===
1. У @BotFather: /setmenubutton -> https://${DOMAIN}/webapp/
2. Додати бота в чати доставки/самовивозу, вписати chat_id в Адмінку -> Налаштування.
3. Якщо клієнт приймає картки — MONOBANK_TOKEN у .env і перевірити вебхук.
4. Замінити 4 іконки під webapp/public/icons/icon-{32,180,192,512}.png на бренд
клієнта, потім ще раз: cd webapp && npm run build
5. Адмінка (https://${DOMAIN}/admin, admin / пароль вище) -> Налаштування:
cafe_name, адреса, графік, кольори, лого.
6. Адмінка -> Меню: видалити демо-меню (Шашлик/BBQ), додати асортимент клієнта.
7. Зареєструвати https://${DOMAIN}/health в UptimeRobot (чи аналог) — вручну,
акаунт створює власник бізнесу, не цей скрипт.
8. Резервні копії: Адмінка -> Резервні копії -> налаштувати сховище (Google
Drive або S3-сумісне) і увімкнути (або окремий cron — docs/DEPLOYMENT.md,
крок 7).
EOF