#!/usr/bin/env bash # Ставит Claude Code и веб-панель CloudCLI на чистый сервер Ubuntu 22.04 или 24.04. # Инструкция: https://prosvirkin.xyz/blog/claude-code-vps # # Запуск на сервере: # curl -fsSL https://prosvirkin.xyz/claude-server.sh | sudo bash -s claude.example.com # # Что делает скрипт: # 1. Проверяет, что домен уже указывает на этот сервер. # 2. Создаёт пользователя claude, ставит Node.js 22, Claude Code и CloudCLI. # 3. Запускает панель как службу на 127.0.0.1:3001 и сам создаёт в ней логин и пароль. # 4. Ставит Caddy: HTTPS-сертификат для домена и проксирование на панель. # 5. Включает файрвол: наружу открыты только SSH, 80 и 443. # Повторный запуск безопасен: уже сделанные шаги пропускаются. set -euo pipefail DOMAIN="${1:-}" APP_USER="claude" PANEL_PORT="3001" CREDENTIALS_FILE="/root/claude-panel.txt" say() { printf '\n\033[1;34m==>\033[0m %s\n' "$1"; } fail() { printf '\n\033[1;31mОшибка:\033[0m %s\n' "$1" >&2; exit 1; } [ "$(id -u)" -eq 0 ] || fail "запусти скрипт через sudo." [ -n "$DOMAIN" ] || fail "укажи домен в конце команды: ... | sudo bash -s claude.example.com" [ -r /etc/os-release ] && . /etc/os-release [ "${ID:-}" = "ubuntu" ] || fail "скрипт рассчитан на Ubuntu 22.04 или 24.04." export DEBIAN_FRONTEND=noninteractive say "Проверяю, что $DOMAIN указывает на этот сервер" apt-get update -q >/dev/null apt-get install -yq curl ca-certificates gnupg git ufw openssl dnsutils >/dev/null SERVER_IP="$(curl -4fsS https://api.ipify.org || true)" DOMAIN_IP="$(dig +short A "$DOMAIN" @1.1.1.1 | tail -n1)" if [ -z "$SERVER_IP" ] || [ "$DOMAIN_IP" != "$SERVER_IP" ]; then fail "домен $DOMAIN указывает на '${DOMAIN_IP:-никуда}', а адрес сервера ${SERVER_IP:-не определён}. Добавь A-запись с адресом сервера, подожди 5-10 минут и запусти команду ещё раз." fi say "Создаю пользователя $APP_USER" id "$APP_USER" >/dev/null 2>&1 || useradd --create-home --shell /bin/bash "$APP_USER" APP_HOME="$(getent passwd "$APP_USER" | cut -d: -f6)" mkdir -p "$APP_HOME/projects" chown "$APP_USER:$APP_USER" "$APP_HOME/projects" say "Ставлю Node.js 22" NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)" if [ "$NODE_MAJOR" -lt 22 ]; then curl -fsSL https://deb.nodesource.com/setup_22.x | bash - >/dev/null apt-get install -yq nodejs >/dev/null fi say "Ставлю Claude Code" runuser -l "$APP_USER" -c 'curl -fsSL https://claude.ai/install.sh | bash' >/dev/null grep -q '.local/bin' "$APP_HOME/.bashrc" || echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$APP_HOME/.bashrc" say "Ставлю панель CloudCLI" npm install -g --silent @cloudcli-ai/cloudcli CLOUDCLI_BIN="$(command -v cloudcli)" cat > /etc/systemd/system/cloudcli.service </dev/null 2>&1 systemctl restart cloudcli for _ in $(seq 1 60); do curl -fsS "http://127.0.0.1:$PANEL_PORT/api/auth/status" >/dev/null 2>&1 && break sleep 1 done STATUS="$(curl -fsS "http://127.0.0.1:$PANEL_PORT/api/auth/status")" || fail "панель не запустилась. Логи: journalctl -u cloudcli -n 50" if printf '%s' "$STATUS" | grep -q '"needsSetup":true'; then say "Создаю логин для панели" PANEL_PASSWORD="$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | cut -c1-20)" curl -fsS -X POST "http://127.0.0.1:$PANEL_PORT/api/auth/register" \ -H 'Content-Type: application/json' \ -d "{\"username\":\"$APP_USER\",\"password\":\"$PANEL_PASSWORD\"}" >/dev/null \ || fail "не удалось создать пользователя панели. Логи: journalctl -u cloudcli -n 50" umask 077 printf 'Адрес: https://%s\nЛогин: %s\nПароль: %s\n' "$DOMAIN" "$APP_USER" "$PANEL_PASSWORD" > "$CREDENTIALS_FILE" fi say "Ставлю Caddy и получаю HTTPS-сертификат" if ! command -v caddy >/dev/null; then curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/gpg.key \ | gpg --dearmor --yes -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt \ > /etc/apt/sources.list.d/caddy-stable.list apt-get update -q >/dev/null apt-get install -yq caddy >/dev/null fi cat > /etc/caddy/Caddyfile </dev/null ufw allow 80,443/tcp >/dev/null ufw --force enable >/dev/null if [ -f "$CREDENTIALS_FILE" ]; then printf '\n\033[1;32mГотово.\033[0m Сохрани эти данные, они понадобятся для входа:\n\n' cat "$CREDENTIALS_FILE" printf '\nЕсли забудешь пароль, он лежит на сервере: sudo cat %s\n' "$CREDENTIALS_FILE" else printf '\n\033[1;32mГотово.\033[0m Панель: https://%s\nЛогин в панели был создан раньше, этим скриптом пароль не менялся.\n' "$DOMAIN" fi