403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/scripts/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/scripts/claude-session-manager.sh
#!/usr/bin/env bash
#
# claude-session-manager.sh
# ---------------------------------------------------------------------------
# Verwaltung von Claude-Code-Sessions je ISPConfig-Web (Debian / ISPConfig 3)
#
# - liest verfuegbare Webs direkt aus der ISPConfig-Datenbank (dbispconfig)
# - Auswahl der Domain ueber ein Menue
# - passt alle Befehle automatisch an den gewaehlten Web-User an
# - zeigt jeden Befehl vor der Ausfuehrung an (Preview + Bestaetigung)
# - einzelne Schritte frei im Menue waehlbar
#
# Autor: erstellt fuer Nico (SeaSide Travel / ISPConfig-Stack)
# ---------------------------------------------------------------------------

set -o pipefail

# --------------------------------------------------------------------------- #
#  Konfiguration
# --------------------------------------------------------------------------- #
# Schema fuer den tmux-Session-Namen:
#   "domain"  ->  claude-taschenmuschi-kaufen-de   (Punkte werden zu - )
#   "user"    ->  claude-web32
SESSION_SCHEME="domain"

# --------------------------------------------------------------------------- #
#  Farben / Stil
# --------------------------------------------------------------------------- #
if [[ -t 1 ]]; then
    C_RESET=$'\e[0m'; C_BOLD=$'\e[1m'; C_DIM=$'\e[2m'
    C_RED=$'\e[38;5;203m'; C_GRN=$'\e[38;5;114m'; C_YLW=$'\e[38;5;221m'
    C_BLU=$'\e[38;5;75m';  C_CYN=$'\e[38;5;80m';  C_MAG=$'\e[38;5;176m'
    C_GRY=$'\e[38;5;245m'
else
    C_RESET=""; C_BOLD=""; C_DIM=""; C_RED=""; C_GRN=""; C_YLW=""
    C_BLU=""; C_CYN=""; C_MAG=""; C_GRY=""
fi

BAR="${C_BLU}────────────────────────────────────────────────────────────────${C_RESET}"

# --------------------------------------------------------------------------- #
#  Hilfsausgaben
# --------------------------------------------------------------------------- #
ok()   { echo -e "  ${C_GRN}✔${C_RESET} $*"; }
err()  { echo -e "  ${C_RED}✘${C_RESET} $*"; }
warn() { echo -e "  ${C_YLW}!${C_RESET} $*"; }
info() { echo -e "  ${C_CYN}»${C_RESET} $*"; }

pause() { echo; read -rp "  ${C_DIM}[Enter] um fortzufahren …${C_RESET} " _; }

confirm() {
    local q="${1:-Befehl jetzt ausfuehren?}"
    echo
    read -rp "  ${C_YLW}?${C_RESET} ${q} ${C_DIM}[j/N]${C_RESET} " a
    [[ "$a" =~ ^([jJyY])$ ]]
}

# Zeigt einen Befehl als Preview-Block (mehrzeilig, lesbar) ------------------ #
preview() {
    echo
    echo -e "  ${C_GRY}┌─ auszufuehrender Befehl ───────────────────────────────${C_RESET}"
    while IFS= read -r line; do
        echo -e "  ${C_GRY}│${C_RESET} ${C_MAG}${line}${C_RESET}"
    done <<< "$1"
    echo -e "  ${C_GRY}└────────────────────────────────────────────────────────${C_RESET}"
}

# --------------------------------------------------------------------------- #
#  Voraussetzungen
# --------------------------------------------------------------------------- #
require_root() {
    if [[ $EUID -ne 0 ]]; then
        err "Bitte als ${C_BOLD}root${C_RESET} starten (chown / sudo -u benoetigt root)."
        exit 1
    fi
}

