#!/usr/bin/env bash # install.sh — instaleaza puntea Discord -> Claude Code pe LXC 171 (claude-agent). # # IDEMPOTENT: se poate rula de cate ori vrei. Nu suprascrie niciodata # ~/.claude-discord/env (acolo sta token-ul completat de om). # # ./ops/install.sh # instaleaza / actualizeaza, NU porneste serviciul # ./ops/install.sh --start # in plus porneste serviciul (cere DISCORD_TOKEN completat) # # Ce face: # 1. ~/.claude-discord/ cu 0700 si logs/ # 2. env 0600 din ops/env.example (doar daca lipseste) # 3. venv + dependinte din requirements.txt # 4. loginctl enable-linger (serviciul de utilizator trebuie sa supravietuiasca logout-ului) # 5. symlink-uri unit -> ~/.config/systemd/user/ (puntea + dashboard-ul de control) # 6. intrare de crontab pentru logrotate # 7. systemd-analyze verify + enable set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SRC="$(dirname "$HERE")" # .../discord-bridge STATE_DIR="$HOME/.claude-discord" UNIT_DIR="$HOME/.config/systemd/user" UNIT_NAME="claude-discord.service" DASH_UNIT="claude-discord-dashboard.service" DO_START=0 [ "${1:-}" = "--start" ] && DO_START=1 info() { printf ' \033[32m*\033[0m %s\n' "$*"; } warn() { printf ' \033[33m!\033[0m %s\n' "$*"; } die() { printf ' \033[31mX\033[0m %s\n' "$*" >&2; exit 1; } [ "$(id -u)" -eq 0 ] && die "NU rula ca root. Puntea e un serviciu de utilizator (claude)." echo "== Punte Discord -> Claude Code: instalare ==" echo " sursa: $SRC" # --- 1. director de stare -------------------------------------------------- mkdir -p "$STATE_DIR/logs" "$STATE_DIR/approvals" chmod 0700 "$STATE_DIR" info "director de stare: $STATE_DIR (0700)" # --- 2. env ---------------------------------------------------------------- if [ -f "$STATE_DIR/env" ]; then chmod 0600 "$STATE_DIR/env" info "env exista deja, nu se atinge" else cp "$HERE/env.example" "$STATE_DIR/env" chmod 0600 "$STATE_DIR/env" info "env creat din env.example (0600) — COMPLETEAZA-L MANUAL" fi if ! grep -qE '^DISCORD_TOKEN=.+' "$STATE_DIR/env" 2>/dev/null; then warn "DISCORD_TOKEN e gol in $STATE_DIR/env — botul nu poate porni" fi # Sunt fisiere versionate langa cod: env-ul real NU trebuie sa ajunga in git. if [ -e "$SRC/env" ]; then warn "exista $SRC/env in arborele de cod — muta-l in $STATE_DIR/env si sterge-l" fi # --- 3. venv --------------------------------------------------------------- if [ ! -x "$STATE_DIR/venv/bin/python" ]; then python3 -m venv "$STATE_DIR/venv" || die "crearea venv a esuat" info "venv creat: $STATE_DIR/venv" else info "venv exista deja" fi "$STATE_DIR/venv/bin/python" -m pip install --quiet --upgrade pip >/dev/null 2>&1 if [ -f "$SRC/requirements.txt" ]; then if "$STATE_DIR/venv/bin/python" -m pip install --quiet -r "$SRC/requirements.txt"; then info "dependinte instalate din requirements.txt" else warn "instalarea dependintelor a esuat (retea?) — reia cu: $STATE_DIR/venv/bin/pip install -r $SRC/requirements.txt" fi else warn "requirements.txt lipseste inca (il aduce Lane A) — sari peste dependinte" fi # --- 4. linger ------------------------------------------------------------- # Fara linger, serviciul de utilizator moare la ultimul logout si nu porneste la boot. if [ "$(loginctl show-user "$USER" -p Linger --value 2>/dev/null)" = "yes" ]; then info "linger deja activ pentru $USER" else if loginctl enable-linger "$USER" 2>/dev/null; then info "linger activat pentru $USER" elif sudo -n loginctl enable-linger "$USER" 2>/dev/null; then info "linger activat pentru $USER (prin sudo)" else warn "nu am putut activa linger; ruleaza manual: sudo loginctl enable-linger $USER" fi fi # --- 5. unit --------------------------------------------------------------- mkdir -p "$UNIT_DIR" # symlink: unitul ramane versionat in repo, actualizarile vin cu `git pull` ln -sfn "$HERE/$UNIT_NAME" "$UNIT_DIR/$UNIT_NAME" info "unit legat: $UNIT_DIR/$UNIT_NAME -> $HERE/$UNIT_NAME" systemctl --user daemon-reload 2>/dev/null || warn "daemon-reload a esuat (sesiune fara systemd de utilizator?)" # Dashboard-ul de control (dashboard/api.py): unit separat, ca o repornire a # puntii sa nu ia si panoul din care ai apasat butonul. ln -sfn "$SRC/dashboard/$DASH_UNIT" "$UNIT_DIR/$DASH_UNIT" info "unit legat: $UNIT_DIR/$DASH_UNIT -> $SRC/dashboard/$DASH_UNIT" # Tokenul dashboard-ului: generat o singura data, niciodata rescris. if grep -qE '^DASHBOARD_TOKEN=.+' "$STATE_DIR/env" 2>/dev/null; then info "DASHBOARD_TOKEN exista deja in env" else printf 'DASHBOARD_TOKEN=%s\n' "$(python3 -c 'import secrets;print(secrets.token_urlsafe(24))')" \ >> "$STATE_DIR/env" info "DASHBOARD_TOKEN generat si adaugat in $STATE_DIR/env" fi systemctl --user daemon-reload 2>/dev/null || true if systemd-analyze verify "$UNIT_DIR/$UNIT_NAME" 2>&1 | grep -vE 'Unknown key|^$' | grep -q .; then systemd-analyze verify "$UNIT_DIR/$UNIT_NAME" 2>&1 | sed 's/^/ /' warn "systemd-analyze verify a raportat probleme (vezi mai sus)" else info "systemd-analyze verify: curat" fi # --- 6. logrotate ---------------------------------------------------------- CRON_LINE="10 4 * * * /usr/sbin/logrotate -s $STATE_DIR/logrotate.state $HERE/logrotate.conf" if crontab -l 2>/dev/null | grep -Fq "$HERE/logrotate.conf"; then info "crontab logrotate exista deja" else # Prin fisier temporar, nu prin pipe: cu `pipefail` un SIGPIPE de la `crontab -` # ar face pasul sa para esuat chiar cand a reusit. TMP_CRON="$(mktemp)" crontab -l 2>/dev/null > "$TMP_CRON" echo "$CRON_LINE" >> "$TMP_CRON" if crontab "$TMP_CRON"; then info "crontab logrotate adaugat (zilnic 04:10)" else warn "nu am putut scrie crontab-ul; adauga manual: $CRON_LINE" fi rm -f "$TMP_CRON" fi # --- 7. mail --------------------------------------------------------------- if command -v mail >/dev/null 2>&1; then info "binarul mail: $(command -v mail)" else warn "binarul \`mail\` lipseste — alertele se degradeaza la scriere in log." warn "instaleaza-l cu: sudo apt-get install -y bsd-mailx" fi # --- 8. enable / start ----------------------------------------------------- systemctl --user enable "$UNIT_NAME" >/dev/null 2>&1 \ && info "serviciu enabled (porneste la boot)" \ || warn "enable a esuat" systemctl --user enable --now "$DASH_UNIT" >/dev/null 2>&1 \ && info "dashboard enabled + pornit pe 127.0.0.1:${DASHBOARD_PORT:-18790}" \ || warn "enable pentru $DASH_UNIT a esuat" if [ "$DO_START" -eq 1 ]; then if [ ! -f "$SRC/bot.py" ]; then die "bot.py lipseste in $SRC — nu pornesc nimic" fi if ! grep -qE '^DISCORD_TOKEN=.+' "$STATE_DIR/env"; then die "DISCORD_TOKEN gol — completeaza $STATE_DIR/env inainte de --start" fi systemctl --user restart "$UNIT_NAME" && info "serviciu pornit" systemctl --user --no-pager status "$UNIT_NAME" | sed 's/^/ /' else echo echo " Serviciul NU a fost pornit (intentionat)." echo " Dupa ce completezi $STATE_DIR/env: $0 --start" fi echo "== gata =="