https://claude-agent.tailf7372d.ts.net/punte — acelasi tipar ca /echo de pe moltbot: procesul ramane legat de 127.0.0.1, tailscaled il proxeaza si pune HTTPS. Montarea sub prefix a cerut doua schimbari: - toate URL-urile din pagini sunt acum relative, fiindca --set-path TAIE prefixul inainte de a proxa (serverul vede /api/status, browserul cere /punte/api/status). DASHBOARD_PREFIX ramane necesar doar pentru redirectul de login, si e acceptat si intact pe intrare, ca sa mearga si curl direct pe localhost. - adresa fara slash final (/punte) primeste 301 catre /punte/: altfel URL-urile relative s-ar rezolva la radacina hostului, unde proxy-ul nu trimite nimic incoace, si panoul ar arata gol fara nicio eroare vizibila. ops/install.sh configureaza serve-ul daca sudo permite; altfel spune comanda. Sase teste noi pentru montarea sub prefix (31 in total pe dashboard). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
191 lines
8.3 KiB
Bash
Executable File
191 lines
8.3 KiB
Bash
Executable File
#!/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
|
|
|
|
# --- 6b. tailscale serve pentru dashboard ----------------------------------
|
|
# Publica panoul in tailnet la https://<host>.<tailnet>.ts.net/punte, exact ca
|
|
# dashboard-ul lui echo de pe LXC 110 (/echo). Serviciul ramane legat de
|
|
# 127.0.0.1: tailscaled e singurul care ajunge la el.
|
|
DASH_PREFIX="$(grep -E '^DASHBOARD_PREFIX=' "$STATE_DIR/env" 2>/dev/null | cut -d= -f2- || true)"
|
|
DASH_PREFIX="${DASH_PREFIX:-/punte}"
|
|
DASH_PORT="$(grep -E '^DASHBOARD_PORT=' "$STATE_DIR/env" 2>/dev/null | cut -d= -f2- || true)"
|
|
DASH_PORT="${DASH_PORT:-18790}"
|
|
if ! command -v tailscale >/dev/null 2>&1; then
|
|
warn "tailscale lipseste — dashboard-ul ramane doar pe 127.0.0.1:$DASH_PORT (tunel SSH)"
|
|
elif tailscale serve status 2>/dev/null | grep -q "$DASH_PREFIX proxy"; then
|
|
info "tailscale serve: $DASH_PREFIX exista deja"
|
|
elif sudo -n tailscale serve --bg --set-path "$DASH_PREFIX" "http://127.0.0.1:$DASH_PORT" >/dev/null 2>&1; then
|
|
info "tailscale serve: https://$(tailscale status --json | python3 -c 'import json,sys;print(json.load(sys.stdin)["Self"]["DNSName"].rstrip("."))')$DASH_PREFIX"
|
|
else
|
|
warn "nu am putut configura tailscale serve; ruleaza manual:"
|
|
warn " sudo tailscale serve --bg --set-path $DASH_PREFIX http://127.0.0.1:$DASH_PORT"
|
|
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 =="
|