mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-06-25 17:23:59 +08:00
feat(setup): improve installer UX and defaults
This commit is contained in:
223
setup/foxel.sh
223
setup/foxel.sh
@@ -1,31 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
#================================================================================
|
||||
# Foxel 一键部署与更新脚本
|
||||
#
|
||||
# 作者: maxage
|
||||
# 版本: 1.7 (增加下载镜像, 解决网络问题)
|
||||
# 描述: 此脚本用于自动化安装、配置和管理 Foxel 项目 (使用 Docker Compose)。
|
||||
# - 智能检测现有安装,提供安装向导和管理菜单两种模式。
|
||||
# - 自动检测并安装依赖。
|
||||
# - 为国内用户提供镜像源切换选项。
|
||||
#
|
||||
# 一键运行命令:
|
||||
# Foxel 一键安装与管理脚本(Docker Compose)
|
||||
# 一键运行:
|
||||
# bash <(curl -sL "https://raw.githubusercontent.com/DrizzleTime/Foxel/main/setup/foxel.sh?_=$(date +%s)")
|
||||
#================================================================================
|
||||
#
|
||||
|
||||
# --- 消息打印函数 ---
|
||||
info() {
|
||||
echo "[信息] $1"
|
||||
}
|
||||
# --- 输出(可关闭颜色:NO_COLOR=1) ---
|
||||
if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then
|
||||
C_RESET='\033[0m'
|
||||
C_RED='\033[31m'
|
||||
C_GREEN='\033[32m'
|
||||
C_YELLOW='\033[33m'
|
||||
C_BLUE='\033[34m'
|
||||
else
|
||||
C_RESET=''
|
||||
C_RED=''
|
||||
C_GREEN=''
|
||||
C_YELLOW=''
|
||||
C_BLUE=''
|
||||
fi
|
||||
|
||||
warn() {
|
||||
echo "[警告] $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo "[错误] $1"
|
||||
}
|
||||
info() { printf "%b[信息]%b %s\n" "$C_BLUE" "$C_RESET" "$*"; }
|
||||
success() { printf "%b[成功]%b %s\n" "$C_GREEN" "$C_RESET" "$*"; }
|
||||
warn() { printf "%b[警告]%b %s\n" "$C_YELLOW" "$C_RESET" "$*"; }
|
||||
error() { printf "%b[错误]%b %s\n" "$C_RED" "$C_RESET" "$*"; }
|
||||
|
||||
# --- 基础函数 ---
|
||||
command_exists() {
|
||||
@@ -34,16 +33,33 @@ command_exists() {
|
||||
|
||||
confirm_action() {
|
||||
local prompt_message="$1"
|
||||
printf "%s" "${prompt_message} (y/n): "
|
||||
read confirmation
|
||||
if [[ "$confirmation" =~ ^[Yy]$ ]]; then
|
||||
return 0 # Yes
|
||||
local default="${2:-N}"
|
||||
local hint='[y/N]'
|
||||
local confirmation
|
||||
|
||||
if [[ "$default" =~ ^[Yy]$ ]]; then
|
||||
default="Y"
|
||||
hint='[Y/n]'
|
||||
else
|
||||
return 1 # No
|
||||
default="N"
|
||||
hint='[y/N]'
|
||||
fi
|
||||
|
||||
while true; do
|
||||
read -r -p "${prompt_message} ${hint}: " confirmation
|
||||
if [[ -z "$confirmation" ]]; then
|
||||
[[ "$default" == "Y" ]] && return 0 || return 1
|
||||
fi
|
||||
|
||||
case "$confirmation" in
|
||||
[Yy]|[Yy][Ee][Ss]) return 0 ;;
|
||||
[Nn]|[Nn][Oo]) return 1 ;;
|
||||
*) warn "请输入 y 或 n。" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# --- IP地址检测函数 (只输出IP) ---
|
||||
# --- IP地址检测函数(只输出IP) ---
|
||||
get_public_ipv4() {
|
||||
curl -4 -s --max-time 2 https://api.ipify.org || \
|
||||
curl -4 -s --max-time 2 https://ifconfig.me/ip || \
|
||||
@@ -65,7 +81,7 @@ get_private_ip() {
|
||||
|
||||
# --- 依赖与环境检查 ---
|
||||
check_and_install_dependencies() {
|
||||
info "正在检查所需依赖..."
|
||||
info "检查依赖..."
|
||||
declare -A deps=( [curl]="curl" [openssl]="openssl" [ss]="iproute2" )
|
||||
local missing_deps=()
|
||||
for cmd in "${!deps[@]}"; do
|
||||
@@ -75,8 +91,8 @@ check_and_install_dependencies() {
|
||||
done
|
||||
|
||||
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||||
warn "检测到以下依赖项缺失: ${missing_deps[*]}"
|
||||
if confirm_action "是否尝试自动安装它们?"; then
|
||||
warn "缺少依赖: ${missing_deps[*]}"
|
||||
if confirm_action "是否尝试自动安装?" "Y"; then
|
||||
local pm_cmd=""
|
||||
if command_exists apt-get; then pm_cmd="sudo apt-get update && sudo apt-get install -y";
|
||||
elif command_exists yum; then pm_cmd="sudo yum install -y";
|
||||
@@ -87,12 +103,12 @@ check_and_install_dependencies() {
|
||||
for cmd in "${!deps[@]}"; do
|
||||
if ! command_exists "$cmd"; then error "依赖 '${deps[$cmd]}' 自动安装失败。"; exit 1; fi
|
||||
done
|
||||
info "依赖已成功安装。"
|
||||
success "依赖安装完成。"
|
||||
else
|
||||
error "用户取消了安装。请先手动安装依赖: ${missing_deps[*]}"; exit 1
|
||||
fi
|
||||
else
|
||||
info "所有基础依赖均已满足。"
|
||||
success "依赖已满足。"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -101,64 +117,107 @@ initialize_environment() {
|
||||
if ! command_exists docker; then
|
||||
error "未找到 Docker。请参照官方文档安装: https://docs.docker.com/engine/install/"; exit 1;
|
||||
fi
|
||||
if ! docker info &> /dev/null; then error "Docker deamon 未在运行。请先启动 Docker。"; exit 1; fi
|
||||
info "Docker 环境检测通过。"
|
||||
if ! docker info &> /dev/null; then error "Docker daemon 未在运行。请先启动 Docker。"; exit 1; fi
|
||||
success "Docker 环境正常。"
|
||||
|
||||
if command_exists docker-compose; then COMPOSE_CMD="docker-compose";
|
||||
elif docker compose version &> /dev/null; then COMPOSE_CMD="docker compose";
|
||||
else error "未找到 Docker Compose。请安装 Docker Compose v1 或 v2。"; exit 1; fi
|
||||
info "检测到 Docker Compose 命令: $COMPOSE_CMD"
|
||||
info "Docker Compose: $COMPOSE_CMD"
|
||||
}
|
||||
|
||||
set_image_source_official() {
|
||||
sed -i -E 's|^([[:space:]]*)#?image:[[:space:]]*ghcr\.io/drizzletime/foxel:latest|\1image: ghcr.io/drizzletime/foxel:latest|' compose.yaml
|
||||
sed -i -E 's|^([[:space:]]*)#?image:[[:space:]]*ghcr\.nju\.edu\.cn/drizzletime/foxel:latest|\1#image: ghcr.nju.edu.cn/drizzletime/foxel:latest|' compose.yaml
|
||||
}
|
||||
|
||||
set_image_source_mirror() {
|
||||
sed -i -E 's|^([[:space:]]*)#?image:[[:space:]]*ghcr\.io/drizzletime/foxel:latest|\1#image: ghcr.io/drizzletime/foxel:latest|' compose.yaml
|
||||
sed -i -E 's|^([[:space:]]*)#?image:[[:space:]]*ghcr\.nju\.edu\.cn/drizzletime/foxel:latest|\1image: ghcr.nju.edu.cn/drizzletime/foxel:latest|' compose.yaml
|
||||
}
|
||||
|
||||
choose_image_source() {
|
||||
echo
|
||||
info "请选择镜像源:"
|
||||
echo "1) ghcr.io (默认)"
|
||||
echo "2) ghcr.nju.edu.cn (国内)"
|
||||
local image_choice
|
||||
read -r -p "请选择 [1-2] (默认 1): " image_choice
|
||||
image_choice="${image_choice:-1}"
|
||||
|
||||
case "$image_choice" in
|
||||
1)
|
||||
set_image_source_official
|
||||
info "已选择: ghcr.io"
|
||||
;;
|
||||
2)
|
||||
set_image_source_mirror
|
||||
info "已选择: ghcr.nju.edu.cn"
|
||||
;;
|
||||
*)
|
||||
warn "无效选择,使用默认 ghcr.io"
|
||||
set_image_source_official
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- 新安装流程 ---
|
||||
install_new_foxel() {
|
||||
info "--- 开始 Foxel 全新安装 ---"
|
||||
local install_path
|
||||
info "开始全新安装..."
|
||||
local foxel_dir
|
||||
local default_dir="/opt/foxel"
|
||||
|
||||
while true; do
|
||||
read -p "请输入您想在哪里创建 Foxel 的数据目录 (例如: /opt/docker): " install_path
|
||||
if [[ -z "$install_path" ]]; then warn "输入不能为空,请重新输入。"; continue; fi
|
||||
if [ ! -d "$install_path" ]; then
|
||||
if confirm_action "目录 '$install_path' 不存在。您想现在创建它吗?"; then
|
||||
mkdir -p "$install_path"
|
||||
if [ $? -eq 0 ]; then info "目录 '$install_path' 创建成功。"; break;
|
||||
else error "创建目录 '$install_path' 失败。"; fi
|
||||
else info "操作已取消。"; fi
|
||||
else info "将使用已存在的目录 '$install_path'。"; break; fi
|
||||
read -r -p "请输入 Foxel 安装目录 (默认: ${default_dir}): " foxel_dir
|
||||
foxel_dir="${foxel_dir:-$default_dir}"
|
||||
|
||||
if [[ -f "$foxel_dir/compose.yaml" ]]; then
|
||||
warn "检测到已存在: $foxel_dir/compose.yaml"
|
||||
if confirm_action "是否覆盖它?" "N"; then
|
||||
mv "$foxel_dir/compose.yaml" "$foxel_dir/compose.yaml.bak.$(date +%s)"
|
||||
info "已备份为: $foxel_dir/compose.yaml.bak.*"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -d "$foxel_dir" ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
if confirm_action "目录不存在,是否创建?" "Y"; then
|
||||
if mkdir -p "$foxel_dir"; then
|
||||
break
|
||||
fi
|
||||
error "创建目录失败: $foxel_dir"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
|
||||
local foxel_dir="$install_path/Foxel"
|
||||
info "将在 '$foxel_dir' 目录中创建所需文件..."
|
||||
info "准备目录: $foxel_dir"
|
||||
mkdir -p "$foxel_dir/data/"{db,mount} && chmod 777 "$foxel_dir/data/"{db,mount}
|
||||
if [ $? -ne 0 ]; then error "创建或设置子目录权限失败。"; exit 1; fi
|
||||
cd "$foxel_dir" || exit
|
||||
|
||||
info "正在下载 'compose.yaml'..."
|
||||
info "下载 compose.yaml..."
|
||||
local COMPOSE_MIRROR_URL="https://ghproxy.com/https://raw.githubusercontent.com/DrizzleTime/Foxel/main/compose.yaml"
|
||||
local COMPOSE_OFFICIAL_URL="https://raw.githubusercontent.com/DrizzleTime/Foxel/main/compose.yaml"
|
||||
|
||||
if ! curl -L -o compose.yaml "$COMPOSE_MIRROR_URL"; then
|
||||
warn "镜像源下载失败,正在尝试从官方源下载..."
|
||||
if ! curl -L -o compose.yaml "$COMPOSE_OFFICIAL_URL"; then
|
||||
if ! curl -fsSL -o compose.yaml "$COMPOSE_MIRROR_URL"; then
|
||||
warn "镜像下载失败,尝试官方源..."
|
||||
if ! curl -fsSL -o compose.yaml "$COMPOSE_OFFICIAL_URL"; then
|
||||
error "下载 'compose.yaml' 失败。请检查您的网络连接。"; exit 1;
|
||||
fi
|
||||
fi
|
||||
info "'compose.yaml' 下载成功。"
|
||||
success "compose.yaml 下载成功。"
|
||||
echo
|
||||
|
||||
if confirm_action "您的服务器是否位于中国大陆(以便为您选择更快的镜像源)?"; then
|
||||
info "正在切换到国内镜像源..."
|
||||
sed -i 's|^\( *\)image: ghcr.io/drizzletime/foxel:latest|\1#image: ghcr.io/drizzletime/foxel:latest|' compose.yaml
|
||||
sed -i 's|^\( *\)#image: ghcr.nju.edu.cn/drizzletime/foxel:latest|\1image: ghcr.nju.edu.cn/drizzletime/foxel:latest|' compose.yaml
|
||||
info "已成功切换到 ghcr.nju.edu.cn 镜像源。"
|
||||
else
|
||||
info "将使用默认的 ghcr.io 官方镜像源。"
|
||||
fi
|
||||
choose_image_source
|
||||
echo
|
||||
|
||||
local new_port
|
||||
while true; do
|
||||
read -p "请输入新的对外端口 (或直接按回车使用默认的 8088): " new_port
|
||||
read -r -p "请输入对外端口 (默认 8088): " new_port
|
||||
if [[ -z "$new_port" ]]; then
|
||||
new_port="8088"
|
||||
info "将使用默认端口 8088。"
|
||||
@@ -173,30 +232,29 @@ install_new_foxel() {
|
||||
if ss -tuln | grep -q ":${new_port}\b"; then
|
||||
warn "端口 $new_port 已被占用,请换一个。"
|
||||
else
|
||||
sed -i "s/\"8088:80\"/\"$new_port:80\"/" compose.yaml
|
||||
sed -i -E "s|\"[0-9]{1,5}:80\"|\"$new_port:80\"|" compose.yaml
|
||||
info "端口已成功修改为 $new_port。"
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo
|
||||
|
||||
if ! confirm_action "是否需要生成新的随机密钥 (推荐)?(选择 'n' 将使用默认值)"; then
|
||||
if ! confirm_action "是否生成新的随机密钥(推荐)?" "Y"; then
|
||||
info "将使用 'compose.yaml' 文件中的默认密钥。"
|
||||
else
|
||||
info "正在生成新的随机密钥..."
|
||||
sed -i "s|SECRET_KEY=.*|SECRET_KEY=$(openssl rand -base64 32)|" compose.yaml
|
||||
sed -i "s|TEMP_LINK_SECRET_KEY=.*|TEMP_LINK_SECRET_KEY=$(openssl rand -base64 32)|" compose.yaml
|
||||
info "新的密钥已成功生成并替换。"
|
||||
success "新的密钥已写入 compose.yaml。"
|
||||
fi
|
||||
echo
|
||||
|
||||
if confirm_action "所有配置已准备就绪!您想现在启动 Foxel 项目吗?"; then
|
||||
info "正在启动 Foxel 服务... 这可能需要一些时间来拉取镜像。"
|
||||
if confirm_action "配置完成,是否现在启动 Foxel?" "Y"; then
|
||||
info "启动中(首次会拉取镜像,可能需要几分钟)..."
|
||||
$COMPOSE_CMD pull && $COMPOSE_CMD up -d
|
||||
if [ $? -eq 0 ]; then
|
||||
info "Foxel 部署成功!"
|
||||
info "-------------------------------------------------"
|
||||
info "正在检测服务器IP地址,请稍候..."
|
||||
success "Foxel 已启动。"
|
||||
info "正在检测访问地址..."
|
||||
|
||||
# 先捕获所有IP地址
|
||||
local public_ipv4=$(get_public_ipv4 2>/dev/null)
|
||||
@@ -206,7 +264,7 @@ install_new_foxel() {
|
||||
local ip_found=false
|
||||
|
||||
echo
|
||||
info "部署完成!您可以通过以下地址访问 Foxel:"
|
||||
info "访问地址:"
|
||||
|
||||
if [[ -n "$private_ip" ]]; then
|
||||
echo " - 局域网地址: http://${private_ip}:${final_port}"
|
||||
@@ -226,12 +284,16 @@ install_new_foxel() {
|
||||
warn "未能自动检测到服务器IP地址。"
|
||||
echo " 请手动使用 http://[您的服务器IP]:${final_port} 访问它。"
|
||||
fi
|
||||
echo "-------------------------------------------------"
|
||||
echo
|
||||
info "常用命令:"
|
||||
echo " - 启动/更新: cd $foxel_dir && $COMPOSE_CMD up -d"
|
||||
echo " - 停止: cd $foxel_dir && $COMPOSE_CMD stop"
|
||||
echo " - 日志: cd $foxel_dir && $COMPOSE_CMD logs -f"
|
||||
else
|
||||
error "启动 Foxel 失败。请运行 'cd $foxel_dir && $COMPOSE_CMD logs' 查看日志。"
|
||||
fi
|
||||
else
|
||||
info "操作已取消。您可以稍后进入 '$foxel_dir' 并手动运行 '$COMPOSE_CMD up -d'。"
|
||||
info "已跳过启动。稍后可运行:cd $foxel_dir && $COMPOSE_CMD up -d"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -291,7 +353,7 @@ manage_existing_installation() {
|
||||
case $choice in
|
||||
1) # 更新
|
||||
warn "更新前,强烈建议您备份 '$foxel_dir/data' 目录!"
|
||||
if confirm_action "您确定要继续更新吗?"; then
|
||||
if confirm_action "确认继续更新?" "Y"; then
|
||||
info "正在拉取最新镜像..."
|
||||
$COMPOSE_CMD pull
|
||||
info "正在使用新镜像重新部署..."
|
||||
@@ -302,14 +364,14 @@ manage_existing_installation() {
|
||||
2) # 卸载
|
||||
warn "这将停止并删除 Foxel 容器及相关网络!"
|
||||
warn "强烈建议您先备份 '$foxel_dir/data' 目录!"
|
||||
if confirm_action "您确定要继续卸载吗?"; then
|
||||
if confirm_action "确认继续卸载?" "N"; then
|
||||
info "正在停止并移除容器..."
|
||||
$COMPOSE_CMD down
|
||||
if confirm_action "是否要删除所有数据卷(这将删除数据库等所有数据)?"; then
|
||||
if confirm_action "是否删除所有数据卷(会删除数据库等数据)?" "N"; then
|
||||
$COMPOSE_CMD down -v
|
||||
info "数据卷已删除。"
|
||||
fi
|
||||
if confirm_action "是否要删除整个 Foxel 安装目录 '$foxel_dir'?"; then
|
||||
if confirm_action "是否删除 Foxel 安装目录 '$foxel_dir'?" "N"; then
|
||||
rm -rf "$foxel_dir"
|
||||
info "安装目录已删除。"
|
||||
fi
|
||||
@@ -320,7 +382,7 @@ manage_existing_installation() {
|
||||
3) # 重新安装
|
||||
warn "重新安装将完全删除当前的 Foxel 实例(包括数据),然后进入全新安装流程。"
|
||||
warn "在继续之前,请务必备份好您的重要数据!"
|
||||
if confirm_action "您确定要重新安装吗?"; then
|
||||
if confirm_action "确认继续重新安装?" "N"; then
|
||||
info "正在执行卸载..."
|
||||
$COMPOSE_CMD down -v && rm -rf "$foxel_dir"
|
||||
info "旧实例已彻底移除。"
|
||||
@@ -344,9 +406,8 @@ manage_existing_installation() {
|
||||
# --- 主函数 ---
|
||||
main() {
|
||||
clear
|
||||
local SCRIPT_VERSION="1.7"
|
||||
echo "================================================="
|
||||
info "欢迎使用 Foxel 一键安装与管理脚本 (版本: ${SCRIPT_VERSION})"
|
||||
info "欢迎使用 Foxel 一键安装与管理脚本"
|
||||
echo "================================================="
|
||||
echo
|
||||
|
||||
|
||||
Reference in New Issue
Block a user