summaryrefslogtreecommitdiff
path: root/hetzner-admin/run-command.sh
blob: b473ce412f69471a505184f6519256b2bb66128e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/bin/bash
# SSH authorized_keys の command= から呼ばれる。$SSH_ORIGINAL_COMMAND をホワイトリストで検証する。
set -e

# 管理画面から停止・起動できるサービス(名前は status/targets.txt の名前に合わせる)
# Caddy / hetzner-admin / status は止めると管理画面に戻れなくなるので対象外
SERVICES="golive whoisband blog todo git vpn ntfy mail code"
# docker compose ではなく systemd で動くサービス
service_unit() {
  case "$1" in
    code) echo code-server@yyamashita.service ;;
    *) return 1 ;;
  esac
}
service_compose() {
  case "$1" in
    golive)    echo /app/tokyo-livehouse-events/docker-compose.yml ;;
    whoisband) echo /app/whois-band/docker-compose.yml ;;
    blog)      echo /app/microblog/docker-compose.yml ;;
    todo)      echo /app/todo/docker-compose.yml ;;
    git)       echo /app/infra/cgit/docker-compose.yml ;;
    vpn)       echo /app/infra/vpn/docker-compose.yml ;;
    ntfy)      echo /app/infra/ntfy/docker-compose.yml ;;
    mail)      echo /app/infra/mail/docker-compose.yml ;;
    *) return 1 ;;
  esac
}

# 管理画面で停止したサービスの一覧。status-app が読み、DOWN ではなく STOPPED と表示する
STOPPED_FILE=/app/infra/status/stopped.txt

case "$SSH_ORIGINAL_COMMAND" in
  "services")
    touch "$STOPPED_FILE"
    for name in $SERVICES; do
      if unit=$(service_unit "$name"); then
        if systemctl is-active --quiet "$unit"; then state=running; else state=stopped; fi
      else
        running=$(docker compose -f "$(service_compose "$name")" ps -q --status running 2>/dev/null | wc -l)
        if [ "$running" -gt 0 ]; then state=running; else state=stopped; fi
      fi
      echo "$name $state"
    done
    ;;
  "stop "*|"start "*)
    action="${SSH_ORIGINAL_COMMAND%% *}"
    name="${SSH_ORIGINAL_COMMAND#* }"
    unit=$(service_unit "$name") || compose=$(service_compose "$name") || { echo "Unknown service: $name" >&2; exit 1; }
    touch "$STOPPED_FILE"
    if [ "$action" = stop ]; then
      grep -qx "$name" "$STOPPED_FILE" || echo "$name" >> "$STOPPED_FILE"
      # systemd は disable もして、再起動後も停止したままにする(docker の unless-stopped と同じ扱い)
      if [ -n "$unit" ]; then systemctl disable --now "$unit" 2>&1; else docker compose -f "$compose" stop 2>&1; fi
    else
      sed -i "/^${name}\$/d" "$STOPPED_FILE"
      if [ -n "$unit" ]; then systemctl enable --now "$unit" 2>&1; else docker compose -f "$compose" up -d 2>&1; fi
    fi
    ;;
  "resources")
    # 管理画面のリソース表示用。"key value..." 形式で出力し app.py がパースする
    echo "cpus $(nproc)"
    echo "load $(cut -d' ' -f1-3 /proc/loadavg)"
    awk '/^MemTotal:/{t=$2} /^MemAvailable:/{a=$2} END{print "mem", t*1024, (t-a)*1024}' /proc/meminfo
    df -B1 --output=size,used / | awk 'NR==2{print "disk", $1, $2}'
    echo "uptime $(cut -d. -f1 /proc/uptime)"
    ;;
  "docker ps")
    exec docker ps
    ;;
  "docker stats")
    exec docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}'
    ;;
  "docker compose -f /app/infra/caddy/docker-compose.yml restart")
    exec docker compose -f /app/infra/caddy/docker-compose.yml restart
    ;;
  "docker compose -f /app/infra/mail/docker-compose.yml restart")
    exec docker compose -f /app/infra/mail/docker-compose.yml restart
    ;;
  "docker compose -f /app/infra/cgit/docker-compose.yml restart")
    exec docker compose -f /app/infra/cgit/docker-compose.yml restart
    ;;
  "docker compose -f /app/infra/hetzner-admin/docker-compose.yml restart")
    exec docker compose -f /app/infra/hetzner-admin/docker-compose.yml restart
    ;;
  "bash /app/infra/claude/restart-sessions.sh")
    exec bash /app/infra/claude/restart-sessions.sh
    ;;
  "reboot")
    exec reboot
    ;;
  *)
    echo "Not allowed: $SSH_ORIGINAL_COMMAND" >&2
    exit 1
    ;;
esac