-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup-mac.sh
More file actions
executable file
·181 lines (151 loc) · 5.79 KB
/
Copy pathsetup-mac.sh
File metadata and controls
executable file
·181 lines (151 loc) · 5.79 KB
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env bash
# MinePanel Setup Wizard — macOS
set -euo pipefail
G='\033[92m'; Y='\033[93m'; R='\033[91m'; C='\033[96m'; B='\033[1m'; X='\033[0m'
W=56
header() {
clear
echo -e "${G}${B}"
echo " ╔$(printf '═%.0s' $(seq 1 $W))╗"
echo " ║$(printf ' %.0s' $(seq 1 $W))║"
echo " ║ __ __ _ ____ _ ║"
echo " ║ | \/ (_)_ __ ___| _ \ __ _ _ __ ___| | ║"
echo " ║ | |\/| | | '_ \/ _ \ |_) / _\` | '_ \/ _ \ | ║"
echo " ║ | | | | | | | | __/ __/ (_| | | | | __/ | ║"
echo " ║ |_| |_|_|_| |_|\___|_| \__,_|_| |_|\___|_| ║"
echo " ║$(printf ' %.0s' $(seq 1 $W))║"
echo " ║ Self-hosted Minecraft Server Manager ║"
echo " ║ Setup Wizard — macOS ║"
echo " ║$(printf ' %.0s' $(seq 1 $W))║"
echo " ╚$(printf '═%.0s' $(seq 1 $W))╝"
echo -e "${X}"
}
step() { echo -e "\n${C}${B}[ $1 ]${X} $2"; }
ok() { echo -e " ${G}✔${X} $*"; }
warn() { echo -e " ${Y}!${X} $*"; }
fail() { echo -e " ${R}✗${X} $*"; exit 1; }
ask() { echo -en " ${B}?${X} $1 "; }
header
# ── Step 1: Prerequisites ─────────────────────────────────────────────────────
step "1/3" "Checking prerequisites"
if ! command -v docker &>/dev/null; then
fail "Docker not found. Install Docker Desktop from https://www.docker.com/products/docker-desktop/"
fi
if ! docker info &>/dev/null; then
fail "Docker daemon not running. Open Docker Desktop and wait for it to start."
fi
docker compose version &>/dev/null || fail "Docker Compose plugin not found. Update Docker Desktop."
ok "Docker $(docker --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + Compose ready"
ok "Docker socket: /var/run/docker.sock (Docker Desktop)"
# ── Step 2: Configuration ─────────────────────────────────────────────────────
step "2/3" "Configuration"
SKIP_ENV=0
if [[ -f .env ]]; then
warn ".env already exists."
ask "Overwrite it? [y/N]"; read -r ANS
if [[ "$ANS" =~ ^[Yy]$ ]]; then
rm -f .env
else
SKIP_ENV=1
# reuse the existing deployment: read DOMAIN from the file without
# executing it (an untrusted .env must never be sourced)
DOMAIN=$(grep -E '^DOMAIN=' .env | head -1 | cut -d= -f2- | tr -d '"' || true)
if [[ -z "$DOMAIN" ]]; then
fail ".env exists but DOMAIN is empty; fix .env or overwrite it"
fi
if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?$ ]]; then
fail ".env exists but DOMAIN is not a bare hostname; fix .env or overwrite it"
fi
fi
fi
if [[ $SKIP_ENV -eq 0 ]]; then
echo -e "\n Enter your domain for automatic HTTPS (e.g. panel.example.com)."
echo " A non-empty DOMAIN is required for production deployment."
for _ in {1..3}; do
ask "Domain:"; read -r DOMAIN
if [[ -n "$DOMAIN" ]]; then
# hostname only: no scheme, no path, no ports, no spaces
if [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?$ ]]; then
break
fi
warn "DOMAIN must be a bare hostname (no scheme, port, path, or spaces)"
DOMAIN=""
fi
done
[[ -n "$DOMAIN" ]] || fail "DOMAIN is required"
ask "Frontend CORS origin [https://minepanel.xyz]:"; read -r CORS_ORIGIN
CORS_ORIGIN=${CORS_ORIGIN:-https://minepanel.xyz}
# macOS has openssl via LibreSSL, works the same
JWT_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -hex 24)
ENCRYPTION_KEY=$(openssl rand -hex 32)
SETUP_TOKEN=$(openssl rand -hex 24)
MC_DATA_PATH_HOST="${HOME}/.minepanel/mc-data"
# secrets file: 0600, owned by the invoking user
umask 077
cat > .env <<EOF
# Generated by setup-mac.sh — $(date -u +"%Y-%m-%dT%H:%M:%SZ")
DOMAIN=${DOMAIN}
CORS_ORIGIN=${CORS_ORIGIN}
MINEPANEL_IMAGE=ghcr.io/minepanelproject/minepanel-backend:latest
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
DATABASE_URL="postgresql://minepanel:${POSTGRES_PASSWORD}@postgres:5432/minepanel"
JWT_SECRET=${JWT_SECRET}
JWT_EXPIRES_IN="15m"
JWT_REFRESH_EXPIRES_IN="7d"
SETUP_TOKEN=${SETUP_TOKEN}
REQUIRE_ADMIN_APPROVAL=true
LOGIN_THROTTLE_LIMIT=5
LOGIN_THROTTLE_TTL_MS=10000
ENCRYPTION_KEY=${ENCRYPTION_KEY}
NODE_ENV=production
PORT=3000
PANEL_NAME="MinePanel"
DOCKER_SOCKET="/var/run/docker.sock"
DOCKER_NETWORK="minepanel_network"
MC_DATA_PATH_HOST=${MC_DATA_PATH_HOST}
MC_DATA_PATH="/mc-data"
MC_DATA_BIND_SOURCE=${MC_DATA_PATH_HOST}
MC_PORT_MIN=25565
MC_PORT_MAX=25665
MIN_FREE_DISK_MB=2048
MAX_MEMORY_RATIO=0.90
STOP_WARN_SECONDS=30
EOF
ok ".env generated"
fi
# ── Step 3: Launch ────────────────────────────────────────────────────────────
step "3/3" "Launching MinePanel"
docker compose pull
docker compose up -d
BASE="https://${DOMAIN}"
echo -e "\n ${G}${B}MinePanel is starting!${X}\n"
echo -e " API: ${C}${BASE}/api${X}"
echo -e " Docs: ${C}${BASE}/docs${X}"
echo -e " Health: ${C}${BASE}/health${X}"
echo -e "\n First-run admin setup:"
echo -e " ${C}${BASE}/api/setup/init${X}\n"
MAX_ATTEMPTS=30
REACHABLE=0
for i in $(seq 1 $MAX_ATTEMPTS); do
if curl -sf "${BASE}/health" >/dev/null; then
REACHABLE=1
break
fi
if [[ $i -eq $MAX_ATTEMPTS ]]; then
break
fi
sleep 2
done
if [[ $REACHABLE -eq 1 ]]; then
ok "HTTPS health check passed (${BASE}/health)"
else
warn "HTTPS health check did not become reachable within the timeout"
echo ""
docker compose ps || true
echo ""
docker compose logs --tail 50 nestjs || true
fail "Deployment failed"
fi
ask "Open setup page in browser? [Y/n]"; read -r ANS
[[ ! "$ANS" =~ ^[Nn]$ ]] && open "${BASE}/api/setup/init"