forked from zhongruan0522/AntiHub-ALL
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeploy.sh
More file actions
645 lines (551 loc) · 21 KB
/
Copy pathdeploy.sh
File metadata and controls
645 lines (551 loc) · 21 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
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
#!/bin/bash
# AntiHub-ALL 一键部署脚本
# 适用于 Linux 系统
set -e
# 确保从脚本所在目录运行(避免在其它目录执行导致找不到 compose/.env)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_prompt() {
echo -e "${BLUE}[INPUT]${NC} $1"
}
# 检查命令是否存在
check_command() {
if ! command -v "$1" &> /dev/null; then
log_error "$1 未安装,请先安装 $1"
exit 1
fi
}
# 生成随机密钥
generate_random_key() {
if command -v openssl &> /dev/null; then
openssl rand -hex 32
return
fi
# 仅装了 Docker 的环境:用容器生成随机值,避免依赖宿主机 openssl
docker run --rm python:3.11-alpine python -c "import secrets; print(secrets.token_hex(32))"
}
# 生成 Fernet 密钥(用于 PLUGIN_API_ENCRYPTION_KEY)
generate_fernet_key() {
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" 2>/dev/null || \
docker run --rm python:3.11-alpine python -c "import os, base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())"
}
# 读取用户输入(带默认值)
read_with_default() {
local prompt="$1"
local default="$2"
local value
read -p "$prompt [$default]: " value
# 兼容某些终端/粘贴会带 CR(\r)的情况:避免后续写入 .env / 解析变量时出错
value=${value//$'\r'/}
echo "${value:-$default}"
}
# 读取密码(明文输入一次)
read_password() {
local prompt="$1"
local min_length="${2:-1}"
local password
while true; do
# 按用户需求:明文输入一次,不再二次确认
read -p "$prompt: " password
password=${password//$'\r'/}
if [ -z "$password" ]; then
log_error "密码不能为空"
continue
fi
if [ "${#password}" -lt "$min_length" ]; then
log_error "密码长度至少 ${min_length} 位"
continue
fi
echo "$password"
break
done
}
# 写入 .env:不依赖 sed(避免特殊字符/终端粘贴导致 sed 解析报错)
write_env_file() {
local env_file="$1"
local tmp_file="${env_file}.tmp"
local postgres_user
local postgres_db
postgres_user=$(grep "^POSTGRES_USER=" "$env_file" | cut -d'=' -f2- 2>/dev/null || true)
postgres_db=$(grep "^POSTGRES_DB=" "$env_file" | cut -d'=' -f2- 2>/dev/null || true)
postgres_user=${postgres_user//$'\r'/}
postgres_db=${postgres_db//$'\r'/}
postgres_user=${postgres_user:-antihub}
postgres_db=${postgres_db:-antihub}
while IFS= read -r line || [ -n "$line" ]; do
line=${line//$'\r'/}
case "$line" in
WEB_PORT=*)
printf '%s\n' "WEB_PORT=$WEB_PORT"
;;
BACKEND_PORT=*)
printf '%s\n' "BACKEND_PORT=$BACKEND_PORT"
;;
\#\ POSTGRES_PORT=*|\#POSTGRES_PORT=*|POSTGRES_PORT=*)
printf '%s\n' "POSTGRES_PORT=$POSTGRES_PORT"
;;
COOKIE_HTTP=*)
printf '%s\n' "COOKIE_HTTP=$COOKIE_HTTP"
;;
ADMIN_USERNAME=*)
printf '%s\n' "ADMIN_USERNAME=$ADMIN_USERNAME"
;;
ADMIN_PASSWORD=*)
printf '%s\n' "ADMIN_PASSWORD=$ADMIN_PASSWORD"
;;
JWT_SECRET_KEY=*)
printf '%s\n' "JWT_SECRET_KEY=$JWT_SECRET"
;;
POSTGRES_PASSWORD=*)
printf '%s\n' "POSTGRES_PASSWORD=$POSTGRES_PASSWORD"
;;
PLUGIN_API_ENCRYPTION_KEY=*)
printf '%s\n' "PLUGIN_API_ENCRYPTION_KEY=$ENCRYPTION_KEY"
;;
DATABASE_URL=*)
printf '%s\n' "DATABASE_URL=postgresql+asyncpg://${postgres_user}:${POSTGRES_PASSWORD}@postgres:5432/${postgres_db}"
;;
*)
printf '%s\n' "$line"
;;
esac
done < "$env_file" > "$tmp_file"
mv "$tmp_file" "$env_file"
}
get_env_value() {
local file="$1"
local key="$2"
if [ ! -f "$file" ]; then
return 0
fi
local value
value=$(grep -m 1 "^${key}=" "$file" 2>/dev/null | cut -d'=' -f2- || true)
value=${value//$'\r'/}
printf '%s' "$value"
}
validate_admin_password() {
local env_file="$1"
local min_length="${2:-6}"
local admin_password
admin_password=$(get_env_value "$env_file" "ADMIN_PASSWORD")
# 允许留空:留空表示不初始化管理员账号(适用于仅 OAuth 登录场景)
if [ -z "$admin_password" ]; then
return 0
fi
if [ "${#admin_password}" -lt "$min_length" ]; then
log_error "ADMIN_PASSWORD 长度至少 ${min_length} 位(当前 ${#admin_password} 位),否则后端会报参数校验错误/无法登录"
log_error "请修改 ${env_file} 中的 ADMIN_PASSWORD 后重试"
return 1
fi
return 0
}
# 修复 docker 目录权限(解决 NAS 等环境下的权限问题)
fix_permissions() {
log_info "修复 docker 目录权限..."
# 仅处理 docker 目录,避免误改 .env / 仓库其它文件权限
TARGET_DIR="$SCRIPT_DIR/docker"
if [ ! -d "$TARGET_DIR" ]; then
log_warn "未找到 docker 目录,跳过权限修复"
return 0
fi
# 目录设置为 755 (rwxr-xr-x)
find "$TARGET_DIR" -type d -exec chmod 755 {} \; 2>/dev/null || true
# 普通文件设置为 644 (rw-r--r--)
find "$TARGET_DIR" -type f -exec chmod 644 {} \; 2>/dev/null || true
# 脚本文件设置为 755 (rwxr-xr-x)
find "$TARGET_DIR" -name "*.sh" -type f -exec chmod 755 {} \; 2>/dev/null || true
log_info "docker 目录权限修复完成"
}
# 初始化 compose 环境(供部署/升级/卸载共用)
prepare_compose() {
if [ ! -f docker-compose.yml ]; then
log_error "未找到 docker-compose.yml,请在项目根目录运行此脚本"
exit 1
fi
# 检查依赖
log_info "检查系统依赖..."
check_command docker
# 检测 docker compose 命令(优先使用新版本)
if docker compose version &> /dev/null; then
DOCKER_COMPOSE="docker compose"
elif command -v docker-compose &> /dev/null; then
DOCKER_COMPOSE="docker-compose"
else
log_error "docker-compose 或 docker compose 未安装"
exit 1
fi
log_info "使用命令: $DOCKER_COMPOSE"
# 组合 docker compose 文件:基础 compose(web/backend/postgres/redis)
COMPOSE_FILES="-f docker-compose.yml"
compose() {
$DOCKER_COMPOSE $COMPOSE_FILES "$@"
}
# 检查 Docker 是否运行
if ! docker info &> /dev/null; then
log_error "Docker 未运行,请先启动 Docker 服务"
exit 1
fi
}
# 部署(首次部署 / 重装)
deploy() {
log_info "开始部署 AntiHub-ALL..."
echo ""
# 0. 修复权限(解决 NAS 等环境下的权限问题)
fix_permissions
# 1. 初始化 compose 环境
prepare_compose
if ! command -v openssl &> /dev/null; then
log_warn "openssl 未安装,将用 Docker 生成随机密钥(可能会额外拉取 python:3.11-alpine 镜像)"
fi
# 2. 检查 .env 文件
ENV_BACKUP_FILE=""
if [ -f .env ]; then
log_warn ".env 文件已存在"
read -p "是否覆盖现有配置?(y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "保留现有配置,跳过环境变量生成"
ENV_EXISTS=true
else
ENV_EXISTS=false
ENV_BACKUP_FILE=".env.bak.$(date +\"%Y%m%d_%H%M%S\")"
cp .env "$ENV_BACKUP_FILE"
log_info "已备份原 .env 到 ${ENV_BACKUP_FILE}"
fi
else
ENV_EXISTS=false
fi
# 3. 生成环境变量配置
if [ "$ENV_EXISTS" = false ]; then
log_info "开始配置部署参数..."
echo ""
if [ ! -f .env.example ]; then
log_error ".env.example 文件不存在"
exit 1
fi
cp .env.example .env
# 3.1 配置端口
log_info "=== 端口配置 ==="
log_prompt "配置服务端口(直接回车使用默认值)"
echo ""
WEB_PORT=$(read_with_default "Web 前端端口(对外暴露)" "3000")
BACKEND_PORT=$(read_with_default "Backend 后端端口(仅本地)" "8000")
POSTGRES_PORT=$(read_with_default "PostgreSQL 数据库端口(仅本地)" "5432")
echo ""
log_info "端口配置完成:"
echo " Web: $WEB_PORT (0.0.0.0:$WEB_PORT)"
echo " Backend: $BACKEND_PORT (127.0.0.1:$BACKEND_PORT)"
echo " PostgreSQL: $POSTGRES_PORT (127.0.0.1:$POSTGRES_PORT)"
echo ""
# 3.2 访问方式(影响登录 Cookie 的 Secure 属性)
log_info "=== 访问方式(影响登录 Cookie) ==="
echo "请选择你准备如何访问前端:"
echo " 1) 域名 + HTTPS(推荐,Cookie 将带 Secure)"
echo " 2) IP 直连 + HTTP(测试/内网,Cookie 不带 Secure)"
COOKIE_HTTP="HTTPS"
DOMAIN_NAME=""
while true; do
read -p "请选择 [1-2] (默认 1): " access_choice
access_choice=${access_choice//$'\r'/}
access_choice=${access_choice:-1}
case "$access_choice" in
1)
COOKIE_HTTP="HTTPS"
read -p "域名(可选,仅用于部署完成后的提示输出;留空跳过): " DOMAIN_NAME
DOMAIN_NAME=${DOMAIN_NAME//$'\r'/}
;;
2)
COOKIE_HTTP="HTTP"
DOMAIN_NAME=""
;;
*)
log_warn "无效选择,请输入 1 或 2"
continue
;;
esac
break
done
echo ""
log_info "访问方式配置完成:COOKIE_HTTP=$COOKIE_HTTP"
if [ -n "$DOMAIN_NAME" ]; then
echo " 域名: $DOMAIN_NAME"
fi
echo ""
# 3.3 配置管理员账户
log_info "=== 管理员账户配置 ==="
ADMIN_USERNAME=$(read_with_default "管理员用户名" "admin")
log_prompt "设置管理员密码(至少 6 位,否则无法登录)"
ADMIN_PASSWORD=$(read_password "管理员密码" 6)
echo ""
log_info "管理员账户配置完成"
echo ""
# 3.4 生成密钥
log_info "生成安全密钥..."
OLD_JWT_SECRET=$(get_env_value "$ENV_BACKUP_FILE" "JWT_SECRET_KEY")
OLD_POSTGRES_PASSWORD=$(get_env_value "$ENV_BACKUP_FILE" "POSTGRES_PASSWORD")
OLD_ENCRYPTION_KEY=$(get_env_value "$ENV_BACKUP_FILE" "PLUGIN_API_ENCRYPTION_KEY")
if [ -n "$OLD_JWT_SECRET" ] && [ "$OLD_JWT_SECRET" != "please-change-me" ]; then
JWT_SECRET="$OLD_JWT_SECRET"
else
JWT_SECRET=$(generate_random_key)
fi
if [ -n "$OLD_POSTGRES_PASSWORD" ] && [ "$OLD_POSTGRES_PASSWORD" != "please-change-me" ]; then
POSTGRES_PASSWORD="$OLD_POSTGRES_PASSWORD"
else
POSTGRES_PASSWORD=$(generate_random_key | cut -c1-24)
fi
log_info "生成 Fernet 加密密钥..."
if [ -n "$OLD_ENCRYPTION_KEY" ] && [ "$OLD_ENCRYPTION_KEY" != "please-generate-a-valid-fernet-key" ]; then
ENCRYPTION_KEY="$OLD_ENCRYPTION_KEY"
else
ENCRYPTION_KEY=$(generate_fernet_key)
fi
# 3.5 替换 .env 中的占位符(兼容 Linux 和 macOS)
log_info "写入配置文件..."
write_env_file ".env"
log_info "环境变量配置已生成"
echo ""
fi
# 3.6 校验关键配置(避免启动后才发现登录/校验错误)
validate_admin_password ".env" 6
# 4. 拉取镜像
log_info "拉取 Docker 镜像..."
compose pull
# 5. 停止旧容器(如果存在)
log_info "停止旧容器..."
compose down 2>/dev/null || true
# 6. 先启动基础依赖(数据库 / 缓存),并完成数据库初始化,再启动主容器
log_info "启动数据库与缓存(postgres/redis)..."
compose up -d postgres redis
log_info "检查 PostgreSQL 状态..."
POSTGRES_USER_CHECK=$(grep "^POSTGRES_USER=" .env | cut -d'=' -f2 || echo "antihub")
for i in {1..30}; do
if compose exec -T postgres pg_isready -U "$POSTGRES_USER_CHECK" &> /dev/null; then
log_info "PostgreSQL 已就绪"
break
fi
if [ $i -eq 30 ]; then
log_error "PostgreSQL 启动超时"
exit 1
fi
sleep 2
done
POSTGRES_USER_ENV=$(grep "^POSTGRES_USER=" .env | cut -d'=' -f2 || echo "antihub")
POSTGRES_PASSWORD_ENV=$(grep "^POSTGRES_PASSWORD=" .env | cut -d'=' -f2- || echo "please-change-me")
POSTGRES_DB_ENV=$(grep "^POSTGRES_DB=" .env | cut -d'=' -f2 || echo "antihub")
# 初始化/同步数据库(Backend 主库)
log_info "初始化数据库(${POSTGRES_DB_ENV})..."
compose exec -T postgres psql -X -v ON_ERROR_STOP=1 \
-U "$POSTGRES_USER_ENV" -d postgres \
-v su_user="$POSTGRES_USER_ENV" -v su_pass="$POSTGRES_PASSWORD_ENV" \
-v main_db="$POSTGRES_DB_ENV" <<-'EOSQL'
SELECT format('ALTER USER %I WITH PASSWORD %L', :'su_user', :'su_pass') \gexec
SELECT format('CREATE DATABASE %I OWNER %I', :'main_db', :'su_user')
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = :'main_db') \gexec
SELECT format('ALTER DATABASE %I OWNER TO %I', :'main_db', :'su_user')
WHERE EXISTS (SELECT 1 FROM pg_database WHERE datname = :'main_db') \gexec
EOSQL
log_info "启动主服务(backend/web)..."
compose up -d backend web
# 检查服务状态
log_info "检查服务状态..."
sleep 3
FAILED_SERVICES=$(compose ps --services --filter "status=exited")
if [ -n "$FAILED_SERVICES" ]; then
log_error "以下服务启动失败:"
echo "$FAILED_SERVICES"
log_info "查看日志:"
compose logs --tail=50
exit 1
fi
# 8. 输出部署信息
echo ""
log_info "=========================================="
log_info "AntiHub-ALL 部署完成!"
log_info "=========================================="
echo ""
# 读取端口配置
WEB_PORT=$(grep "^WEB_PORT=" .env | cut -d'=' -f2 || echo "3000")
BACKEND_PORT=$(grep "^BACKEND_PORT=" .env | cut -d'=' -f2 || echo "8000")
POSTGRES_PORT=$(grep "^POSTGRES_PORT=" .env | cut -d'=' -f2 || echo "5432")
POSTGRES_DB=$(grep "^POSTGRES_DB=" .env | cut -d'=' -f2 || echo "antihub")
POSTGRES_DB=${POSTGRES_DB//$'\r'/}
ADMIN_USERNAME=$(grep "^ADMIN_USERNAME=" .env | cut -d'=' -f2 || echo "admin")
ADMIN_PASSWORD=$(grep "^ADMIN_PASSWORD=" .env | cut -d'=' -f2-)
COOKIE_HTTP=$(grep "^COOKIE_HTTP=" .env | cut -d'=' -f2 || echo "HTTPS")
COOKIE_HTTP=${COOKIE_HTTP//$'\r'/}
COOKIE_HTTP_UPPER=$(echo "$COOKIE_HTTP" | tr '[:lower:]' '[:upper:]')
# 获取服务器 IP
SERVER_IP=$(hostname -I | awk '{print $1}' || echo "YOUR_SERVER_IP")
log_info "访问地址:"
if [ "$COOKIE_HTTP_UPPER" = "HTTPS" ]; then
if [ -n "$DOMAIN_NAME" ]; then
echo " 前端(HTTPS): https://${DOMAIN_NAME}"
else
echo " 前端(HTTPS): https://<your-domain>"
fi
echo " Web 上游(给反代用): http://127.0.0.1:${WEB_PORT}"
else
echo " 前端(对外): http://${SERVER_IP}:${WEB_PORT}"
echo " 前端(本地): http://localhost:${WEB_PORT}"
fi
echo " 后端(仅本地): http://127.0.0.1:${BACKEND_PORT}"
echo " Cookie 模式: ${COOKIE_HTTP_UPPER}(HTTP=不加 Secure;HTTPS=加 Secure)"
echo ""
log_info "反向代理提示(必读):"
echo " 需要把 /backend 转发到后端,否则前端请求会 404"
echo " / -> http://127.0.0.1:${WEB_PORT}"
echo " /backend -> http://127.0.0.1:${BACKEND_PORT}"
echo ""
log_info "管理员账号:"
echo " 用户名: ${ADMIN_USERNAME}"
echo " 密码: ${ADMIN_PASSWORD}"
echo ""
log_info "数据库信息(仅本地访问):"
echo " PostgreSQL: localhost:${POSTGRES_PORT}"
echo " 数据库: ${POSTGRES_DB}"
echo ""
log_info "常用命令:"
echo " 查看日志: $DOCKER_COMPOSE $COMPOSE_FILES logs -f"
echo " 停止服务: $DOCKER_COMPOSE $COMPOSE_FILES down"
echo " 重启服务: $DOCKER_COMPOSE $COMPOSE_FILES restart"
echo " 查看状态: $DOCKER_COMPOSE $COMPOSE_FILES ps"
echo ""
log_warn "重要提示:"
echo " 1. 请妥善保管 .env 文件中的密钥"
echo " 2. Web 端口已对外暴露,建议配置防火墙"
echo " 3. Backend 和数据库仅本地访问(127.0.0.1)"
echo " 4. 反向代理需要配置 /backend -> http://127.0.0.1:${BACKEND_PORT}"
echo " 5. 用 IP 直连 HTTP 时设置 COOKIE_HTTP=HTTP;用域名 HTTPS 时保持 COOKIE_HTTP=HTTPS"
echo ""
}
upgrade() {
log_info "开始升级 AntiHub-ALL(仅升级 web/backend,不操作数据库)..."
echo ""
fix_permissions
prepare_compose
if [ ! -f .env ]; then
log_warn "未找到 .env,当前目录似乎还未部署,将进入一键部署流程"
deploy
return 0
fi
validate_admin_password ".env" 6
# 备份 .env,避免误改或回滚困难
ENV_BACKUP_FILE=".env.bak.upgrade.$(date +\"%Y%m%d_%H%M%S\")"
cp .env "$ENV_BACKUP_FILE"
log_info "已备份 .env 到 ${ENV_BACKUP_FILE}"
log_info "拉取最新 Docker 镜像(仅 web/backend)..."
compose pull web backend
log_info "重启服务(仅 web/backend),不重启 postgres/redis..."
compose up -d --no-deps web backend
log_info "检查服务状态..."
sleep 3
FAILED_SERVICES=$(compose ps --services --filter "status=exited" | grep -E "^(web|backend)$" || true)
if [ -n "$FAILED_SERVICES" ]; then
log_error "以下服务启动失败(web/backend):"
echo "$FAILED_SERVICES"
log_info "查看日志:"
compose logs --tail=80
exit 1
fi
DB_SERVICES_EXITED=$(compose ps --services --filter "status=exited" | grep -E "^(postgres|redis)$" || true)
if [ -n "$DB_SERVICES_EXITED" ]; then
log_warn "检测到数据库/缓存服务未运行(本次升级不会操作它们):"
echo "$DB_SERVICES_EXITED"
fi
log_info "升级完成(数据库未被重启/重建)!"
echo " 查看状态: $DOCKER_COMPOSE $COMPOSE_FILES ps"
echo " 查看日志: $DOCKER_COMPOSE $COMPOSE_FILES logs -f"
echo ""
}
uninstall() {
log_warn "即将卸载 AntiHub-ALL"
echo ""
prepare_compose
log_warn "卸载将停止并删除容器/网络;可选删除数据卷(会清空数据库数据)"
read -p "是否同时删除数据卷(数据库/缓存)?(y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
log_warn "删除数据卷中(不可恢复)..."
compose down -v --remove-orphans 2>/dev/null || true
else
log_info "保留数据卷..."
compose down --remove-orphans 2>/dev/null || true
fi
if [ -f .env ]; then
read -p "是否删除本地 .env 配置文件?(y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -f .env
log_info ".env 已删除"
else
log_info "保留 .env"
fi
fi
log_info "卸载完成"
echo ""
}
show_menu() {
echo ""
log_info "请选择要执行的操作:"
echo " 1) 一键部署(首次部署/重装)"
echo " 2) 升级(仅升级 web/backend,不操作数据库)"
echo " 3) 卸载(停止并删除容器,可选删除数据卷)"
echo " 0) 退出"
echo ""
while true; do
read -p "请输入序号 [0-3]: " choice
choice=${choice//$'\r'/}
case "$choice" in
1) deploy; break ;;
2) upgrade; break ;;
3) uninstall; break ;;
0) log_info "已退出"; exit 0 ;;
*) log_warn "无效选择,请输入 0/1/2/3" ;;
esac
done
}
case "${1:-}" in
1|deploy|install)
deploy
;;
2|upgrade|update)
upgrade
;;
3|uninstall|remove)
uninstall
;;
-h|--help|help)
echo "Usage: ./deploy.sh [deploy|upgrade|uninstall]"
echo " deploy 一键部署(首次部署/重装)"
echo " upgrade 升级(仅升级 web/backend,不操作数据库)"
echo " uninstall 卸载(停止并删除容器,可选删除数据卷)"
echo ""
echo "不传参数会进入交互菜单。"
;;
"")
show_menu
;;
*)
log_warn "未知参数: $1"
echo "可用参数: deploy | upgrade | uninstall | --help"
echo "或直接运行进入交互菜单。"
exit 1
;;
esac