check_deps() {
    local missing=()
    for bin in tmux sudo mysql; do
        command -v "$bin" >/dev/null 2>&1 || missing+=("$bin")
    done
    if ((${#missing[@]})); then
        err "Fehlende Programme: ${missing[*]}"
        info "Installation: ${C_BOLD}apt install ${missing[*]}${C_RESET}"
        exit 1
    fi
}

# --------------------------------------------------------------------------- #
#  ISPConfig-DB-Zugang aus config.inc.php lesen
# --------------------------------------------------------------------------- #
DB_HOST=""; DB_PORT="3306"; DB_USER=""; DB_PASS=""; DB_NAME="dbispconfig"

php_conf() {  # $1 = datei  $2 = key
    grep -oP "\\\$conf\['$2'\]\s*=\s*'\K[^']*" "$1" | head -n1
}

read_db_config() {
    local f
    for f in /usr/local/ispconfig/server/lib/config.inc.php \
             /usr/local/ispconfig/interface/lib/config.inc.php; do
        [[ -f "$f" ]] || continue
        DB_HOST=$(php_conf "$f" db_host)
        DB_PORT=$(php_conf "$f" db_port)
        DB_USER=$(php_conf "$f" db_user)
        DB_PASS=$(php_conf "$f" db_password)
        DB_NAME=$(php_conf "$f" db_database)
        [[ -n "$DB_USER" ]] && break
    done
    [[ -z "$DB_HOST" ]] && DB_HOST="localhost"
    [[ -z "$DB_PORT" ]] && DB_PORT="3306"
    [[ -z "$DB_NAME" ]] && DB_NAME="dbispconfig"
    [[ -n "$DB_USER" ]]
}

# MySQL-Aufruf ueber temporaere, geschuetzte Optionsdatei (Passwort nicht in ps)
mysql_q() {
    local cnf rc
    cnf=$(mktemp); chmod 600 "$cnf"
    cat > "$cnf" <<EOF
[client]
host=${DB_HOST}
port=${DB_PORT}
user=${DB_USER}
password=${DB_PASS}
EOF
    mysql --defaults-extra-file="$cnf" -N -B -e "$1"
    rc=$?
    rm -f "$cnf"
    return $rc
}

# --------------------------------------------------------------------------- #
#  Webs laden  (Arrays:  W_DOMAIN  W_USER  W_GROUP  W_DOCROOT)
# --------------------------------------------------------------------------- #
declare -a W_DOMAIN W_USER W_GROUP W_DOCROOT

load_webs() {
    W_DOMAIN=(); W_USER=(); W_GROUP=(); W_DOCROOT=()
    local rows
    if read_db_config; then
        rows=$(mysql_q "SELECT domain, system_user, system_group, document_root
                        FROM ${DB_NAME}.web_domain
                        WHERE active='y'
                          AND system_user <> ''
                          AND type IN ('vhost','vhostsubdomain')
                        ORDER BY domain;")
        if [[ -n "$rows" ]]; then
            while IFS=$'\t' read -r dom usr grp doc; do
                [[ -z "$dom" ]] && continue
                W_DOMAIN+=("$dom"); W_USER+=("$usr")
                W_GROUP+=("$grp");  W_DOCROOT+=("$doc")
            done <<< "$rows"
            return 0
        fi
        warn "Keine Eintraege in der ISPConfig-DB gefunden – nutze Fallback."
    else
        warn "ISPConfig-DB-Zugang nicht lesbar – nutze Fallback (/var/www)."
    fi

    # Fallback: /var/www-Symlinks + Owner aus dem Dateisystem
    local link target usr grp
    for link in /var/www/*; do
        [[ -L "$link" ]] || continue
        target=$(readlink -f "$link")
        [[ -d "$target" ]] || continue
        usr=$(stat -c '%U' "$target" 2>/dev/null)
        grp=$(stat -c '%G' "$target" 2>/dev/null)
        [[ "$usr" =~ ^web[0-9]+$ ]] || continue
        W_DOMAIN+=("$(basename "$link")"); W_USER+=("$usr")
        W_GROUP+=("$grp"); W_DOCROOT+=("$target")
    done
    ((${#W_DOMAIN[@]} > 0))
}

# --------------------------------------------------------------------------- #
#  Aktuelle Auswahl
# --------------------------------------------------------------------------- #
SEL_DOMAIN=""; SEL_USER=""; SEL_GROUP=""; SEL_DOCROOT=""
WORKDIR=""; CLAUDE_HOME=""; SESSION=""

resolve_paths() {
    CLAUDE_HOME="/home/claude-${SEL_USER}"
    # tmux-sichere Bezeichnung: Punkte/Doppelpunkte/Slashes -> Bindestrich
    local safe_domain="${SEL_DOMAIN//[.:\/ ]/-}"
    if [[ "$SESSION_SCHEME" == "domain" ]]; then
        SESSION="claude-${safe_domain}"
    else
        SESSION="claude-${SEL_USER}"
    fi
    # bevorzugt der vom Nutzer genutzte Domain-Pfad, sonst document_root
    if   [[ -d "/var/www/${SEL_DOMAIN}/web" ]]; then WORKDIR="/var/www/${SEL_DOMAIN}/web"
    elif [[ -d "${SEL_DOCROOT}/web" ]];        then WORKDIR="${SEL_DOCROOT}/web"
    else WORKDIR="${SEL_DOCROOT}"
    fi
}

session_running() {
    sudo -u "$SEL_USER" env HOME="$CLAUDE_HOME" \
        tmux has-session -t "$SESSION" 2>/dev/null
}

select_domain() {
    if ! load_webs; then
        err "Es konnten keine Webs ermittelt werden."
        pause; return 1
    fi
    while true; do
        clear; header_min
        echo -e "  ${C_BOLD}Verfuegbare Webs${C_RESET} ${C_DIM}(Quelle: ${DB_NAME:-Dateisystem})${C_RESET}"
        echo -e "  $BAR"
        local i
        for i in "${!W_DOMAIN[@]}"; do
            printf "  ${C_CYN}%2d${C_RESET})  ${C_BOLD}%-32s${C_RESET} ${C_GRY}%-8s %s${C_RESET}\n" \
                $((i+1)) "${W_DOMAIN[$i]}" "${W_USER[$i]}" "${W_GROUP[$i]}"
        done
        echo -e "  $BAR"
        echo -e "   ${C_DIM}0)  zurueck${C_RESET}"
        echo
        read -rp "  Domain waehlen: " n
        [[ "$n" == "0" ]] && return 1
        if [[ "$n" =~ ^[0-9]+$ ]] && ((n>=1 && n<=${#W_DOMAIN[@]})); then
            local idx=$((n-1))
            SEL_DOMAIN="${W_DOMAIN[$idx]}"; SEL_USER="${W_USER[$idx]}"
            SEL_GROUP="${W_GROUP[$idx]}";  SEL_DOCROOT="${W_DOCROOT[$idx]}"
            resolve_paths
            return 0
        fi
        warn "Ungueltige Eingabe."; sleep 1
    done
}

# --------------------------------------------------------------------------- #
#  Schritte
# --------------------------------------------------------------------------- #
step_create_folder() {
    clear; header
    echo -e "  ${C_BOLD}Schritt 1 – Claude-Home anlegen${C_RESET}\n  $BAR"
    info "Verzeichnis : ${C_BOLD}${CLAUDE_HOME}${C_RESET}"
    info "Besitzer    : ${C_BOLD}${SEL_USER}:${SEL_GROUP}${C_RESET}  (chmod 700)"

    preview "mkdir -p ${CLAUDE_HOME}
chown ${SEL_USER}:${SEL_GROUP} ${CLAUDE_HOME}
chmod 700 ${CLAUDE_HOME}"

    if [[ -d "$CLAUDE_HOME" ]]; then
        warn "Verzeichnis existiert bereits – Rechte werden nur aktualisiert."
    fi
    confirm || { info "Abgebrochen."; pause; return; }

    if mkdir -p "$CLAUDE_HOME" \
       && chown "${SEL_USER}:${SEL_GROUP}" "$CLAUDE_HOME" \
       && chmod 700 "$CLAUDE_HOME"; then
        ok "Claude-Home eingerichtet: ${CLAUDE_HOME}"
    else
        err "Fehler beim Anlegen / Setzen der Rechte."
    fi
    pause
}

step_start_session() {
    clear; header
    echo -e "  ${C_BOLD}Schritt 2 – Claude-Session starten${C_RESET}\n  $BAR"

    if [[ ! -d "$CLAUDE_HOME" ]]; then
        warn "Claude-Home ${CLAUDE_HOME} fehlt – bitte zuerst Schritt 1."
        pause; return
    fi
    if [[ ! -d "$WORKDIR" ]]; then
        err "Arbeitsverzeichnis ${WORKDIR} existiert nicht."
        pause; return
    fi
    if session_running; then
        warn "Session ${C_BOLD}${SESSION}${C_RESET} laeuft bereits."
        pause; return
    fi

    info "Session     : ${C_BOLD}${SESSION}${C_RESET}"
    info "Workdir     : ${C_BOLD}${WORKDIR}${C_RESET}"

    preview "sudo -u ${SEL_USER} env HOME=${CLAUDE_HOME} USER=${SEL_USER} LOGNAME=${SEL_USER} SHELL=/bin/bash \\
  tmux new -d -s ${SESSION} \\
  -c ${WORKDIR} \\
  'claude --dangerously-skip-permissions --remote-control'"

    confirm || { info "Abgebrochen."; pause; return; }

    if sudo -u "$SEL_USER" env HOME="$CLAUDE_HOME" USER="$SEL_USER" \
        LOGNAME="$SEL_USER" SHELL=/bin/bash \
        tmux new -d -s "$SESSION" -c "$WORKDIR" \
        'claude --dangerously-skip-permissions --remote-control'; then
        ok "Session ${SESSION} gestartet (detached)."
    else
        err "Start fehlgeschlagen – ist 'claude' fuer ${SEL_USER} im PATH?"
    fi
    pause
}

step_attach() {
    clear; header
    echo -e "  ${C_BOLD}Schritt 3 – Session oeffnen (attach)${C_RESET}\n  $BAR"
    if ! session_running; then
        warn "Keine laufende Session ${C_BOLD}${SESSION}${C_RESET}."
        pause; return
    fi
    info "Loesen ohne Beenden: ${C_BOLD}Strg-b  d${C_RESET}"

    preview "sudo -u ${SEL_USER} env HOME=${CLAUDE_HOME} tmux attach -t ${SESSION}"

    confirm "Jetzt verbinden?" || { info "Abgebrochen."; pause; return; }
    sudo -u "$SEL_USER" env HOME="$CLAUDE_HOME" tmux attach -t "$SESSION"
}

step_list() {
    clear; header
    echo -e "  ${C_BOLD}Schritt 4 – Laufende Sessions (User ${SEL_USER})${C_RESET}\n  $BAR"

    preview "sudo -u ${SEL_USER} env HOME=${CLAUDE_HOME} tmux ls"
    echo
    local out
    out=$(sudo -u "$SEL_USER" env HOME="$CLAUDE_HOME" tmux ls 2>&1)
    if [[ -z "$out" || "$out" == *"no server running"* ]]; then
        info "Keine aktiven Sessions fuer ${SEL_USER}."
    else
        echo "$out" | sed 's/^/  /'
    fi
    pause
}

step_kill() {
    clear; header
    echo -e "  ${C_BOLD}Session beenden${C_RESET}\n  $BAR"
    if ! session_running; then
        warn "Keine laufende Session ${C_BOLD}${SESSION}${C_RESET}."
        pause; return
    fi
    preview "sudo -u ${SEL_USER} env HOME=${CLAUDE_HOME} tmux kill-session -t ${SESSION}"
    confirm "Session ${SESSION} wirklich beenden?" || { info "Abgebrochen."; pause; return; }
    if sudo -u "$SEL_USER" env HOME="$CLAUDE_HOME" tmux kill-session -t "$SESSION"; then
        ok "Session ${SESSION} beendet."
    else
        err "Konnte Session nicht beenden."
    fi
    pause
}

# --------------------------------------------------------------------------- #
#  Header / Menue
# --------------------------------------------------------------------------- #
header_min() {
    echo -e "${C_BLU}${C_BOLD}"
    echo "  ╔══════════════════════════════════════════════════════════════╗"
    echo "  ║            Claude Session Manager · ISPConfig / Debian         ║"
    echo "  ╚══════════════════════════════════════════════════════════════╝"
    echo -e "${C_RESET}"
}

header() {
    header_min
    local state
    if session_running; then state="${C_GRN}● laeuft${C_RESET}"
    else state="${C_GRY}○ gestoppt${C_RESET}"; fi
    echo -e "  ${C_DIM}Domain ${C_RESET} ${C_BOLD}${SEL_DOMAIN}${C_RESET}"
    echo -e "  ${C_DIM}Web    ${C_RESET} ${SEL_USER}:${SEL_GROUP}    ${C_DIM}Session${C_RESET} ${SESSION}  [ ${state} ]"
    echo -e "  ${C_DIM}Workdir${C_RESET} ${WORKDIR}"
    echo -e "  ${C_DIM}Home   ${C_RESET} ${CLAUDE_HOME}"
    echo -e "  $BAR"
}

main_menu() {
    while true; do
        clear; header
        echo -e "   ${C_CYN}1${C_RESET})  Claude-Home anlegen        ${C_DIM}(mkdir + chown + chmod)${C_RESET}"
        echo -e "   ${C_CYN}2${C_RESET})  Session starten            ${C_DIM}(tmux new, detached)${C_RESET}"
        echo -e "   ${C_CYN}3${C_RESET})  Session oeffnen            ${C_DIM}(tmux attach)${C_RESET}"
        echo -e "   ${C_CYN}4${C_RESET})  Sessions anzeigen          ${C_DIM}(tmux ls)${C_RESET}"
        echo -e "   ${C_CYN}5${C_RESET})  Session beenden            ${C_DIM}(tmux kill-session)${C_RESET}"
        echo -e "  $BAR"
        echo -e "   ${C_YLW}d${C_RESET})  Domain wechseln"
        echo -e "   ${C_RED}q${C_RESET})  Beenden"
        echo
        read -rp "  Auswahl: " c
        case "$c" in
            1) step_create_folder ;;
            2) step_start_session ;;
            3) step_attach ;;
            4) step_list ;;
            5) step_kill ;;
            d|D) select_domain || true ;;
            q|Q) clear; ok "Beendet."; exit 0 ;;
            *) warn "Ungueltige Auswahl."; sleep 1 ;;
        esac
    done
}

# --------------------------------------------------------------------------- #
#  Start
# --------------------------------------------------------------------------- #
require_root
check_deps
clear; header_min
info "Lese verfuegbare Webs aus ISPConfig …"
if ! select_domain; then
    clear; err "Keine Domain gewaehlt – Abbruch."
    exit 1
fi
main_menu

Youez - 2016 - github.com/yon3zu
LinuXploit