├── LICENSE ├── README.md ├── help.md ├── install.sh └── vpsai.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Protomyst 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VPSAI - 开源AI服务快速部署工具 2 | 3 |

4 | 轻松部署和管理各类AI服务的自动化脚本工具 5 |

6 | 7 |
8 | 9 | ![Version](https://img.shields.io/badge/version-0.0.1-blue) 10 | ![License](https://img.shields.io/badge/license-MIT-green) 11 | ![Docker](https://img.shields.io/badge/docker-required-blue) 12 | 13 |
14 | 15 | ## ✨ 特性 16 | 17 | - 🚀 一键部署多种流行AI服务 18 | - 🔧 自动配置运行环境(Docker/Nginx) 19 | - 🔐 支持HTTPS和证书自动配置 20 | - 💾 数据持久化和备份方案 21 | - 🔄 支持服务状态监控和自动更新 22 | 23 | ## 📦 支持的服务 24 | 25 | ### API网关 26 | | 服务名 | 默认端口 | 说明 | 27 | |--------|----------|------| 28 | | OneAPI | 3000 | 新一代API管理平台 | 29 | | NewAPI | 4000 | OneAPI二开 | 30 | | VoAPI | 5000 | NewAPI二开(仅x86,闭源) | 31 | 32 | ### Chat前端 33 | | 服务名 | 默认端口 | 说明 | 34 | |--------|----------|------| 35 | | Open-WebUI | 6001 | 功能强大的Chat客户端 | 36 | | NextChat | 7000 | 轻量级聊天前端 | 37 | | LibreChat | 8000 | 界面美观的聊天系统 | 38 | | LobeChat | 9000 | 界面美观的聊天系统 | 39 | 40 | ## 🚀 快速开始 41 | 42 | ### 一键安装 43 | ```bash 44 | curl -fsSL https://raw.githubusercontent.com/Protomyst/vpsai/main/install.sh | sudo bash 45 | ``` 46 | 47 | ### 使用教程 48 | 49 | 1. **选择服务类型** 50 | ```bash 51 | 1. API服务 52 | 2. Chat服务 53 | ``` 54 | 55 | 2. **配置参数** 56 | - 端口号(可自定义) 57 | - API Key(部分服务需要) 58 | - 访问密码(可选) 59 | 60 | 3. **域名配置** 61 | ```bash 62 | # 使用自定义证书 63 | vpsai > 5 > 1 64 | 65 | # 自动申请Let's Encrypt 66 | vpsai > 5 > 2 67 | ``` 68 | 69 | ## 💻 环境要求 70 | 71 | - Linux系统(Debian/Ubuntu/CentOS) 72 | - Root权限 73 | - Docker环境 74 | - 最低配置: 75 | - CPU: 1核 76 | - 内存: 2G 77 | - 硬盘: 20G 78 | 79 | ## 📝 配置说明 80 | 81 | ### 数据目录结构 82 | ``` 83 | /root/ai/ 84 | ├── data/ # 服务数据 85 | │ ├── one-api/ 86 | │ ├── new-api/ 87 | │ └── ... 88 | ├── logs/ # 运行日志 89 | └── backup/ # 备份文件 90 | ``` 91 | 92 | ### 端口使用 93 | - API服务: 3000-5000 94 | - Chat服务: 6001-9000 95 | - 可自定义修改 96 | 97 | ## 🔒 安全建议 98 | 99 | 1. 修改默认密码 100 | 2. 配置域名和HTTPS 101 | 3. 定期备份数据 102 | 4. 及时更新版本 103 | 104 | ## 🆘 常见问题 105 | 106 |
107 | 1. 端口冲突解决 108 | 检查占用端口进程: 109 | ```bash 110 | netstat -tunlp | grep 端口号 111 | ``` 112 |
113 | 114 |
115 | 2. 服务无法访问 116 | - 检查防火墙配置 117 | - 确认端口是否开放 118 | - 查看服务日志 119 |
120 | 121 | ## 📞 获取帮助 122 | 123 | - Issues: https://github.com/Protomyst/vpsai/issues 124 | - 邮箱: protomyst@outlook.com 125 | 126 | ## 📄 开源协议 127 | 128 | 本项目采用 [MIT](LICENSE) 协议开源。 129 | -------------------------------------------------------------------------------- /help.md: -------------------------------------------------------------------------------- 1 | # VPSAI 帮助文档 2 | 3 | ## 基本信息 4 | 5 | - 版本:v0.0.1 6 | - 作者:Protomyst 7 | - 仓库:https://github.com/Protomyst/vpsai 8 | 9 | ## 功能说明 10 | 11 | ### 1. API服务 12 | 13 | - OneAPI (端口: 3000) 14 | - NewAPI (端口: 4000) 15 | - VoAPI (端口: 5000, 仅支持AMD64架构) 16 | 17 | ### 2. Chat服务 18 | 19 | - Open-WebUI (端口: 6001) 20 | - NextChat (端口: 7000) 21 | - LibreChat (端口: 8000) 22 | - LobeChat (端口: 9000) 23 | 24 | ### 3. 域名配置 25 | 26 | 支持: 27 | - 自定义SSL证书 28 | - 自动申请Let's Encrypt证书 29 | 30 | ### 4. 自动更新 31 | 32 | - 支持每日自动检查更新 33 | - 可手动检查更新 34 | 35 | ## 数据目录 36 | 37 | 所有服务数据存储在:/root/ai/data/{服务名称} 38 | 39 | ## 注意事项 40 | 41 | 1. 必须使用root用户运行 42 | 2. Open-WebUI建议配置不低于1核1G内存 43 | 3. 请定期备份数据目录 44 | 45 | ## 常见问题 46 | 47 | 1. 端口被占用: 48 | 检查并关闭占用端口的程序,或更换端口 49 | 50 | 2. 服务无法访问: 51 | 检查防火墙设置是否放行对应端口 52 | 53 | 3. 证书配置失败: 54 | 确保域名已正确解析到服务器IP 55 | 56 | ## 获取帮助 57 | 58 | 如遇问题请访问项目仓库提交Issue 59 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 颜色定义 4 | RED='\033[0;31m' 5 | GREEN='\033[0;32m' 6 | YELLOW='\033[1;33m' 7 | NC='\033[0m' 8 | 9 | # 检查root权限 10 | check_root() { 11 | if [ "$(id -u)" != "0" ]; then 12 | echo -e "${RED}错误: 必须使用root权限运行此脚本${NC}" 13 | exit 1 14 | fi 15 | } 16 | 17 | # 检查系统要求 18 | check_system() { 19 | echo -e "${YELLOW}正在检查系统环境...${NC}" 20 | 21 | # 检查Linux发行版 22 | if [ -f /etc/os-release ]; then 23 | . /etc/os-release 24 | case $ID in 25 | debian|ubuntu|centos|rocky|rhel) 26 | echo -e "${GREEN}系统检查通过: $PRETTY_NAME${NC}" 27 | ;; 28 | *) 29 | echo -e "${RED}不支持的系统: $PRETTY_NAME${NC}" 30 | exit 1 31 | ;; 32 | esac 33 | else 34 | echo -e "${RED}无法确定系统类型${NC}" 35 | exit 1 36 | fi 37 | 38 | # 检查内存 39 | total_mem=$(free -m | awk '/^Mem:/{print $2}') 40 | if [ $total_mem -lt 2048 ]; then 41 | echo -e "${YELLOW}警告: 内存小于2GB,部分服务可能无法正常运行${NC}" 42 | fi 43 | } 44 | 45 | # 安装基础依赖 46 | install_base() { 47 | echo -e "${YELLOW}安装基础依赖...${NC}" 48 | 49 | # 检测包管理器 50 | if command -v apt &> /dev/null; then 51 | PKG_MANAGER="apt" 52 | PKG_UPDATE="apt update" 53 | PKG_INSTALL="apt install -y" 54 | elif command -v yum &> /dev/null; then 55 | PKG_MANAGER="yum" 56 | PKG_UPDATE="yum update" 57 | PKG_INSTALL="yum install -y" 58 | else 59 | echo -e "${RED}不支持的包管理器${NC}" 60 | exit 1 61 | fi 62 | 63 | # 更新包列表 64 | $PKG_UPDATE 65 | 66 | # 安装必要工具 67 | for pkg in curl wget git sudo; do 68 | if ! command -v $pkg &> /dev/null; then 69 | echo "安装 $pkg..." 70 | $PKG_INSTALL $pkg 71 | fi 72 | done 73 | } 74 | 75 | # 安装Docker 76 | install_docker() { 77 | echo -e "${YELLOW}安装Docker环境...${NC}" 78 | 79 | if ! command -v docker &> /dev/null; then 80 | curl -fsSL https://get.docker.com | sh 81 | systemctl start docker 82 | systemctl enable docker 83 | fi 84 | 85 | if ! command -v docker-compose &> /dev/null; then 86 | $PKG_INSTALL docker-compose 87 | fi 88 | } 89 | 90 | # 下载VPSAI 91 | install_vpsai() { 92 | echo -e "${YELLOW}安装VPSAI...${NC}" 93 | 94 | # 创建安装目录 95 | install_dir="/root/vpsai" 96 | mkdir -p "$install_dir" 97 | 98 | # 下载代码 99 | git clone https://github.com/Protomyst/vpsai.git "$install_dir" 100 | 101 | # 设置权限 102 | chmod +x "$install_dir/vpsai.sh" 103 | 104 | # 创建快捷方式 105 | ln -sf "$install_dir/vpsai.sh" /usr/local/bin/vpsai 106 | 107 | echo -e "${GREEN}VPSAI安装完成!${NC}" 108 | echo -e "使用方法:" 109 | echo -e " 1. 输入 ${YELLOW}vpsai${NC} 启动管理面板" 110 | echo -e " 2. 或进入 ${YELLOW}$install_dir${NC} 目录" 111 | echo -e " 执行 ${YELLOW}./vpsai.sh${NC}" 112 | } 113 | 114 | # 主函数 115 | main() { 116 | clear 117 | echo "================================================" 118 | echo " VPSAI 一键安装脚本 " 119 | echo "================================================" 120 | echo 121 | 122 | check_root 123 | check_system 124 | install_base 125 | install_docker 126 | install_vpsai 127 | 128 | echo 129 | echo "================================================" 130 | echo -e "${GREEN}安装已完成!${NC}" 131 | echo "现在可以使用 'vpsai' 命令启动管理面板" 132 | echo "================================================" 133 | } 134 | 135 | main 136 | -------------------------------------------------------------------------------- /vpsai.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="v0.0.1" 4 | AUTHOR="Protomyst" 5 | ROOT_DIR="/root/ai" 6 | DATA_DIR="${ROOT_DIR}/data" 7 | 8 | # 显示logo 9 | show_logo() { 10 | echo ' __ ______ ____ _ ___ ' 11 | echo ' \ \ / / _ \/ ___| / \ |_ _|' 12 | echo ' \ \ / /| |_) \___ \ / _ \ | | ' 13 | echo ' \ V / | __/ ___) / ___ \ | | ' 14 | echo ' \_/ |_| |____/_/ \_\___|' 15 | echo 16 | echo " Version: ${VERSION} Author: ${AUTHOR}" 17 | echo 18 | } 19 | 20 | # 检查root权限 21 | check_root() { 22 | if [ "$(id -u)" != "0" ]; then 23 | echo "错误:必须以root用户运行此脚本" 24 | exit 1 25 | fi 26 | } 27 | 28 | # 检查系统架构 29 | check_arch() { 30 | arch=$(uname -m) 31 | is_arm=0 32 | if [ "$arch" = "aarch64" ] || [ "$arch" = "arm64" ]; then 33 | is_arm=1 34 | fi 35 | } 36 | 37 | # 检查并安装依赖 38 | install_dependencies() { 39 | echo "正在检查依赖..." 40 | 41 | # 检测包管理器 42 | if command -v apt &> /dev/null; then 43 | PKG_MANAGER="apt" 44 | PKG_UPDATE="apt update" 45 | PKG_INSTALL="apt install -y" 46 | elif command -v yum &> /dev/null; then 47 | PKG_MANAGER="yum" 48 | PKG_UPDATE="yum update" 49 | PKG_INSTALL="yum install -y" 50 | else 51 | echo "不支持的包管理器" 52 | exit 1 53 | fi 54 | 55 | # 更新包列表 56 | $PKG_UPDATE 57 | 58 | # 安装基础依赖 59 | for pkg in git docker.io docker-compose nginx; do 60 | if ! command -v $pkg &> /dev/null; then 61 | echo "正在安装 $pkg..." 62 | $PKG_INSTALL $pkg 63 | fi 64 | done 65 | } 66 | 67 | # 启动必要服务 68 | start_services() { 69 | systemctl start docker 70 | systemctl enable docker 71 | systemctl start nginx 72 | systemctl enable nginx 73 | } 74 | 75 | # 检查端口是否被占用 76 | check_port() { 77 | local port=$1 78 | if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null ; then 79 | return 1 80 | fi 81 | return 0 82 | } 83 | 84 | # 开放防火墙端口 85 | open_firewall_port() { 86 | local port=$1 87 | if command -v ufw &> /dev/null; then 88 | ufw allow $port 89 | elif command -v firewall-cmd &> /dev/null; then 90 | firewall-cmd --permanent --add-port=$port/tcp 91 | firewall-cmd --reload 92 | fi 93 | } 94 | 95 | # 创建数据目录 96 | create_data_dir() { 97 | local service=$1 98 | local dir="${DATA_DIR}/${service}" 99 | mkdir -p "$dir" 100 | echo "$dir" 101 | } 102 | 103 | # 显示帮助信息 104 | show_help() { 105 | if [ -f "${ROOT_DIR}/help.md" ]; then 106 | less "${ROOT_DIR}/help.md" 107 | else 108 | echo "帮助文件不存在" 109 | fi 110 | } 111 | 112 | # 错误处理 113 | handle_error() { 114 | local error_msg="$1" 115 | echo "错误: ${error_msg}" >&2 116 | logger -t vpsai "错误: ${error_msg}" 117 | } 118 | 119 | # 显示进度 120 | show_progress() { 121 | local current="$1" 122 | local total="$2" 123 | local msg="$3" 124 | printf "\r[%-50s] %d%% %s" \ 125 | "$(printf '#%.0s' $(seq 1 $(($current*50/$total))))" \ 126 | $(($current*100/$total)) \ 127 | "${msg}" 128 | } 129 | 130 | # 主菜单 131 | show_menu() { 132 | clear 133 | show_logo 134 | echo "请选择操作:" 135 | echo "1. 安装API服务 (OneAPI / NewAPI / VoAPI)" 136 | echo "2. 安装Chat服务 (Open-WebUI / NextChat / LibreChat (暂时失效) / LobeChat)" 137 | echo "3. 检查服务状态" 138 | echo "4. 配置自动更新或手动更新" 139 | echo "5. 配置自定义域名" 140 | echo "6. 删除Chat / API服务" 141 | echo "7. 查看帮助" 142 | echo "8. 退出脚本" 143 | echo 144 | read -p "请输入选项 [1-8]: " choice 145 | 146 | case $choice in 147 | 1) install_api_service && read -p "按回车键继续..." ;; 148 | 2) install_chat_service && read -p "按回车键继续..." ;; 149 | 3) check_service_status ;; 150 | 4) configure_updates && read -p "按回车键继续..." ;; 151 | 5) configure_domain && read -p "按回车键继续..." ;; 152 | 6) remove_service && read -p "按回车键继续..." ;; 153 | 7) show_help ;; 154 | 8) exit 0 ;; 155 | *) echo "无效选项" && read -p "按回车键继续..." ;; 156 | esac 157 | } 158 | 159 | ################## 160 | # 服务安装相关函数 # 161 | ################## 162 | 163 | install_api_service() { 164 | echo "请选择要安装的API服务:" 165 | echo "1. OneAPI (默认端口: 3000)" 166 | echo "2. NewAPI (默认端口: 4000)" 167 | if [ $is_arm -eq 0 ]; then 168 | echo "3. VoAPI (默认端口: 5000)" 169 | fi 170 | echo "0. 返回" 171 | 172 | read -p "请选择: " api_choice 173 | 174 | case $api_choice in 175 | 1) install_one_api ;; 176 | 2) install_new_api ;; 177 | 3) 178 | if [ $is_arm -eq 0 ]; then 179 | install_vo_api 180 | else 181 | echo "VoAPI 不支持 ARM 架构" 182 | fi 183 | ;; 184 | 0) return ;; 185 | *) echo "无效选项" ;; 186 | esac 187 | } 188 | 189 | # Chat服务安装 190 | install_chat_service() { 191 | echo "请选择要安装的Chat服务:" 192 | echo "1. Open-WebUI (默认端口: 6001)" 193 | echo "2. NextChat (默认端口: 7000)" 194 | echo "3. LibreChat (默认端口: 8000)" 195 | echo "4. LobeChat (默认端口: 9000)" 196 | echo "0. 返回" 197 | 198 | read -p "请选择: " chat_choice 199 | 200 | # 获取系统内存 201 | total_mem=$(free -m | awk '/^Mem:/{print $2}') 202 | 203 | case $chat_choice in 204 | 1) 205 | if [ $total_mem -lt 1024 ]; then 206 | echo "警告: 系统内存小于1GB,Open-WebUI可能无法正常运行" 207 | read -p "是否继续安装?(y/n): " confirm 208 | if [ "$confirm" != "y" ];then 209 | return 210 | fi 211 | fi 212 | install_open_webui 213 | ;; 214 | 2) install_nextchat ;; 215 | 3) install_librechat ;; 216 | 4) install_lobechat ;; 217 | 0) return ;; 218 | *) echo "无效选项" ;; 219 | esac 220 | } 221 | 222 | # 获取服务器IP 223 | get_server_ip() { 224 | # 优先获取公网IPv4地址 225 | public_ip=$(curl -s -4 ip.sb || curl -s -4 ifconfig.me || curl -s -4 icanhazip.com) 226 | if [ -n "$public_ip" ]; then 227 | echo "$public_ip" 228 | return 229 | fi 230 | 231 | # 如果获取公网IP失败,使用本地IP 232 | local_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v "127.0.0.1" | head -n 1) 233 | if [ -n "$local_ip" ]; then 234 | echo "$local_ip" 235 | return 236 | fi 237 | 238 | echo "无法获取服务器IP" 239 | } 240 | 241 | # 显示访问地址 242 | show_access_info() { 243 | local service_name="$1" 244 | local port="$2" 245 | local extra_info="$3" 246 | 247 | echo "----------------------------------------" 248 | echo "🎉 $service_name 安装完成!" 249 | echo 250 | local ip=$(get_server_ip) 251 | echo "访问地址: http://$ip:$port" 252 | if [ -n "$extra_info" ]; then 253 | echo "$extra_info" 254 | fi 255 | echo "----------------------------------------" 256 | echo -e "\n按回车键返回主菜单..." 257 | read 258 | } 259 | 260 | # OneAPI安装 261 | install_one_api() { 262 | local default_port=3000 263 | read -p "请输入端口号 (默认: $default_port): " port 264 | port=${port:-$default_port} 265 | 266 | if ! check_port $port; then 267 | echo "端口 $port 已被占用" 268 | return 269 | fi 270 | 271 | local data_dir=$(create_data_dir "one-api") 272 | 273 | docker run --name one-api -d \ 274 | --restart always \ 275 | -p ${port}:3000 \ 276 | -e TZ=Asia/Shanghai \ 277 | -v ${data_dir}:/data \ 278 | justsong/one-api 279 | 280 | open_firewall_port $port 281 | show_access_info "OneAPI" "$port" "初始用户名: root\n初始密码: 123456" 282 | } 283 | 284 | # NewAPI安装 285 | install_new_api() { 286 | local default_port=4000 287 | read -p "请输入端口号 (默认: $default_port): " port 288 | port=${port:-$default_port} 289 | 290 | if ! check_port $port; then 291 | echo "端口 $port 已被占用" 292 | return 293 | fi 294 | 295 | local data_dir=$(create_data_dir "new-api") 296 | 297 | docker run --name new-api -d \ 298 | --restart always \ 299 | -p ${port}:3000 \ 300 | -e TZ=Asia/Shanghai \ 301 | -v ${data_dir}:/data \ 302 | calciumion/new-api:latest 303 | 304 | open_firewall_port $port 305 | show_access_info "NewAPI" "$port" 306 | } 307 | 308 | # VoAPI安装 309 | install_vo_api() { 310 | local default_port=5000 311 | read -p "请输入端口号 (默认: $default_port): " port 312 | port=${port:-$default_port} 313 | 314 | if ! check_port $port; then 315 | echo "端口 $port 已被占用" 316 | return 317 | fi 318 | 319 | local data_dir=$(create_data_dir "voapi") 320 | local compose_file="${data_dir}/docker-compose.yml" 321 | 322 | # 生成docker-compose配置 323 | cat > "$compose_file" </dev/null 2>&1; then 522 | local status="运行中" 523 | local ports=$(docker port $service 2>/dev/null | awk '{print $3}' | cut -d':' -f2 | tr '\n' ',') 524 | local mem=$(docker stats $service --no-stream --format "{{.MemUsage}}" 2>/dev/null) 525 | printf "%-15s %-10s %-20s %-10s\n" "$service" "$status" "${ports%,}" "$mem" 526 | else 527 | if docker ps -aq -f name=$service >/dev/null 2>&1; then 528 | printf "%-15s %-10s %-20s %-10s\n" "$service" "已停止" "-" "-" 529 | else 530 | printf "%-15s %-10s %-20s %-10s\n" "$service" "未安装" "-" "-" 531 | fi 532 | fi 533 | done 534 | 535 | echo -e "\n输入服务名可以管理该服务,直接回车返回主菜单" 536 | read -p "请输入: " service_name 537 | 538 | if [ -n "$service_name" ]; then 539 | if [[ " ${services[@]} " =~ " ${service_name} " ]]; then 540 | manage_service "$service_name" 541 | else 542 | echo "无效的服务名" 543 | fi 544 | fi 545 | } 546 | 547 | ################## 548 | # 自定义域名配置 # 549 | ################## 550 | # 配置域名 551 | configure_domain() { 552 | echo "域名配置:" 553 | echo "1. 使用自定义证书" 554 | echo "2. 自动申请Let's Encrypt证书" 555 | echo "0. 返回" 556 | 557 | read -p "请选择: " choice 558 | 559 | case $choice in 560 | 1) configure_custom_cert ;; 561 | 2) configure_letsencrypt ;; 562 | 0) return ;; 563 | *) echo "无效选项" ;; 564 | esac 565 | } 566 | 567 | # 配置自定义证书 568 | configure_custom_cert() { 569 | read -p "请输入域名: " domain 570 | read -p "请输入证书路径: " cert_path 571 | read -p "请输入私钥路径: " key_path 572 | 573 | if [ ! -f "$cert_path" ] || [ ! -f "$key_path" ]; then 574 | echo "证书文件不存在" 575 | return 576 | fi 577 | 578 | # 选择要配置的服务 579 | echo "请选择要配置的服务:" 580 | echo "1. OneAPI (端口: 3000)" 581 | echo "2. NewAPI (端口: 4000)" 582 | echo "3. VoAPI (端口: 5000)" 583 | echo "4. Open-WebUI (端口: 6001)" 584 | echo "5. NextChat (端口: 7000)" 585 | echo "6. LibreChat (端口: 8000)" 586 | echo "7. LobeChat (端口: 9000)" 587 | 588 | read -p "请选择 [1-7]: " service_choice 589 | 590 | local port 591 | case $service_choice in 592 | 1) port=3000 ;; 593 | 2) port=4000 ;; 594 | 3) port=5000 ;; 595 | 4) port=6001 ;; 596 | 5) port=7000 ;; 597 | 6) port=8000 ;; 598 | 7) port=9000 ;; 599 | *) 600 | echo "无效选项" 601 | return 602 | ;; 603 | esac 604 | 605 | create_nginx_config "$domain" "$cert_path" "$key_path" "$port" 606 | } 607 | 608 | # 配置Let's Encrypt证书 609 | configure_letsencrypt() { 610 | # 清理可能存在的旧临时配置 611 | rm -f /etc/nginx/sites-enabled/*.temp 612 | rm -f /etc/nginx/sites-available/*.temp 613 | 614 | read -p "请输入域名: " domain 615 | read -p "请输入邮箱(用于证书通知): " email 616 | 617 | # 创建验证目录 618 | local acme_dir="/var/www/letsencrypt/.well-known/acme-challenge" 619 | mkdir -p "$acme_dir" 620 | 621 | # 创建初始Nginx配置用于验证 622 | local nginx_temp="/etc/nginx/sites-available/${domain}.temp" 623 | cat > "$nginx_temp" < /dev/null; then 657 | echo "正在安装certbot..." 658 | snap install --classic certbot 659 | # 等待安装完成 660 | sleep 2 661 | fi 662 | 663 | # 申请证书 664 | certbot certonly \ 665 | --webroot \ 666 | --agree-tos \ 667 | --email "$email" \ 668 | --webroot-path /var/www/letsencrypt \ 669 | --domains "$domain" \ 670 | --non-interactive 671 | 672 | local cert_result=$? 673 | 674 | # 清理临时配置 675 | rm -f "$nginx_temp" 676 | rm -f "/etc/nginx/sites-enabled/${domain}.temp" 677 | systemctl reload nginx 678 | 679 | if [ $cert_result -ne 0 ]; then 680 | echo "证书申请失败" 681 | return 1 682 | fi 683 | 684 | # 获取证书路径 685 | local cert_path="/etc/letsencrypt/live/$domain/fullchain.pem" 686 | local key_path="/etc/letsencrypt/live/$domain/privkey.pem" 687 | 688 | if [ ! -f "$cert_path" ] || [ ! -f "$key_path" ]; then 689 | echo "证书文件不存在" 690 | return 1 691 | fi 692 | 693 | # 配置服务 694 | echo "请选择要配置的服务:" 695 | echo "1. OneAPI (端口: 3000)" 696 | echo "2. NewAPI (端口: 4000)" 697 | echo "3. VoAPI (端口: 5000)" 698 | echo "4. Open-WebUI (端口: 6001)" 699 | echo "5. NextChat (端口: 7000)" 700 | echo "6. LibreChat (端口: 8000)" 701 | echo "7. LobeChat (端口: 9000)" 702 | 703 | read -p "请选择 [1-7]: " service_choice 704 | 705 | local port 706 | case $service_choice in 707 | 1) port=3000 ;; 708 | 2) port=4000 ;; 709 | 3) port=5000 ;; 710 | 4) port=6001 ;; 711 | 5) port=7000 ;; 712 | 6) port=8000 ;; 713 | 7) port=9000 ;; 714 | *) 715 | echo "无效选项" 716 | return 1 717 | ;; 718 | esac 719 | 720 | create_nginx_config "$domain" "$cert_path" "$key_path" "$port" 721 | 722 | # 配置自动续期 723 | echo "配置证书自动续期..." 724 | (crontab -l 2>/dev/null | grep -v "certbot renew"; echo "0 0 1 * * certbot renew --quiet") | crontab - 725 | 726 | echo "证书配置完成!" 727 | echo "域名访问地址: https://$domain" 728 | } 729 | 730 | # 创建Nginx配置 731 | create_nginx_config() { 732 | domain=$1 733 | cert_path=$2 734 | key_path=$3 735 | port=$4 736 | 737 | nginx_config="/etc/nginx/sites-available/$domain" 738 | 739 | # 创建最终的Nginx配置 740 | cat > "$nginx_config" </dev/null 2>&1; then 827 | cd vpsai 828 | remote_version=$(grep "VERSION=" vpsai.sh | cut -d'"' -f2) 829 | if [ "$remote_version" != "$VERSION" ]; then 830 | echo "发现新版本: $remote_version" 831 | read -p "是否更新?(y/n): " update_choice 832 | if [ "$update_choice" = "y" ]; then 833 | cp -r * /root/vpsai/ 834 | echo "更新完成" 835 | fi 836 | else 837 | echo "已是最新版本" 838 | fi 839 | cd .. 840 | rm -rf vpsai 841 | else 842 | echo "检查更新失败" 843 | fi 844 | } 845 | 846 | # 配置自动更新 847 | configure_updates() { 848 | echo "更新配置:" 849 | echo "1. 立即检查更新" 850 | echo "2. 配置自动更新" 851 | echo "3. 取消自动更新" 852 | echo "0. 返回" 853 | 854 | read -p "请选择: " choice 855 | 856 | case $choice in 857 | 1) check_script_update ;; 858 | 2) setup_auto_update ;; 859 | 3) remove_auto_update ;; 860 | 0) return ;; 861 | *) echo "无效选项" ;; 862 | esac 863 | } 864 | 865 | # 检查单个服务更新 866 | check_service_update() { 867 | local service="$1" 868 | local image="$2" 869 | 870 | echo "检查 $service 更新..." 871 | if docker pull "$image" | grep -q "Image is up to date"; then 872 | echo "$service 已是最新版本" 873 | return 0 874 | else 875 | echo "发现 $service 新版本" 876 | read -p "是否更新?(y/n): " update_choice 877 | if [ "$update_choice" = "y" ]; then 878 | docker stop "$service" 879 | docker rm "$service" 880 | return 1 881 | fi 882 | fi 883 | return 0 884 | } 885 | 886 | # 优化自动更新设置 887 | setup_auto_update() { 888 | # 创建更新脚本 889 | cat > /etc/cron.daily/vpsai-update < >(tee -a "${ROOT_DIR}/logs/vpsai.log") 934 | exec 2> >(tee -a "${ROOT_DIR}/logs/vpsai.error.log") 935 | 936 | install_dependencies 937 | start_services 938 | 939 | while true; do 940 | show_menu 941 | # 这里不需要额外的暂停,因为在show_menu中已经处理了 942 | done 943 | } 944 | 945 | main 946 | --------------------------------------------------------------------------------