Files
flvx/panel_install.sh
T
2025-06-25 11:24:05 +08:00

777 lines
23 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
set -e
# 解决 macOS 下 tr 可能出现的非法字节序列问题
export LANG=en_US.UTF-8
export LC_ALL=C
# 全局下载地址配置
DOCKER_COMPOSE_URL="https://ghfast.top/https://github.com/bqlpfy/forward-panel/raw/refs/heads/main/docker-compose.yml"
GOST_SQL_URL="https://ghfast.top/https://github.com/bqlpfy/forward-panel/raw/refs/heads/main/gost.sql"
# 检查 docker-compose 或 docker compose 命令
check_docker() {
if command -v docker-compose &> /dev/null; then
DOCKER_CMD="docker-compose"
elif command -v docker &> /dev/null; then
if docker compose version &> /dev/null; then
DOCKER_CMD="docker compose"
else
echo "错误:检测到 docker,但不支持 'docker compose' 命令。请安装 docker-compose 或更新 docker 版本。"
exit 1
fi
else
echo "错误:未检测到 docker 或 docker-compose 命令。请先安装 Docker。"
exit 1
fi
echo "检测到 Docker 命令:$DOCKER_CMD"
}
# 检查Docker IPv6支持的多种方法
check_docker_ipv6_support() {
echo "🔍 验证Docker IPv6支持..."
# 方法1: 检查daemon.json配置文件
if [ -f "/etc/docker/daemon.json" ]; then
if grep -q '"ipv6".*true' /etc/docker/daemon.json 2>/dev/null; then
echo "✅ daemon.json配置检查通过"
return 0
fi
fi
# 方法2: 尝试创建IPv6测试网络
echo "🧪 尝试创建IPv6测试网络..."
if docker network create --ipv6 --subnet=2001:db8:test::/64 ipv6-test-net 2>/dev/null; then
echo "✅ IPv6网络创建成功"
# 清理测试网络
docker network rm ipv6-test-net >/dev/null 2>&1
return 0
fi
# 方法3: 检查docker info的详细输出
if docker info 2>/dev/null | grep -i ipv6 | grep -q true; then
echo "✅ docker info IPv6检查通过"
return 0
fi
# 方法4: 检查Docker版本和配置
DOCKER_VERSION=$(docker version --format '{{.Server.Version}}' 2>/dev/null)
if [ -n "$DOCKER_VERSION" ]; then
echo "ℹ️ Docker版本: $DOCKER_VERSION"
# 对于新版本Docker,配置可能需要更多时间生效
echo "⏳ 等待Docker配置生效..."
sleep 5
# 再次尝试网络创建
if docker network create --ipv6 --subnet=2001:db8:test2::/64 ipv6-test-net2 2>/dev/null; then
echo "✅ 延迟检查IPv6网络创建成功"
docker network rm ipv6-test-net2 >/dev/null 2>&1
return 0
fi
fi
echo "⚠️ IPv6支持验证失败,但配置可能已生效"
echo "ℹ️ 建议手动验证: docker network create --ipv6 --subnet=2001:db8:test::/64 test-net"
return 1
}
# 检查IPv6支持
check_ipv6_support() {
echo "🔍 检查IPv6支持..."
# 检查内核是否支持IPv6
if [ ! -f /proc/net/if_inet6 ]; then
echo "⚠️ 警告:系统内核不支持IPv6"
return 1
fi
# 检查Docker是否支持IPv6
if ! check_docker_ipv6_support; then
echo "⚠️ Docker守护进程未启用IPv6支持,正在自动配置..."
# 自动配置Docker daemon.json
configure_docker_ipv6
# 重新检查
if ! check_docker_ipv6_support; then
echo "❌ Docker IPv6配置失败,请手动检查"
return 1
fi
fi
echo "✅ IPv6支持检查通过"
return 0
}
# 配置Docker IPv6支持
configure_docker_ipv6() {
echo "🔧 正在配置Docker IPv6支持..."
DAEMON_JSON_PATH="/etc/docker/daemon.json"
# 检查是否有写入权限
if [ ! -w "/etc/docker" ] && [ ! -w "$DAEMON_JSON_PATH" ]; then
echo "⚠️ 需要管理员权限来配置Docker"
echo "🔐 正在请求sudo权限..."
fi
# 备份现有配置
if [ -f "$DAEMON_JSON_PATH" ]; then
sudo cp "$DAEMON_JSON_PATH" "$DAEMON_JSON_PATH.backup.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || {
echo "❌ 无法备份daemon.json文件,请检查权限"
return 1
}
echo "✅ 已备份现有daemon.json配置"
fi
# 创建或更新daemon.json
if [ -f "$DAEMON_JSON_PATH" ]; then
# 文件存在,检查是否已配置IPv6
if grep -q '"ipv6".*true' "$DAEMON_JSON_PATH" 2>/dev/null; then
echo "ℹ️ daemon.json已配置IPv6支持"
else
echo "🔧 更新现有daemon.json配置..."
# 尝试多种方式更新JSON
JSON_UPDATED=false
# 方法1: 使用jq
if command -v jq &> /dev/null; then
if sudo jq '. + {"ipv6": true, "fixed-cidr-v6": "2001:db8:1::/64"}' "$DAEMON_JSON_PATH" > /tmp/daemon.json.tmp 2>/dev/null && \
sudo mv /tmp/daemon.json.tmp "$DAEMON_JSON_PATH" 2>/dev/null; then
JSON_UPDATED=true
echo "✅ 使用jq更新JSON配置"
fi
fi
# 方法2: 使用Python3
if [ "$JSON_UPDATED" = false ] && command -v python3 &> /dev/null; then
if sudo python3 -c "
import json
try:
with open('$DAEMON_JSON_PATH', 'r') as f:
config = json.load(f)
except:
config = {}
config['ipv6'] = True
config['fixed-cidr-v6'] = '2001:db8:1::/64'
with open('$DAEMON_JSON_PATH', 'w') as f:
json.dump(config, f, indent=2)
print('JSON updated successfully')
" 2>/dev/null; then
JSON_UPDATED=true
echo "✅ 使用Python3更新JSON配置"
fi
fi
# 方法3: 手动创建新文件(简单覆盖)
if [ "$JSON_UPDATED" = false ]; then
echo "⚠️ 无法解析现有JSON,将创建新的配置文件"
read -p "这将覆盖现有的daemon.json配置,是否继续?(y/N): " confirm
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
sudo tee "$DAEMON_JSON_PATH" > /dev/null <<EOF
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64"
}
EOF
JSON_UPDATED=true
echo "✅ 创建新的daemon.json配置"
else
echo "❌ 用户取消操作"
return 1
fi
fi
if [ "$JSON_UPDATED" = false ]; then
echo "❌ 无法更新daemon.json,请手动配置"
return 1
fi
fi
else
# 文件不存在,创建新的
echo "🆕 创建新的daemon.json配置..."
sudo mkdir -p /etc/docker 2>/dev/null || {
echo "❌ 无法创建/etc/docker目录"
return 1
}
sudo tee "$DAEMON_JSON_PATH" > /dev/null <<EOF
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64"
}
EOF
fi
echo "✅ Docker IPv6配置完成"
echo "🔄 重启Docker服务..."
# 重启Docker服务
if command -v systemctl &> /dev/null; then
if sudo systemctl restart docker 2>/dev/null; then
echo "⏳ 等待Docker服务启动..."
sleep 5
if sudo systemctl is-active docker &> /dev/null; then
echo "✅ Docker服务重启成功"
else
echo "❌ Docker服务启动失败"
sudo systemctl status docker --no-pager -l
return 1
fi
else
echo "❌ 无法重启Docker服务"
return 1
fi
elif command -v service &> /dev/null; then
if sudo service docker restart 2>/dev/null; then
sleep 5
echo "✅ Docker服务重启完成"
else
echo "❌ 无法重启Docker服务"
return 1
fi
else
echo "⚠️ 无法自动重启Docker服务,请手动重启:"
echo " sudo systemctl restart docker"
echo " 或者: sudo service docker restart"
return 1
fi
return 0
}
# 配置IPv6网络
configure_ipv6_network() {
if [[ "$ENABLE_IPV6" == "true" ]]; then
echo "🌐 配置IPv6网络..."
# 检查IPv6支持
if ! check_ipv6_support; then
echo "❌ IPv6支持检查失败"
echo ""
echo "📋 您有以下选择:"
echo "1. 继续安装(仅使用IPv4网络)"
echo "2. 取消安装,手动配置IPv6后重试"
echo ""
read -p "请选择 (1/2): " ipv6_choice
case $ipv6_choice in
1)
echo "ℹ️ 继续安装,将使用IPv4网络"
ENABLE_IPV6=false
return 0
;;
2)
echo "❌ 安装已取消"
echo "ℹ️ 请参考以下步骤手动配置IPv6:"
echo " 1. 确保系统内核支持IPv6"
echo " 2. 编辑 /etc/docker/daemon.json 添加:"
echo ' {"ipv6": true, "fixed-cidr-v6": "2001:db8:1::/64"}'
echo " 3. 重启Docker服务: sudo systemctl restart docker"
echo " 4. 重新运行此安装脚本"
exit 1
;;
*)
echo "❌ 无效选择,安装已取消"
exit 1
;;
esac
fi
# 修改docker-compose.yml以支持IPv6
# 检测操作系统,使用合适的sed语法
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
sed -i '' '/networks:/,/subnet: 172.20.0.0\/16/c\
networks:\
gost-network:\
driver: bridge\
enable_ipv6: true\
ipam:\
config:\
- subnet: 172.20.0.0/16\
- subnet: 2001:db8:1::/64' docker-compose.yml
else
# Linux
sed -i.bak '/networks:/,/subnet: 172.20.0.0\/16/c\
networks:\
gost-network:\
driver: bridge\
enable_ipv6: true\
ipam:\
config:\
- subnet: 172.20.0.0/16\
- subnet: 2001:db8:1::/64' docker-compose.yml
fi
echo "✅ IPv6网络配置完成"
echo "ℹ️ IPv6子网: 2001:db8:1::/64"
echo "ℹ️ IPv4子网: 172.20.0.0/16"
else
echo "ℹ️ 跳过IPv6网络配置,仅使用IPv4网络"
fi
}
# 显示菜单
show_menu() {
echo "==============================================="
echo " 面板管理脚本"
echo "==============================================="
echo "请选择操作:"
echo "1. 安装面板"
echo "2. 更新面板"
echo "3. 卸载面板"
echo "4. 退出"
echo "==============================================="
}
generate_random() {
LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c16
}
# 获取用户输入的配置参数
get_config_params() {
echo "🔧 请输入配置参数:"
echo "📡 节点端服务器和面板通信的地址,需要能正常访问(IPv6不需要加[])"
while true; do
read -p "当前面板服务器地址: " SERVER_HOST
if [ -n "$SERVER_HOST" ]; then
break
else
echo "面板服务器地址不能为空,请输入。"
fi
done
read -p "前端端口(默认 6366): " FRONTEND_PORT
FRONTEND_PORT=${FRONTEND_PORT:-6366}
read -p "后端端口(默认 6365): " BACKEND_PORT
BACKEND_PORT=${BACKEND_PORT:-6365}
# 询问是否开启IPv6支持
echo ""
echo "🌐 IPv6 支持配置"
echo "开启IPv6可以让容器支持IPv6网络连接"
echo "ℹ️ 注意事项:"
echo " - 需要系统内核支持IPv6"
echo " - 需要Docker守护进程启用IPv6支持"
echo " - 如果Docker未启用IPv6,请先配置Docker后再安装"
echo ""
read -p "是否开启IPv6支持?(y/N): " ENABLE_IPV6
if [[ "$ENABLE_IPV6" == "y" || "$ENABLE_IPV6" == "Y" ]]; then
ENABLE_IPV6=true
echo "✅ 已选择开启IPv6支持"
echo "ℹ️ 将使用IPv6子网: 2001:db8:1::/64"
else
ENABLE_IPV6=false
echo "ℹ️ 未开启IPv6支持,使用IPv4网络"
fi
DB_NAME=$(generate_random)
DB_USER=$(generate_random)
DB_PASSWORD=$(generate_random)
JWT_SECRET=$(generate_random)
SERVER_HOST_PORT="${SERVER_HOST}:${BACKEND_PORT}"
}
# 安装功能
install_panel() {
echo "🚀 开始安装面板..."
check_docker
get_config_params
echo "🔽 下载必要文件..."
curl -L -o docker-compose.yml "$DOCKER_COMPOSE_URL"
curl -L -o gost.sql "$GOST_SQL_URL"
echo "✅ 下载完成"
# 配置IPv6网络
configure_ipv6_network
cat > .env <<EOF
DB_NAME=$DB_NAME
DB_USER=$DB_USER
DB_PASSWORD=$DB_PASSWORD
JWT_SECRET=$JWT_SECRET
SERVER_HOST=$SERVER_HOST_PORT
FRONTEND_PORT=$FRONTEND_PORT
BACKEND_PORT=$BACKEND_PORT
ENABLE_IPV6=$ENABLE_IPV6
EOF
echo "🚀 启动 docker 服务..."
$DOCKER_CMD up -d
echo "🎉 部署完成"
echo "✅ .env 文件内容:"
echo "DB_NAME=$DB_NAME"
echo "DB_USER=$DB_USER"
echo "DB_PASSWORD=$DB_PASSWORD"
echo "JWT_SECRET=$JWT_SECRET"
echo "SERVER_HOST=$SERVER_HOST_PORT"
echo "FRONTEND_PORT=$FRONTEND_PORT"
echo "BACKEND_PORT=$BACKEND_PORT"
echo "ENABLE_IPV6=$ENABLE_IPV6"
}
# 更新功能
update_panel() {
echo "🔄 开始更新面板..."
check_docker
echo "🔽 下载最新配置文件..."
curl -L -o docker-compose.yml "$DOCKER_COMPOSE_URL"
echo "✅ 下载完成"
echo "🛑 停止当前服务..."
$DOCKER_CMD down
echo "⬇️ 拉取最新镜像..."
$DOCKER_CMD pull
echo "🚀 启动更新后的服务..."
$DOCKER_CMD up -d
# 等待服务启动
echo "⏳ 等待服务启动..."
# 检查后端容器健康状态
echo "🔍 检查后端服务状态..."
for i in {1..90}; do
if docker ps --format "{{.Names}}" | grep -q "^springboot-backend$"; then
BACKEND_HEALTH=$(docker inspect -f '{{.State.Health.Status}}' springboot-backend 2>/dev/null || echo "unknown")
if [[ "$BACKEND_HEALTH" == "healthy" ]]; then
echo "✅ 后端服务健康检查通过"
break
elif [[ "$BACKEND_HEALTH" == "starting" ]]; then
# 继续等待
:
elif [[ "$BACKEND_HEALTH" == "unhealthy" ]]; then
echo "⚠️ 后端健康状态:$BACKEND_HEALTH"
fi
else
echo "⚠️ 后端容器未找到或未运行"
BACKEND_HEALTH="not_running"
fi
if [ $i -eq 90 ]; then
echo "❌ 后端服务启动超时(90秒)"
echo "🔍 当前状态:$(docker inspect -f '{{.State.Health.Status}}' springboot-backend 2>/dev/null || echo '容器不存在')"
echo "🛑 更新终止"
return 1
fi
# 每15秒显示一次进度
if [ $((i % 15)) -eq 1 ]; then
echo "⏳ 等待后端服务启动... ($i/90) 状态:${BACKEND_HEALTH:-unknown}"
fi
sleep 1
done
# 检查数据库容器健康状态
echo "🔍 检查数据库服务状态..."
for i in {1..60}; do
if docker ps --format "{{.Names}}" | grep -q "^gost-mysql$"; then
DB_HEALTH=$(docker inspect -f '{{.State.Health.Status}}' gost-mysql 2>/dev/null || echo "unknown")
if [[ "$DB_HEALTH" == "healthy" ]]; then
echo "✅ 数据库服务健康检查通过"
break
elif [[ "$DB_HEALTH" == "starting" ]]; then
# 继续等待
:
elif [[ "$DB_HEALTH" == "unhealthy" ]]; then
echo "⚠️ 数据库健康状态:$DB_HEALTH"
fi
else
echo "⚠️ 数据库容器未找到或未运行"
DB_HEALTH="not_running"
fi
if [ $i -eq 60 ]; then
echo "❌ 数据库服务启动超时(60秒)"
echo "🔍 当前状态:$(docker inspect -f '{{.State.Health.Status}}' gost-mysql 2>/dev/null || echo '容器不存在')"
echo "🛑 更新终止"
return 1
fi
# 每10秒显示一次进度
if [ $((i % 10)) -eq 1 ]; then
echo "⏳ 等待数据库服务启动... ($i/60) 状态:${DB_HEALTH:-unknown}"
fi
sleep 1
done
# 从容器环境变量获取数据库信息
echo "🔍 获取数据库配置信息..."
# 等待一下让服务完全就绪
echo "⏳ 等待服务完全就绪..."
sleep 5
# 先检查后端容器是否在运行
if ! docker ps --format "{{.Names}}" | grep -q "^springboot-backend$"; then
echo "❌ 后端容器未运行,无法获取数据库配置"
echo "🔍 当前运行的容器:"
docker ps --format "table {{.Names}}\t{{.Status}}"
echo "🛑 更新终止"
return 1
fi
DB_INFO=$(docker exec springboot-backend env | grep "^DB_" 2>/dev/null || echo "")
if [[ -n "$DB_INFO" ]]; then
DB_NAME=$(echo "$DB_INFO" | grep "^DB_NAME=" | cut -d'=' -f2)
DB_PASSWORD=$(echo "$DB_INFO" | grep "^DB_PASSWORD=" | cut -d'=' -f2)
DB_USER=$(echo "$DB_INFO" | grep "^DB_USER=" | cut -d'=' -f2)
DB_HOST=$(echo "$DB_INFO" | grep "^DB_HOST=" | cut -d'=' -f2)
echo "📋 数据库配置:"
echo " 数据库名: $DB_NAME"
echo " 用户名: $DB_USER"
echo " 主机: $DB_HOST"
else
echo "❌ 无法获取数据库配置信息"
echo "🔍 尝试诊断问题:"
echo " 容器状态: $(docker inspect -f '{{.State.Status}}' springboot-backend 2>/dev/null || echo '容器不存在')"
echo " 健康状态: $(docker inspect -f '{{.State.Health.Status}}' springboot-backend 2>/dev/null || echo '无健康检查')"
# 尝试从 .env 文件读取配置
if [[ -f ".env" ]]; then
echo "🔄 尝试从 .env 文件读取配置..."
DB_NAME=$(grep "^DB_NAME=" .env | cut -d'=' -f2 2>/dev/null)
DB_PASSWORD=$(grep "^DB_PASSWORD=" .env | cut -d'=' -f2 2>/dev/null)
DB_USER=$(grep "^DB_USER=" .env | cut -d'=' -f2 2>/dev/null)
if [[ -n "$DB_NAME" && -n "$DB_PASSWORD" && -n "$DB_USER" ]]; then
echo "✅ 从 .env 文件成功读取数据库配置"
echo "📋 数据库配置:"
echo " 数据库名: $DB_NAME"
echo " 用户名: $DB_USER"
else
echo "❌ .env 文件中的数据库配置不完整"
echo "🛑 更新终止"
return 1
fi
else
echo "❌ 未找到 .env 文件"
echo "🛑 更新终止"
return 1
fi
fi
# 检查必要的数据库配置
if [[ -z "$DB_PASSWORD" || -z "$DB_USER" || -z "$DB_NAME" ]]; then
echo "❌ 数据库配置不完整(缺少必要参数)"
echo "🛑 更新终止"
return 1
fi
# 执行数据库字段变更
echo "🔄 执行数据库结构更新..."
# 创建临时迁移文件(现在有了数据库信息)
cat > temp_migration.sql <<EOF
-- 数据库结构更新
USE \`$DB_NAME\`;
-- user 表:删除 name 字段(如果存在)
SET @sql = (
SELECT IF(
EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'user'
AND column_name = 'name'
),
'ALTER TABLE \`user\` DROP COLUMN \`name\`;',
'SELECT "Column \`name\` not exists in \`user\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- node 表:删除 port 字段、添加 server_ip 字段(如果不存在)
SET @sql = (
SELECT IF(
EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'node'
AND column_name = 'port'
),
'ALTER TABLE \`node\` DROP COLUMN \`port\`;',
'SELECT "Column \`port\` not exists in \`node\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql = (
SELECT IF(
NOT EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'node'
AND column_name = 'server_ip'
),
'ALTER TABLE \`node\` ADD COLUMN \`server_ip\` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;',
'SELECT "Column \`server_ip\` already exists in \`node\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 将 ip 赋值给 server_ip(如果字段都存在)
UPDATE \`node\`
SET \`server_ip\` = \`ip\`
WHERE \`server_ip\` IS NULL;
-- tunnel 表:添加 tcp_listen_addr、udp_listen_addr、protocol(如果不存在)
-- tcp_listen_addr
SET @sql = (
SELECT IF(
NOT EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'tunnel'
AND column_name = 'tcp_listen_addr'
),
'ALTER TABLE \`tunnel\` ADD COLUMN \`tcp_listen_addr\` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT "0.0.0.0";',
'SELECT "Column \`tcp_listen_addr\` already exists in \`tunnel\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- udp_listen_addr
SET @sql = (
SELECT IF(
NOT EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'tunnel'
AND column_name = 'udp_listen_addr'
),
'ALTER TABLE \`tunnel\` ADD COLUMN \`udp_listen_addr\` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT "0.0.0.0";',
'SELECT "Column \`udp_listen_addr\` already exists in \`tunnel\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- protocol
SET @sql = (
SELECT IF(
NOT EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'tunnel'
AND column_name = 'protocol'
),
'ALTER TABLE \`tunnel\` ADD COLUMN \`protocol\` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT "tls";',
'SELECT "Column \`protocol\` already exists in \`tunnel\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
EOF
# 检查数据库容器
if ! docker ps --format "{{.Names}}" | grep -q "^gost-mysql$"; then
echo "❌ 数据库容器 gost-mysql 未运行"
echo "🔍 当前运行的容器:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
echo "❌ 数据库结构更新失败,请手动执行 temp_migration.sql"
echo "📁 迁移文件已保存为 temp_migration.sql"
return 1
fi
# 执行数据库迁移
if docker exec -i gost-mysql mysql -u "$DB_USER" -p"$DB_PASSWORD" < temp_migration.sql 2>/dev/null; then
echo "✅ 数据库结构更新完成"
else
echo "⚠️ 使用用户密码失败,尝试root密码..."
if docker exec -i gost-mysql mysql -u root -p"$DB_PASSWORD" < temp_migration.sql 2>/dev/null; then
echo "✅ 数据库结构更新完成"
else
echo "❌ 数据库结构更新失败,请手动执行 temp_migration.sql"
echo "📁 迁移文件已保存为 temp_migration.sql"
echo "🔍 数据库容器状态: $(docker inspect -f '{{.State.Status}}' gost-mysql 2>/dev/null || echo '容器不存在')"
echo "🛑 更新终止"
return 1
fi
fi
# 清理临时文件
rm -f temp_migration.sql
echo "✅ 更新完成"
}
# 卸载功能
uninstall_panel() {
echo "🗑️ 开始卸载面板..."
check_docker
if [[ ! -f "docker-compose.yml" ]]; then
echo "⚠️ 未找到 docker-compose.yml 文件,正在下载以完成卸载..."
curl -L -o docker-compose.yml "$DOCKER_COMPOSE_URL"
echo "✅ docker-compose.yml 下载完成"
fi
read -p "确认卸载面板吗?此操作将停止并删除所有容器和数据 (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "❌ 取消卸载"
return 0
fi
echo "🛑 停止并删除容器、镜像、卷..."
$DOCKER_CMD down --rmi all --volumes --remove-orphans
echo "🧹 删除配置文件..."
rm -f docker-compose.yml gost.sql .env
echo "✅ 卸载完成"
}
# 主逻辑
main() {
# 显示交互式菜单
while true; do
show_menu
read -p "请输入选项 (1-4): " choice
case $choice in
1)
install_panel
break
;;
2)
update_panel
break
;;
3)
uninstall_panel
break
;;
4)
echo "👋 退出脚本"
exit 0
;;
*)
echo "❌ 无效选项,请输入 1-4"
echo ""
;;
esac
done
}
# 执行主函数
main