-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·409 lines (331 loc) · 15.5 KB
/
setup.sh
File metadata and controls
executable file
·409 lines (331 loc) · 15.5 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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
#!/usr/bin/env bash
set -e
# Wrap in main() so bash reads entire script before executing
# This prevents issues when piping: cat script | bash
main() {
SETUP_SCRIPT_URL="https://raw.githubusercontent.com/Blazity/next-cwv-monitor/main/setup.sh"
COMPOSE_URL="${COMPOSE_URL:-https://raw.githubusercontent.com/Blazity/next-cwv-monitor/main/docker/docker-compose.yml}"
COMPOSE_SSL_URL="${COMPOSE_SSL_URL:-https://raw.githubusercontent.com/Blazity/next-cwv-monitor/main/docker/docker-compose.ssl.yml}"
DEFAULT_INSTALL_DIR="$HOME/cwv-monitor"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Read from terminal even when script is piped
# This allows: curl ... | bash OR bash <(curl ...)
show_tty_error() {
echo -e "${RED}Error: Cannot access terminal for interactive input.${NC}"
echo ""
echo "Try one of these instead:"
echo " 1. Download and run locally:"
echo " curl -fsSL $SETUP_SCRIPT_URL -o setup.sh && bash setup.sh"
echo ""
echo " 2. Use process substitution:"
echo " bash <(curl -fsSL $SETUP_SCRIPT_URL)"
exit 1
}
if [ -t 0 ]; then
# stdin is already a terminal
:
else
# Try to open /dev/tty and verify it's actually a usable terminal
# The -t test ensures the fd is connected to a real terminal, not just a file
if { exec 3</dev/tty; } 2>/dev/null && [ -t 3 ]; then
exec 0<&3 # Redirect stdin from fd 3
exec 3<&- # Close fd 3
else
exec 3<&- 2>/dev/null # Clean up fd 3 if it was opened
show_tty_error
fi
fi
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ CWV Monitor - Production Setup Wizard ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo -e "${NC}"
INSTALL_DIR="${1:-$DEFAULT_INSTALL_DIR}"
echo -e "${YELLOW}Installation directory: $INSTALL_DIR${NC}"
read -p "Press Enter to continue or specify a different path: " CUSTOM_DIR
if [ -n "$CUSTOM_DIR" ]; then
INSTALL_DIR="$CUSTOM_DIR"
fi
mkdir -p "$INSTALL_DIR"
ENV_FILE="$INSTALL_DIR/.env"
echo ""
echo -e "${BLUE}▶ Downloading configuration files...${NC}"
echo "─────────────────────────────────────────────────────────────"
download_file() {
local url="$1"
local dest="$2"
if command -v curl &> /dev/null; then
curl -fsSL "$url" -o "$dest"
elif command -v wget &> /dev/null; then
wget -q "$url" -O "$dest"
else
echo -e "${RED}Error: curl or wget is required${NC}"
exit 1
fi
}
download_file "$COMPOSE_URL" "$INSTALL_DIR/docker-compose.yml"
echo -e "${GREEN}✓ Downloaded docker-compose.yml${NC}"
# Download SSL compose if needed (we'll check ENABLE_SSL_BOOL later after it's set)
echo ""
generate_secret() {
openssl rand -base64 32 | tr -d '/+=' | head -c 32
}
validate_email() {
local email="$1"
if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
return 0
fi
return 1
}
echo -e "${YELLOW}This wizard will help you configure CWV Monitor for production.${NC}"
echo ""
echo -e "${GREEN}▶ SSL Configuration${NC}"
echo "─────────────────────────────────────────────────────────────"
echo "Caddy can automatically obtain and renew SSL certificates."
echo ""
read -p "Enable automatic SSL with Caddy? (yes/no) [no]: " ENABLE_SSL
ENABLE_SSL="${ENABLE_SSL:-no}"
ENABLE_SSL_BOOL="false"
if [ "$ENABLE_SSL" = "yes" ] || [ "$ENABLE_SSL" = "y" ]; then
ENABLE_SSL_BOOL="true"
echo ""
echo -e "${YELLOW}Note: Your domain must be pointing to this server's IP address.${NC}"
echo ""
while true; do
read -p "Domain name (e.g., monitor.example.com): " SSL_DOMAIN
if [ -n "$SSL_DOMAIN" ]; then
break
fi
echo -e "${RED}Domain is required for SSL.${NC}"
done
read -p "Email for Let's Encrypt notifications (optional): " SSL_EMAIL
# Set derived values for SSL mode
AUTH_BASE_URL="https://$SSL_DOMAIN"
APP_PORT="3000" # Internal port, Caddy handles 80/443
TRUST_PROXY="true"
echo ""
echo -e "${GREEN}✓ SSL will be configured for: $SSL_DOMAIN${NC}"
fi
echo ""
echo -e "${GREEN}▶ Application Settings${NC}"
echo "─────────────────────────────────────────────────────────────"
if [ "$ENABLE_SSL_BOOL" = "false" ]; then
read -p "Public URL where the app will be accessible [http://localhost]: " AUTH_BASE_URL
AUTH_BASE_URL="${AUTH_BASE_URL:-http://localhost}"
read -p "Port to expose the app on [80]: " APP_PORT
APP_PORT="${APP_PORT:-80}"
read -p "Is the app behind a reverse proxy? (true/false) [false]: " TRUST_PROXY
TRUST_PROXY="${TRUST_PROXY:-false}"
else
echo -e "Public URL: ${GREEN}$AUTH_BASE_URL${NC} (set by SSL config)"
echo -e "Reverse proxy: ${GREEN}true${NC} (Caddy)"
fi
echo ""
echo -e "${GREEN}▶ Initial Admin User${NC}"
echo "─────────────────────────────────────────────────────────────"
echo "This user will be created on first startup."
echo ""
while true; do
read -p "Admin email: " INITIAL_USER_EMAIL
if validate_email "$INITIAL_USER_EMAIL"; then
break
fi
echo -e "${RED}Invalid email format. Please try again.${NC}"
done
read -p "Admin name [Admin]: " INITIAL_USER_NAME
INITIAL_USER_NAME="${INITIAL_USER_NAME:-Admin}"
while true; do
read -sp "Admin password (min 8 chars): " INITIAL_USER_PASSWORD
echo ""
if [ ${#INITIAL_USER_PASSWORD} -ge 8 ]; then
read -sp "Confirm password: " INITIAL_USER_PASSWORD_CONFIRM
echo ""
if [ "$INITIAL_USER_PASSWORD" = "$INITIAL_USER_PASSWORD_CONFIRM" ]; then
break
fi
echo -e "${RED}Passwords don't match. Please try again.${NC}"
else
echo -e "${RED}Password must be at least 8 characters.${NC}"
fi
done
echo ""
echo -e "${GREEN}▶ ClickHouse Settings${NC}"
echo "─────────────────────────────────────────────────────────────"
read -p "ClickHouse host [clickhouse]: " CLICKHOUSE_HOST
CLICKHOUSE_HOST="${CLICKHOUSE_HOST:-clickhouse}"
read -p "ClickHouse port [8123]: " CLICKHOUSE_PORT
CLICKHOUSE_PORT="${CLICKHOUSE_PORT:-8123}"
read -p "ClickHouse database [cwv_monitor]: " CLICKHOUSE_DB
CLICKHOUSE_DB="${CLICKHOUSE_DB:-cwv_monitor}"
read -p "ClickHouse user [default]: " CLICKHOUSE_USER
CLICKHOUSE_USER="${CLICKHOUSE_USER:-default}"
echo ""
echo -e "${YELLOW}Generating ClickHouse password...${NC}"
CLICKHOUSE_PASSWORD=$(generate_secret)
echo -e "${GREEN}✓ Generated secure password${NC}"
echo ""
echo -e "${GREEN}▶ Security Settings${NC}"
echo "─────────────────────────────────────────────────────────────"
echo -e "${YELLOW}Generating auth secret...${NC}"
BETTER_AUTH_SECRET=$(generate_secret)
echo -e "${GREEN}✓ Generated secure auth secret${NC}"
read -p "Min password strength score (0-4) [2]: " MIN_PASSWORD_SCORE
MIN_PASSWORD_SCORE="${MIN_PASSWORD_SCORE:-2}"
read -p "Rate limit window (ms) [60000]: " RATE_LIMIT_WINDOW_MS
RATE_LIMIT_WINDOW_MS="${RATE_LIMIT_WINDOW_MS:-60000}"
read -p "Max login attempts [5]: " MAX_LOGIN_ATTEMPTS
MAX_LOGIN_ATTEMPTS="${MAX_LOGIN_ATTEMPTS:-5}"
echo ""
echo -e "${GREEN}▶ Image Settings (Optional)${NC}"
echo "─────────────────────────────────────────────────────────────"
echo "Leave blank to use default registry."
echo ""
read -p "Image registry [ghcr.io/blazity]: " IMAGE_REGISTRY
IMAGE_REGISTRY="${IMAGE_REGISTRY:-ghcr.io/blazity}"
read -p "Image tag [latest]: " IMAGE_TAG
IMAGE_TAG="${IMAGE_TAG:-latest}"
echo ""
echo -e "${GREEN}▶ Demo Data (Optional)${NC}"
echo "─────────────────────────────────────────────────────────────"
read -p "Seed demo data on startup? (yes/no) [no]: " SEED_DEMO
SEED_DEMO="${SEED_DEMO:-no}"
INCLUDE_DEMO_PROFILE="false"
if [ "$SEED_DEMO" = "yes" ] || [ "$SEED_DEMO" = "y" ]; then
INCLUDE_DEMO_PROFILE="true"
read -p "Demo project name [Next CWV Demo]: " SEED_PROJECT_NAME
SEED_PROJECT_NAME="${SEED_PROJECT_NAME:-Next CWV Demo}"
read -p "Days of data to generate [14]: " SEED_DAYS
SEED_DAYS="${SEED_DAYS:-14}"
fi
echo ""
if [ "$ENABLE_SSL_BOOL" = "true" ]; then
echo -e "${BLUE}▶ Configuring SSL...${NC}"
echo "─────────────────────────────────────────────────────────────"
download_file "$COMPOSE_SSL_URL" "$INSTALL_DIR/docker-compose.ssl.yml"
echo -e "${GREEN}✓ Downloaded docker-compose.ssl.yml${NC}"
CADDYFILE="$INSTALL_DIR/Caddyfile"
if [ -n "$SSL_EMAIL" ]; then
cat > "$CADDYFILE" << CADDYEOF
{
email $SSL_EMAIL
}
$SSL_DOMAIN {
reverse_proxy monitor-app:3000
}
CADDYEOF
else
cat > "$CADDYFILE" << CADDYEOF
$SSL_DOMAIN {
reverse_proxy monitor-app:3000
}
CADDYEOF
fi
echo -e "${GREEN}✓ Generated Caddyfile${NC}"
echo ""
fi
echo -e "${BLUE}▶ Writing configuration...${NC}"
echo "─────────────────────────────────────────────────────────────"
cat > "$ENV_FILE" << EOF
# CWV Monitor Production Configuration
# Generated on $(date)
# ═══════════════════════════════════════════════════════════════
# ─────────────────────────────────────────────────────────────────
# Image Settings
# ─────────────────────────────────────────────────────────────────
IMAGE_REGISTRY=$IMAGE_REGISTRY
IMAGE_TAG=$IMAGE_TAG
# ─────────────────────────────────────────────────────────────────
# Application
# ─────────────────────────────────────────────────────────────────
AUTH_BASE_URL=$AUTH_BASE_URL
APP_PORT=$APP_PORT
TRUST_PROXY=$TRUST_PROXY
NODE_ENV=production
LOG_LEVEL=info
# ─────────────────────────────────────────────────────────────────
# Initial Admin User (created on first startup)
# ─────────────────────────────────────────────────────────────────
INITIAL_USER_EMAIL=$INITIAL_USER_EMAIL
INITIAL_USER_NAME="$INITIAL_USER_NAME"
INITIAL_USER_PASSWORD="$INITIAL_USER_PASSWORD"
# ─────────────────────────────────────────────────────────────────
# ClickHouse Database
# ─────────────────────────────────────────────────────────────────
CLICKHOUSE_HOST=$CLICKHOUSE_HOST
CLICKHOUSE_PORT=$CLICKHOUSE_PORT
CLICKHOUSE_DB=$CLICKHOUSE_DB
CLICKHOUSE_USER=$CLICKHOUSE_USER
CLICKHOUSE_PASSWORD=$CLICKHOUSE_PASSWORD
# ─────────────────────────────────────────────────────────────────
# Security
# ─────────────────────────────────────────────────────────────────
BETTER_AUTH_SECRET=$BETTER_AUTH_SECRET
MIN_PASSWORD_SCORE=$MIN_PASSWORD_SCORE
RATE_LIMIT_WINDOW_MS=$RATE_LIMIT_WINDOW_MS
MAX_LOGIN_ATTEMPTS=$MAX_LOGIN_ATTEMPTS
EOF
if [ "$INCLUDE_DEMO_PROFILE" = "true" ]; then
cat >> "$ENV_FILE" << EOF
# ─────────────────────────────────────────────────────────────────
# Demo Data Seeding (used with: docker compose --profile demo up)
# ─────────────────────────────────────────────────────────────────
SEED_PROJECT_NAME="$SEED_PROJECT_NAME"
SEED_DAYS=$SEED_DAYS
SEED_RESET=false
EOF
fi
chmod 600 "$ENV_FILE"
echo -e "${GREEN}✓ Configuration written to: $ENV_FILE${NC}"
echo ""
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ Setup Complete! ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo "Files created in: $INSTALL_DIR"
echo " - docker-compose.yml"
if [ "$ENABLE_SSL_BOOL" = "true" ]; then
echo " - docker-compose.ssl.yml"
echo " - Caddyfile"
fi
echo " - .env"
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo ""
# Build the docker compose command based on options
COMPOSE_CMD="docker compose"
if [ "$ENABLE_SSL_BOOL" = "true" ]; then
COMPOSE_CMD="$COMPOSE_CMD -f docker-compose.yml -f docker-compose.ssl.yml"
fi
if [ "$INCLUDE_DEMO_PROFILE" = "true" ]; then
echo " 1. Start the services with demo data:"
echo -e " ${GREEN}cd $INSTALL_DIR && $COMPOSE_CMD --profile demo up -d${NC}"
echo ""
echo -e " ${YELLOW}Note:${NC} The --profile demo flag seeds demo data on first startup."
echo -e " For subsequent starts without re-seeding, use:"
echo -e " ${GREEN}$COMPOSE_CMD up -d${NC}"
else
echo " 1. Start the services:"
echo -e " ${GREEN}cd $INSTALL_DIR && $COMPOSE_CMD up -d${NC}"
fi
echo ""
echo " 2. Access the dashboard:"
echo -e " ${GREEN}$AUTH_BASE_URL${NC}"
if [ "$ENABLE_SSL_BOOL" = "true" ]; then
echo ""
echo -e " ${YELLOW}Note:${NC} SSL certificates will be automatically obtained on first request."
echo -e " Make sure your domain ${GREEN}$SSL_DOMAIN${NC} points to this server."
fi
echo ""
echo " 3. Login with:"
echo -e " Email: ${GREEN}$INITIAL_USER_EMAIL${NC}"
echo ""
echo -e "${RED}⚠ IMPORTANT:${NC} Keep the .env file secure. It contains secrets!"
echo ""
}
# Call main function - this ensures bash reads the entire script before executing
main "$@"