feat: align with upstream 1.5.9.9

This commit is contained in:
2026-09-12 18:21:11 +08:00
commit 34f0679117
805 changed files with 376275 additions and 0 deletions
+362
View File
@@ -0,0 +1,362 @@
#!/bin/bash
set -euo pipefail
# ==================== 配置常量 ====================
COMPOSE_DIR="/opt/voicehub"
COMPOSE_FILE="${COMPOSE_DIR}/docker-compose.yml"
# 数据库固定账号库名(可自行修改)
PG_USER="user"
PG_DB_NAME="voicehub"
GHCR_HOST="ghcr.nju.edu.cn"
# 镜像地址常量
VOICEHUB_IMAGE="ghcr.nju.edu.cn/laoshuikaixue/voicehub:latest"
POSTGRES_IMAGE="ghcr.nju.edu.cn/laoshuikaixue/voicehub-postgres:latest"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# ==================== root 提权 ====================
if [ "$(id -u)" -ne 0 ]; then
echo -e "权限:root 用户:${RED}非 root${NC}"
exit 1
# exec sudo "$0" "$@"
else
echo -e "权限:root 用户:${GREEN}正常${NC}"
fi
# ==================== 系统信息展示 ====================
OS_NAME=$(grep -oP 'PRETTY_NAME="\K[^"]+' /etc/os-release)
echo -e "兼容性:操作系统:${GREEN}${OS_NAME}${NC}"
# 检查架构
valid_archs=("x86_64" "armv6l" "armv7l" "aarch64" "ppc64le" "s390x")
current_arch=$(uname -m)
found=false
for arch in "${valid_archs[@]}"; do
if [ "$arch" = "$current_arch" ]; then
found=true
break
fi
done
if $found; then
echo -e "兼容性:系统架构:${GREEN}${current_arch}(符合要求)${NC}"
else
echo -e "兼容性:系统架构:${RED}${current_arch}(不支持)${NC},${RED}支持的架构:${valid_archs[*]}${NC}"
exit 1
fi
# ==================== 检查依赖 ====================
# 检查 docker
if command -v docker &> /dev/null; then
echo -e "依赖:docker:${GREEN}已安装${NC}"
else
echo -e "依赖:docker:${RED}未安装,请先安装 Docker${NC}"
exit 1
fi
# 检查 docker-compose / docker compose
DOCKER_COMPOSE_CMD=""
if command -v docker-compose &> /dev/null; then
DOCKER_COMPOSE_CMD="docker-compose"
echo -e "依赖:docker-compose:${GREEN}已安装${NC}"
elif docker compose version &> /dev/null; then
DOCKER_COMPOSE_CMD="docker compose"
echo -e "依赖:docker compose:${GREEN}已安装${NC}"
else
echo -e "依赖:docker-compose:${RED}未安装${NC}"
exit 1
fi
# ==================== 随机密钥生成逻辑:openssl优先,无则bash内置降级 ====================
gen_bash_random() {
local length="$1"
local charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
local str=""
for ((i=0; i<length; i++)); do
str+=${charset:$(( RANDOM % ${#charset} )):1}
done
echo "$str"
}
# 判定openssl可用性
if command -v openssl &> /dev/null; then
echo -e "依赖:openssl:${GREEN}已安装${NC}"
gen_openssl() {
local LEN="$1"
openssl rand -base64 $((LEN*2)) | tr -dc 'A-Za-z0-9' | head -c "$LEN"
echo
}
JWT_SECRET=$(gen_openssl 64)
PG_PASSWORD=$(gen_openssl 64)
else
echo -e "${YELLOW}警告:未找到openssl,降级使用Bash内置RANDOM生成${NC}"
JWT_SECRET=$(gen_bash_random 64)
PG_PASSWORD=$(gen_bash_random 64)
fi
# ==================== 检查网络 ====================
echo -e "网络:${GHCR_HOST} ...${NC}"
if ping -c 4 -W 5 "${GHCR_HOST}" >/dev/null 2>&1; then
echo -e "网络:${GHCR_HOST} ${GREEN}连通正常${NC}"
else
echo -e "网络:${GHCR_HOST} ${RED}失败,网络无法连通该镜像仓库,退出执行${NC}"
exit 1
fi
# ==================== 生成配置 ====================
# 创建工作目录
if [ ! -d "${COMPOSE_DIR}" ]; then
echo -e "${YELLOW}未检测到工作目录,正在创建:${COMPOSE_DIR}${NC}"
mkdir -p "${COMPOSE_DIR}"
chmod 755 "${COMPOSE_DIR}"
else
echo -e "环境:工作目录:${GREEN}已存在${NC}"
fi
# 生成 compose 文件模板
generate_compose_template() {
cat > "${COMPOSE_FILE}" <<'EOF'
services:
voicehub:
image: ghcr.nju.edu.cn/laoshuikaixue/voicehub:latest
ports:
- '3000:3000'
environment:
- DATABASE_URL=postgresql://user:PG_PWD_PLACEHOLDER@postgres:5432/voicehub?sslmode=disable
- JWT_SECRET=JWT_SECRET_PLACEHOLDER
- NODE_ENV=production
- SKIP_BUILD=true
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
postgres:
image: ghcr.nju.edu.cn/laoshuikaixue/voicehub-postgres:latest
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=PG_PWD_PLACEHOLDER
- POSTGRES_DB=voicehub
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U user -d voicehub']
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
postgres_data:
EOF
}
# 生成配置文件
generate_config() {
echo -e "${YELLOW}正在生成全新配置文件...${NC}"
generate_compose_template
# sed 批量替换占位符
sed -i "s|PG_PWD_PLACEHOLDER|${PG_PASSWORD}|g" "${COMPOSE_FILE}"
sed -i "s|JWT_SECRET_PLACEHOLDER|${JWT_SECRET}|g" "${COMPOSE_FILE}"
echo -e "环境:配置文件:${GREEN}已生成并填充密钥${NC}"
echo -e " JWT_SECRET:${BLUE}${JWT_SECRET}${NC}"
echo -e " 数据库密码:${BLUE}${PG_PASSWORD}${NC}"
}
# ==================== 服务控制函数 ====================
start_service() {
echo -e "\n${GREEN}=== 正在启动 VoiceHub 服务 ===${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} up -d
}
stop_service() {
echo -e "\n${RED}=== 正在停止 VoiceHub 服务 ===${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} down
}
restart_service() {
echo -e "\n${YELLOW}=== 正在重启 VoiceHub 服务 ===${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} restart
}
status_service() {
echo -e "\n${BLUE}=== 当前服务状态 ===${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} ps
}
# ==================== 检查/更新镜像函数(基于镜像ID对比) ====================
check_and_update_image() {
local IMAGE_FULL="$1"
local IMAGE_NAME="$2"
# 1. 本地无镜像 → 直接拉取
if [ -z "$(docker images -q "${IMAGE_FULL}")" ]; then
echo -e "镜像版本:${YELLOW}${IMAGE_NAME}本地无镜像,开始拉取最新版${NC}"
if docker pull "${IMAGE_FULL}"; then
echo -e "镜像版本:${GREEN}${IMAGE_NAME}拉取成功${NC}"
return 0
else
echo -e "镜像版本:${RED}${IMAGE_NAME}拉取失败${NC}"
return 1
fi
fi
# 2. 执行拉取并判断状态
echo -e "镜像版本:${YELLOW}检测${IMAGE_NAME}更新状态${NC}"
local PULL_OUTPUT
PULL_OUTPUT=$(docker pull "${IMAGE_FULL}" 2>&1)
if [ $? -ne 0 ]; then
echo -e "镜像版本:${RED}${IMAGE_NAME}拉取失败:${PULL_OUTPUT}${NC}"
return 1
fi
if echo "${PULL_OUTPUT}" | grep -q "Image is up to date"; then
echo -e "镜像版本:${GREEN}${IMAGE_NAME}已是最新${NC}"
return 0
else
echo -e "镜像版本:${YELLOW}${IMAGE_NAME}已更新至新版本${NC}"
return 0
fi
}
# ==================== 交互式初始化函数(仅重新部署使用) ====================
run_interactive_init() {
echo -e "\n${BLUE}=== 进入交互式初始化环境 ===${NC}"
echo -e "${YELLOW}提示:进入容器后执行可能需要按回车${NC}"
cd "${COMPOSE_DIR}"
# 临时启动容器进入bash,退出自动删除
${DOCKER_COMPOSE_CMD} run --rm voicehub pnpm run init-help
}
# ==================== 1.重新部署(清空数据卷 + 执行初始化) ====================
redeploy_service() {
echo -e "\n${RED}=================================================="
echo -e "警告:重新部署会清空所有数据库数据!"
echo -e "==================================================${NC}"
read -p "确定继续?(y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}已取消重新部署${NC}"
return
fi
# 停止服务
echo -e "${YELLOW}停止旧的 voicehub (若有)${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} down 2>/dev/null || true
sleep 2
# 检查并更新两个镜像
echo -e "\n${BLUE}=== 开始检查并更新镜像 ===${NC}"
if ! check_and_update_image "${VOICEHUB_IMAGE}" "VoiceHub主程序"; then
echo -e "${RED}镜像更新失败,终止重新部署${NC}"
return 1
fi
if ! check_and_update_image "${POSTGRES_IMAGE}" "PostgreSQL数据库"; then
echo -e "${RED}镜像更新失败,终止重新部署${NC}"
return 1
fi
# 删除旧配置
if [ -f "${COMPOSE_FILE}" ]; then
echo -e "${YELLOW}删除旧 docker-compose.yml...${NC}"
rm -f "${COMPOSE_FILE}"
fi
# 删除数据库数据卷(清空数据)
echo -e "${YELLOW}删除数据库数据卷 voicehub_postgres_data...${NC}"
docker volume rm voicehub_postgres_data 2>/dev/null || true
# 生成新配置
generate_config
# 启动整套服务
echo -e "\n${YELLOW}启动数据库服务...${NC}"
start_service
sleep 10
# 执行交互式初始化
run_interactive_init
# 重启正式业务容器
echo -e "\n${YELLOW}重启正式业务容器...${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} restart
echo -e "\n${GREEN}✅ 重新部署完成(数据已清空,已执行初始化)${NC}"
status_service
}
# ==================== 2.版本更新(保留原有数据卷,不执行初始化) ====================
update_version() {
echo -e "\n${YELLOW}=================================================="
echo -e "说明:版本更新【保留原有数据库数据,不执行初始化】"
echo -e "==================================================${NC}"
read -p "确定继续更新版本?(y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}已取消版本更新${NC}"
return
fi
# 停止服务(不删数据卷)
echo -e "${YELLOW}停止当前服务${NC}"
cd "${COMPOSE_DIR}" && ${DOCKER_COMPOSE_CMD} down 2>/dev/null || true
sleep 2
# 检查并更新镜像
echo -e "\n${BLUE}=== 检查并拉取最新镜像 ===${NC}"
if ! check_and_update_image "${VOICEHUB_IMAGE}" "VoiceHub主程序"; then
echo -e "${RED}镜像更新失败,终止版本升级${NC}"
return 1
fi
if ! check_and_update_image "${POSTGRES_IMAGE}" "PostgreSQL数据库"; then
echo -e "${RED}镜像更新失败,终止版本升级${NC}"
return 1
fi
# 直接启动服务,沿用旧数据、旧配置,跳过初始化
echo -e "${YELLOW}使用现有配置与数据库,直接启动服务${NC}"
start_service
echo -e "\n${YELLOW}等待容器启动完成(10秒)...${NC}"
sleep 10
echo -e "\n${GREEN}✅ 版本更新完成(已保留原有数据库数据)${NC}"
status_service
}
# ==================== 主菜单 ====================
show_menu() {
echo -e "\n========================================"
echo -e " VoiceHub 管理菜单"
echo -e "========================================"
echo -e " 1. 重新部署(清空数据,全新安装+初始化)"
echo -e " 2. 版本更新(保留旧数据,仅升级镜像)"
echo -e " 3. 查看服务状态"
echo -e " 0. 退出脚本"
echo -e "========================================"
echo -n "请输入数字选择操作:"
}
# 循环菜单
while true; do
show_menu
read -r choice
case $choice in
1) redeploy_service ;;
2) update_version ;;
3) status_service ;;
0) echo -e "\n${GREEN}退出脚本${NC}"; exit 0 ;;
*) echo -e "\n${RED}输入错误,请输入有效数字!${NC}" ;;
esac
done