320 lines
9.1 KiB
Bash
320 lines
9.1 KiB
Bash
#!/bin/bash
|
||
|
||
# OpenList Docker 一键部署脚本(带自动密码检测)
|
||
# 版本:1.3 - 在原有成功脚本基础上添加密码自动处理
|
||
|
||
set -e # 任何命令失败则立即退出脚本
|
||
|
||
# 全局变量
|
||
CONTAINER_NAME="openlist"
|
||
DATA_DIR="/data/openlist"
|
||
IMAGE_NAME="openlistteam/openlist:latest"
|
||
DEFAULT_PORT=5344
|
||
PORT_MAPPING=""
|
||
|
||
# 颜色输出函数
|
||
red() { echo -e "\033[31m$1\033[0m"; }
|
||
green() { echo -e "\033[32m$1\033[0m"; }
|
||
yellow() { echo -e "\033[33m$1\033[0m"; }
|
||
blue() { echo -e "\033[34m$1\033[0m"; }
|
||
|
||
# 错误退出函数
|
||
error_exit() {
|
||
red "❌ 错误:$1"
|
||
exit 1
|
||
}
|
||
|
||
# 检查 Docker 是否可用
|
||
check_docker() {
|
||
blue "🔍 检查 Docker 环境..."
|
||
if ! command -v docker &> /dev/null; then
|
||
error_exit "未检测到 Docker,请先安装 Docker"
|
||
fi
|
||
|
||
if ! docker info &> /dev/null; then
|
||
error_exit "Docker 服务未运行,请启动 Docker 服务"
|
||
fi
|
||
green "✅ Docker 环境检查通过"
|
||
}
|
||
|
||
# 准备数据目录
|
||
prepare_directory() {
|
||
blue "📁 准备数据目录..."
|
||
|
||
# 创建 /data 目录(如果不存在)
|
||
if [ ! -d "/data" ]; then
|
||
yellow "创建 /data 目录..."
|
||
sudo mkdir -p /data || error_exit "创建 /data 目录失败"
|
||
sudo chmod 755 /data || error_exit "设置 /data 权限失败"
|
||
fi
|
||
|
||
# 创建 OpenList 数据目录
|
||
if [ ! -d "$DATA_DIR" ]; then
|
||
yellow "创建 OpenList 数据目录: $DATA_DIR"
|
||
sudo mkdir -p "$DATA_DIR" || error_exit "创建数据目录失败"
|
||
fi
|
||
|
||
# 设置正确的目录权限
|
||
blue "🔧 设置目录权限..."
|
||
sudo chown -R 1000:1000 "$DATA_DIR" || error_exit "设置目录所有权失败"
|
||
sudo chmod -R 755 "$DATA_DIR" || error_exit "设置目录权限失败"
|
||
|
||
# 验证权限设置
|
||
if [ -w "$DATA_DIR" ] && [ -x "$DATA_DIR" ]; then
|
||
green "✅ 目录权限验证通过"
|
||
else
|
||
error_exit "目录权限设置失败,请手动检查权限"
|
||
fi
|
||
|
||
green "✅ 数据目录准备完成"
|
||
}
|
||
|
||
# 清理现有容器
|
||
cleanup_existing_container() {
|
||
blue "🧹 检查现有容器..."
|
||
if docker ps -a --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
|
||
yellow "发现已存在的 OpenList 容器,正在清理..."
|
||
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||
docker rm "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||
green "✅ 旧容器清理完成"
|
||
else
|
||
green "✅ 无现有容器需要清理"
|
||
fi
|
||
}
|
||
|
||
# 检查端口占用
|
||
check_port() {
|
||
local port=$1
|
||
blue "🔍 检查端口 $port 占用情况..."
|
||
|
||
if command -v ss &> /dev/null; then
|
||
if ss -tuln | grep ":$port " > /dev/null; then
|
||
return 1
|
||
fi
|
||
else
|
||
if netstat -tuln | grep ":$port " > /dev/null; then
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
green "✅ 端口 $port 可用"
|
||
return 0
|
||
}
|
||
|
||
# 获取可用端口
|
||
get_available_port() {
|
||
local port=$DEFAULT_PORT
|
||
local max_port=6000
|
||
|
||
while [ $port -le $max_port ]; do
|
||
if check_port $port; then
|
||
echo $port
|
||
return 0
|
||
fi
|
||
port=$((port + 1))
|
||
done
|
||
|
||
error_exit "在 $DEFAULT_PORT-$max_port 范围内找不到可用端口"
|
||
}
|
||
|
||
# 拉取 OpenList 镜像
|
||
pull_openlist_image() {
|
||
blue "📦 拉取 OpenList 镜像..."
|
||
if docker pull "$IMAGE_NAME"; then
|
||
green "✅ OpenList 镜像拉取完成"
|
||
else
|
||
error_exit "拉取 OpenList 镜像失败,请检查网络连接"
|
||
fi
|
||
}
|
||
|
||
# 部署 OpenList 容器(使用特权模式解决权限问题)
|
||
deploy_openlist() {
|
||
blue "🚀 开始部署 OpenList 容器..."
|
||
|
||
# 确定使用的端口
|
||
if check_port $DEFAULT_PORT; then
|
||
PORT_MAPPING="$DEFAULT_PORT:5244"
|
||
green "✅ 使用默认端口:$DEFAULT_PORT"
|
||
else
|
||
local available_port=$(get_available_port)
|
||
PORT_MAPPING="$available_port:5244"
|
||
yellow "📊 使用端口:$available_port (原端口 $DEFAULT_PORT 被占用)"
|
||
fi
|
||
|
||
# 部署命令(使用特权模式解决权限问题)
|
||
blue "🐳 启动容器..."
|
||
if docker run -d \
|
||
--name "$CONTAINER_NAME" \
|
||
--restart=unless-stopped \
|
||
--privileged \
|
||
--user root \
|
||
-p "$PORT_MAPPING" \
|
||
-v "$DATA_DIR:/opt/openlist/data" \
|
||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||
-e PUID=0 \
|
||
-e PGID=0 \
|
||
-e UMASK=000 \
|
||
-e TZ=Asia/Shanghai \
|
||
"$IMAGE_NAME"; then
|
||
green "✅ OpenList 容器部署成功"
|
||
else
|
||
error_exit "OpenList 容器启动失败"
|
||
fi
|
||
}
|
||
|
||
# === 新增功能:密码检测和自动重置 ===
|
||
# 生成随机密码
|
||
generate_random_password() {
|
||
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 12
|
||
echo
|
||
}
|
||
|
||
# 检测并获取密码
|
||
detect_and_get_password() {
|
||
blue "🔍 检测密码生成情况..."
|
||
|
||
local max_attempts=10
|
||
local attempt=1
|
||
local password=""
|
||
|
||
while [ $attempt -le $max_attempts ]; do
|
||
yellow "尝试检测密码 ($attempt/$max_attempts)..."
|
||
sleep 3
|
||
|
||
# 检查容器日志获取密码
|
||
local logs=$(docker logs "$CONTAINER_NAME" 2>&1 | tail -10)
|
||
|
||
# 尝试提取密码
|
||
password=$(echo "$logs" | grep -o "initial password is: [[:alnum:]]*" | awk '{print $4}' | tail -1)
|
||
|
||
if [ -n "$password" ]; then
|
||
green "✅ 检测到自动生成的密码"
|
||
echo "$password"
|
||
return 0
|
||
fi
|
||
|
||
# 检查是否启动成功但没有新密码(使用现有配置)
|
||
if echo "$logs" | grep -q "HTTP server"; then
|
||
yellow "⚠️ 使用现有配置,无新密码生成"
|
||
# 返回空字符串表示使用现有配置
|
||
echo ""
|
||
return 0
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
done
|
||
|
||
# 如果检测不到密码,生成随机密码并重置
|
||
yellow "⚠️ 未检测到密码生成,执行自动重置..."
|
||
auto_reset_password
|
||
}
|
||
|
||
# 自动重置密码
|
||
auto_reset_password() {
|
||
blue "🔄 自动重置密码..."
|
||
|
||
# 停止容器
|
||
docker stop "$CONTAINER_NAME" 2>/dev/null || true
|
||
|
||
# 删除配置文件以触发新密码生成
|
||
yellow "删除现有配置..."
|
||
sudo rm -f "$DATA_DIR/config.json" 2>/dev/null || true
|
||
sudo rm -f "$DATA_DIR"/*.db 2>/dev/null || true
|
||
|
||
# 重启容器
|
||
yellow "重启容器生成新密码..."
|
||
docker start "$CONTAINER_NAME" 2>/dev/null || {
|
||
yellow "容器启动失败,重新部署..."
|
||
deploy_openlist
|
||
}
|
||
|
||
# 等待并再次检测密码
|
||
sleep 8
|
||
detect_and_get_password
|
||
}
|
||
|
||
# 验证部署状态
|
||
verify_deployment() {
|
||
blue "🔍 验证容器运行状态..."
|
||
|
||
local max_attempts=15
|
||
local attempt=1
|
||
|
||
while [ $attempt -le $max_attempts ]; do
|
||
yellow "⏳ 等待容器启动 ($attempt/$max_attempts)..."
|
||
sleep 3
|
||
|
||
# 检查容器状态
|
||
if docker ps --filter "name=$CONTAINER_NAME" --filter "status=running" --format "{{.Names}}" | grep -q "$CONTAINER_NAME"; then
|
||
green "✅ 容器正在运行"
|
||
|
||
# 检测并获取密码
|
||
PASSWORD=$(detect_and_get_password)
|
||
|
||
return 0
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
done
|
||
|
||
red "❌ 容器启动超时"
|
||
blue "📋 容器日志:"
|
||
docker logs "$CONTAINER_NAME" | tail -20
|
||
error_exit "容器启动失败,请检查上方日志"
|
||
}
|
||
|
||
# 显示部署成功信息
|
||
show_success_info() {
|
||
local port=$(echo "$PORT_MAPPING" | cut -d':' -f1)
|
||
local ip_address=$(hostname -I | awk '{print $1}' | head -n1)
|
||
|
||
echo ""
|
||
green "🎉 OpenList 部署成功!"
|
||
echo "=========================================="
|
||
blue "📋 访问信息:"
|
||
echo " 🌐 访问地址:http://$ip_address:$port"
|
||
echo " 🏠 本地访问:http://localhost:$port"
|
||
echo " 📁 数据目录:$DATA_DIR"
|
||
echo ""
|
||
blue "🔐 登录信息:"
|
||
echo " 用户名:admin"
|
||
|
||
if [ -n "$PASSWORD" ]; then
|
||
echo " 密码:$PASSWORD"
|
||
yellow "💡 首次登录后请立即修改密码!"
|
||
else
|
||
echo " 密码:使用之前设置的密码"
|
||
echo " 🔄 如需重置:删除 $DATA_DIR/config.json 并重启容器"
|
||
fi
|
||
|
||
echo ""
|
||
blue "⚡ 常用命令:"
|
||
echo " 📜 查看日志:docker logs $CONTAINER_NAME"
|
||
echo " 🔄 重启容器:docker restart $CONTAINER_NAME"
|
||
echo " ⏹️ 停止容器:docker stop $CONTAINER_NAME"
|
||
echo " 🚀 启动容器:docker start $CONTAINER_NAME"
|
||
}
|
||
|
||
# 主执行流程
|
||
main() {
|
||
echo "=========================================="
|
||
green " OpenList Docker 一键部署脚本"
|
||
yellow " (带自动密码检测功能)"
|
||
echo "=========================================="
|
||
|
||
check_docker
|
||
prepare_directory
|
||
cleanup_existing_container
|
||
pull_openlist_image
|
||
deploy_openlist
|
||
verify_deployment
|
||
show_success_info
|
||
|
||
echo ""
|
||
green "✅ 部署流程完成!"
|
||
}
|
||
|
||
# 执行主函数(捕获中断信号)
|
||
trap 'echo; red "⚠️ 操作被用户中断"; exit 1' INT
|
||
main "$@"
|