forked from wangbo/easyai
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6d9705566 | |||
| 41549e8a72 | |||
| ac795ad38d | |||
| dd1c905cd8 | |||
| 512dd66762 | |||
| f2f10d543e | |||
| a70545b702 | |||
| ded45063d9 | |||
| e64a90332c | |||
| e9c594c3e6 | |||
| f37fd958e1 | |||
| fe370bd5bc | |||
| b7a11abe9f | |||
| ea70cecef1 | |||
| bb2373d4f6 | |||
| 59a1a88e29 | |||
| 5bca174da8 | |||
| b3f969b3a3 | |||
| 1b85369903 |
37
.env.AMS.sample
Normal file
37
.env.AMS.sample
Normal file
@ -0,0 +1,37 @@
|
||||
# ============================================
|
||||
# Agent 记忆服务(agent-memory)环境变量
|
||||
# 使用前请复制为 .env.AMS 并根据实际情况修改
|
||||
# ============================================
|
||||
|
||||
# ---------- PostgreSQL(复用 easyai-pgvector)----------
|
||||
|
||||
# ---------- 主服务连接(用于 Embedding 与对话压缩)----------
|
||||
# 主服务 API 地址(容器内网地址)
|
||||
MEMORY_AI_BASE_URL=http://comfy-server:3001
|
||||
MEMORY_AI_API_KEY=
|
||||
|
||||
# ---------- Embeddings ----------
|
||||
MEMORY_EMBEDDINGS_PATH=/v1/embeddings
|
||||
MEMORY_EMBEDDING_MODELS_PATH=/v1/embeddings/models
|
||||
MEMORY_EMBEDDING_DIMENSION=1024
|
||||
MEMORY_EMBEDDING_MODEL=Qwen3-Embedding-v4
|
||||
MEMORY_SCORE_CONFIDENCE_BASE=0.6
|
||||
MEMORY_SCORE_CONFIDENCE_GAIN=0.4
|
||||
MEMORY_SCORE_IMPORTANCE_BASE=0.7
|
||||
MEMORY_SCORE_IMPORTANCE_GAIN=0.3
|
||||
|
||||
# ---------- 对话压缩 ----------
|
||||
MEMORY_CHAT_COMPLETIONS_PATH=/v1/chat/completions
|
||||
MEMORY_CHAT_MODELS_PATH=/v1/models
|
||||
MEMORY_CHAT_MODEL=qwen-plus
|
||||
MEMORY_COMPRESSION_ENABLED=true
|
||||
MEMORY_COMPRESSION_MIN_LENGTH=80
|
||||
|
||||
# ---------- 其他 ----------
|
||||
# 日志级别
|
||||
LOG_LEVEL=log,error,warn,debug
|
||||
# 是否禁用 Swagger 文档(true 禁用)
|
||||
MEMORY_DOCS_DISABLE=false
|
||||
|
||||
# 记忆服务镜像版本
|
||||
AMS_VERSION=latest
|
||||
@ -5,9 +5,8 @@
|
||||
|
||||
# ---------- PostgreSQL ----------
|
||||
# Prisma 数据库连接字符串(容器内网地址)
|
||||
ASG_DATABASE_URL=postgresql://easyai:easyai2025@easyai-asg-pg:5432/agent_governance?schema=public
|
||||
ASG_DATABASE_URL=postgresql://easyai:easyai2025@easyai-pgvector:5432/agent_governance?schema=public
|
||||
|
||||
# PostgreSQL 容器初始化配置(与 docker-compose 中 sg-postgres 保持一致)
|
||||
ASG_POSTGRES_USER=easyai
|
||||
ASG_POSTGRES_PASSWORD=easyai2025
|
||||
ASG_POSTGRES_DB=agent_governance
|
||||
|
||||
114
.env.sample
114
.env.sample
@ -1,70 +1,66 @@
|
||||
#############################################
|
||||
#1、如下配置需要根据实际情况进行配置
|
||||
##############################################
|
||||
# 默认服务器地址,本地不需要更改,云服务需要修改为云端IP,并放行对应端口 TODO
|
||||
# EasyAI 环境变量配置
|
||||
# 复制为 .env 并根据实际情况修改
|
||||
#############################################
|
||||
|
||||
# ========== 1. 访问地址(部署时必改) ==========
|
||||
# 默认服务器 API 地址,云服务需修改为云端 IP 并放行对应端口
|
||||
NUXT_PUBLIC_BASE_APIURL=http://127.0.0.1:3001
|
||||
#域名访问使用如下配置,使用/api进行转发,并配置nginx代理/api至3001端口
|
||||
#NUXT_PUBLIC_BASE_APIURL=/api
|
||||
# 默认的服务器websocket地址,本地不需要更改,云服务需要修改为云端IP,并放行对应端口 TODO
|
||||
# 域名访问:NUXT_PUBLIC_BASE_APIURL=/api
|
||||
|
||||
# 默认 WebSocket 地址
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=ws://127.0.0.1:3002
|
||||
#域名访问使用如下配置,配置为/,并代理/socket.io请求到3002端口
|
||||
#NUXT_PUBLIC_BASE_SOCKETURL=wss://yourwebsite.com/socket.io
|
||||
# Agent 服务治理 API 地址,前端管理页面需要。IP访问填http://<IP>:3003,域名访问填/asg-api
|
||||
# 域名访问:NUXT_PUBLIC_BASE_SOCKETURL=wss://yourwebsite.com/socket.io
|
||||
|
||||
# Agent 服务治理 API 地址,前端管理页面需要
|
||||
NUXT_PUBLIC_SG_APIURL=http://127.0.0.1:3003
|
||||
#域名访问使用如下配置,通过nginx代理转发
|
||||
#NUXT_PUBLIC_SG_APIURL=/asg-api
|
||||
# 域名访问:NUXT_PUBLIC_SG_APIURL=/asg-api
|
||||
|
||||
#############################################
|
||||
#2、以下部分可保持默认,如果没有端口冲突,生产环境可修改密码
|
||||
##############################################
|
||||
#comfyAI-web 前端应用暴露端口,访问地址ip:3010访问,初始化管理员账号admin,密码123456
|
||||
# ========== 2. 服务端口 ==========
|
||||
WEB_PORT=3010
|
||||
#支持静态资源CDN,将静态资源从服务器分离
|
||||
NUXT_APP_CDN_URL=
|
||||
# easyai-web 前端端口,访问地址 ip:3010,初始化管理员 admin/123456
|
||||
|
||||
SERVER_HTTP_PORT=3001
|
||||
SERVER_WS_PORT=3002
|
||||
# easyai-server 后端 HTTP / WebSocket 端口
|
||||
|
||||
#视频编辑对外暴露端口
|
||||
VIDEO_EDIT_PORT=8000
|
||||
# 视频编辑服务对外端口
|
||||
|
||||
#沙箱环境对外端口,不建议暴露,如果需要暴露,取消docker-compose.yml中的对应注释
|
||||
AMS_PORT=3004
|
||||
# Agent 记忆服务 HTTP 端口(启用 memory profile 时)
|
||||
|
||||
# ========== 3. Agent 记忆服务(启用 memory profile 时) ==========
|
||||
MEMORY_TCP_HOST=agent-memory
|
||||
MEMORY_TCP_PORT=4004
|
||||
# easyai-server 调用 agent-memory 的 TCP 连接
|
||||
|
||||
# ========== 4. 沙箱环境 ==========
|
||||
SANDBOX_PORT=8081
|
||||
#SANDBOX jupyterlab 端口
|
||||
# 不建议对外暴露
|
||||
|
||||
SANDBOX_JUPYTERLAB_PORT=8888
|
||||
# 配置Jupter的token,安全考虑,建议设置
|
||||
SANDBOX_JUPYTER_TOKEN=easyaiisbest
|
||||
# 建议设置 token
|
||||
|
||||
SANDBOX_SERVICE_BASE_URL=
|
||||
|
||||
|
||||
|
||||
#REDIS暴露端口,默认不暴露
|
||||
# ========== 5. Redis ==========
|
||||
REDIS_PORT=
|
||||
# 默认不对外暴露
|
||||
|
||||
CONFIG_COMFYUI_QUENE_REDIS_USERNAME=
|
||||
CONFIG_COMFYUI_QUENE_REDIS_PASSWORD=
|
||||
#队列使用的DB
|
||||
CONFIG_COMFYUI_QUENE_REDIS_DB=6
|
||||
#普通缓存使用的DB
|
||||
CONFIG_COMFYUI_CACHE_REDIS_DB=11
|
||||
|
||||
#MONGO 暴露端口,默认不暴露。用户名密码初次部署可以修改,更新请勿修改
|
||||
# ========== 6. MongoDB ==========
|
||||
MONGO_PORT=27017
|
||||
MONGO_INITDB_ROOT_USERNAME=username
|
||||
MONGO_INITDB_ROOT_PASSWORD=password
|
||||
#comfy-server后端web服务暴露端口。一般情况下无需修改
|
||||
SERVER_HTTP_PORT=3001
|
||||
SERVER_WS_PORT=3002
|
||||
#watchtower 监听端口,自动更新容器和通过浏览器查看容器日志
|
||||
WATCHTOWER_PORT=8089
|
||||
PORTAINER_PORT=8090
|
||||
PORTAINER_HTTPS_PORT=8091
|
||||
# 初次部署可修改,更新请勿修改
|
||||
|
||||
#网络代理,链接gpt等需要设置
|
||||
CONFIG_PROXY_URL=
|
||||
|
||||
|
||||
#实例ID、集群情况下区分不同客户端
|
||||
CONFIG_INSTANCE_ID=2025
|
||||
|
||||
#消息队列
|
||||
# ========== 7. 消息队列 RabbitMQ ==========
|
||||
CONFIG_MQ_PROTOCOL=amqp
|
||||
CONFIG_MQ_USER=admin
|
||||
CONFIG_MQ_PASSWORD=easyai2025
|
||||
@ -73,25 +69,35 @@ CONFIG_MQ_PORT=5672
|
||||
CONFIG_MQ_ADMIN_PORT=15672
|
||||
CONFIG_MQ_VHOST=/
|
||||
|
||||
#版本
|
||||
VERSION=latest
|
||||
#日志与调试
|
||||
LOG_LEVEL=log,error,warn,debug
|
||||
#Token过期时间,单位秒
|
||||
# ========== 8. 鉴权与安全 ==========
|
||||
CONFIG_TOKEN_EXPIRE=1800
|
||||
# token加密密钥,可以修改为任意字符串
|
||||
CONFIG_JWT_SECRET='this is a very secret secret'
|
||||
CONFIG_TOKEN_SIGN_SK=easyai2025easyai
|
||||
|
||||
# ========== 9. 运维与调试 ==========
|
||||
CONFIG_INSTANCE_ID=2025
|
||||
# 集群情况下区分不同客户端
|
||||
|
||||
#minio
|
||||
#MINIO_ROOT_USER=minioadmin
|
||||
#MINIO_ROOT_PASSWORD=minioadmin
|
||||
|
||||
|
||||
|
||||
CONFIG_PROXY_URL=
|
||||
# 连接 GPT 等外部服务时设置
|
||||
|
||||
LOG_LEVEL=log,error,warn,debug
|
||||
|
||||
WATCHTOWER_PORT=8089
|
||||
PORTAINER_PORT=8090
|
||||
PORTAINER_HTTPS_PORT=8091
|
||||
|
||||
# ========== 10. 静态资源 CDN(可选) ==========
|
||||
NUXT_APP_CDN_URL=
|
||||
|
||||
# ========== 11. 版本 ==========
|
||||
VERSION=latest
|
||||
|
||||
# ========== 12. OSS 配置(可选,PDF 解析图片上传) ==========
|
||||
# 可填写 .env.tools 或在此覆盖
|
||||
# OSS_ENDPOINT=
|
||||
# OSS_ACCESS_KEY_ID=
|
||||
# OSS_ACCESS_KEY_SECRET=
|
||||
# OSS_BUCKET=
|
||||
# OSS_REGION=us-east-1
|
||||
# OSS_DOMAIN=
|
||||
|
||||
38
.github/workflows/test-start-ps1.yml
vendored
Normal file
38
.github/workflows/test-start-ps1.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# 在 Windows 上测试 start.ps1(DRY_RUN 模式,不启动 Docker)
|
||||
name: Test start.ps1 (Windows)
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'start.ps1'
|
||||
- '.env.sample'
|
||||
- '.github/workflows/test-start-ps1.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'start.ps1'
|
||||
- '.env.sample'
|
||||
- '.github/workflows/test-start-ps1.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run start.ps1 (DRY_RUN)
|
||||
env:
|
||||
DEPLOY_DRY_RUN: "1"
|
||||
DEPLOY_IP: "192.168.1.100"
|
||||
run: |
|
||||
powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||
|
||||
- name: Verify .env
|
||||
run: |
|
||||
$api = Select-String -Path .env -Pattern '^NUXT_PUBLIC_BASE_APIURL=' | ForEach-Object { $_.Line }
|
||||
$socket = Select-String -Path .env -Pattern '^NUXT_PUBLIC_BASE_SOCKETURL=' | ForEach-Object { $_.Line }
|
||||
$sg = Select-String -Path .env -Pattern '^NUXT_PUBLIC_SG_APIURL=' | ForEach-Object { $_.Line }
|
||||
if ($api -ne 'NUXT_PUBLIC_BASE_APIURL=http://192.168.1.100:3001') { exit 1 }
|
||||
if ($socket -ne 'NUXT_PUBLIC_BASE_SOCKETURL=ws://192.168.1.100:3002') { exit 1 }
|
||||
if ($sg -ne 'NUXT_PUBLIC_SG_APIURL=http://192.168.1.100:3003') { exit 1 }
|
||||
Write-Host "OK: .env 配置正确"
|
||||
183
README.md
183
README.md
@ -1,10 +1,104 @@
|
||||
## 一键部署
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
git clone https://git.51easyai.com/wangbo/easyai.git && cd easyai && chmod +x start.sh && ./start.sh
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
git clone https://git.51easyai.com/wangbo/easyai.git; cd easyai; powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||
```
|
||||
|
||||
> **Windows 脚本权限说明**:PowerShell 默认禁止运行脚本,直接双击 `start.ps1` 会闪退。
|
||||
>
|
||||
> - **推荐**:使用 `powershell -ExecutionPolicy Bypass -File .\start.ps1` 执行,无需修改系统策略
|
||||
> - **或**:以管理员身份打开 PowerShell,执行 `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`,之后可直接运行 `.\start.ps1`
|
||||
|
||||
---
|
||||
|
||||
## 重要更新记录:
|
||||
|
||||
### 2026.3.20
|
||||
|
||||
1. **新增 Agent 记忆服务(AMS)模块**:新增 `agent-memory`(Agent 长期记忆)容器,复用 `easyai-pgvector` 数据库(需 pgvector 扩展),用于支持 Agent 对话记忆、向量召回与反馈能力。
|
||||
2. **新增环境变量文件**:新增 `.env.AMS.sample` 文件,包含记忆服务所需的全部环境变量。
|
||||
3. 主服务 `comfy-server` 新增 `MEMORY_TCP_HOST` 和 `MEMORY_TCP_PORT` 环境变量,用于内部 TCP 微服务通信。
|
||||
4. **Nginx 代理**:`easyai-proxy.conf.sample` 中新增 `/ams-api/` 路径代理(可选)。
|
||||
|
||||
#### 升级步骤
|
||||
|
||||
**步骤一:更新文件**
|
||||
|
||||
将以下文件更新到最新版本:
|
||||
- `docker-compose.yml`
|
||||
- `easyai-proxy.conf.sample`
|
||||
- `docker/postgres/init-pgvector.sql`(PostgreSQL 首次启动时创建 vector 扩展)
|
||||
|
||||
新增文件复制到部署目录:
|
||||
- `.env.AMS.sample` → 复制为 `.env.AMS` 并根据实际环境修改
|
||||
|
||||
**步骤二:配置 `.env.AMS`**
|
||||
|
||||
```bash
|
||||
cp .env.AMS.sample .env.AMS
|
||||
```
|
||||
|
||||
根据实际环境修改 `.env.AMS` 中的关键配置:
|
||||
```dotenv
|
||||
# PostgreSQL 连接(独立库 easyai_memory,与 .env.ASG 中账号密码一致)
|
||||
MEMORY_DATABASE_URL=postgresql://easyai:easyai2025@easyai-pgvector:5432/easyai_memory?schema=public
|
||||
|
||||
# 主服务 API 地址(用于 Embedding 与对话压缩)
|
||||
MEMORY_AI_BASE_URL=http://comfy-server:3001
|
||||
|
||||
# Embedding 模型与维度(需与主服务一致)
|
||||
MEMORY_EMBEDDING_DIMENSION=1024
|
||||
MEMORY_EMBEDDING_MODEL=text-embedding-v4
|
||||
```
|
||||
|
||||
**步骤三:启动服务**
|
||||
|
||||
记忆服务使用 Docker Compose Profile,需显式启用:
|
||||
|
||||
```bash
|
||||
cd ~/easyai
|
||||
# 启动全部服务(含记忆服务)
|
||||
docker compose --profile memory up -d
|
||||
|
||||
# 或仅启动基础服务(不含记忆)
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
新增容器:`agent-memory`(Agent 记忆服务),复用 `easyai-pgvector`,使用独立库 `easyai_memory`。初始化流程与 ASG 一致:
|
||||
- **PostgreSQL 首次启动**:`docker/postgres/init-pgvector.sql` 创建 `vector`、`pgcrypto` 扩展
|
||||
- **easyai-asg 启动**:entrypoint 执行 `prisma migrate deploy`,创建治理相关表
|
||||
- **agent-memory 启动**:entrypoint 执行 `prisma migrate deploy`,创建 `memory_records` 等表
|
||||
|
||||
**注意**:`easyai-pgvector` 使用 `registry.cn-shanghai.aliyuncs.com/easyaigc/pgvector:0.8.2-pg18-trixie`(含 pgvector 扩展)。若此前使用其他镜像且已有数据,升级前请备份 `asg_postgres_data` 卷。
|
||||
|
||||
**步骤四:验证**
|
||||
|
||||
```bash
|
||||
# 检查容器状态
|
||||
docker compose ps agent-memory
|
||||
|
||||
# 检查记忆服务健康状态
|
||||
curl http://127.0.0.1:3004/health
|
||||
|
||||
# 通过 Nginx 代理访问(配置 Nginx 后)
|
||||
curl https://<你的域名>/ams-api/health
|
||||
```
|
||||
|
||||
> **注意**:记忆服务默认不启动,使用 `docker compose --profile memory up -d` 显式启用。若不需要 Agent 记忆功能,保持 `docker compose up -d` 即可,主服务会正常运行(记忆相关能力不可用)。
|
||||
|
||||
---
|
||||
|
||||
### 2026.3.2
|
||||
|
||||
1. **新增 Agent 服务治理(ASG)模块**:新增 `easyai-asg`(Agent 服务治理)容器和独立的 `easyai-asg-pg`(PostgreSQL 18)数据库容器,用于支持 Agent 自动化治理能力。
|
||||
1. **新增 Agent 服务治理(ASG)模块**:新增 `easyai-asg`(Agent 服务治理)容器和独立的 `easyai-pgvector`(PostgreSQL 18)数据库容器,用于支持 Agent 自动化治理能力。
|
||||
2. **新增 Nginx 反向代理**:在 `easyai-proxy.conf.sample` 中新增 `/asg-api/` 路径代理,用于暴露 ASG 服务的 REST API。
|
||||
3. **新增环境变量文件**:新增 `.env.ASG.sample` 文件,包含 ASG 服务所需的全部环境变量。
|
||||
4. 主服务 `comfy-server` 新增 `ASG_TCP_HOST` 和 `ASG_TCP_PORT` 环境变量,用于内部 TCP 微服务通信。
|
||||
@ -41,7 +135,7 @@ cp .env.ASG.sample .env.ASG
|
||||
根据实际环境修改 `.env.ASG` 中的关键配置:
|
||||
```dotenv
|
||||
# PostgreSQL 连接(默认使用容器内网地址,一般无需修改)
|
||||
ASG_DATABASE_URL=postgresql://easyai:easyai2025@easyai-asg-pg:5432/agent_governance?schema=public
|
||||
ASG_DATABASE_URL=postgresql://easyai:easyai2025@easyai-pgvector:5432/agent_governance?schema=public
|
||||
ASG_POSTGRES_USER=easyai
|
||||
ASG_POSTGRES_PASSWORD=easyai2025
|
||||
|
||||
@ -85,13 +179,13 @@ cd ~/easyai
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
新增容器:`easyai-asg-pg`(PostgreSQL)和 `easyai-asg`(Agent 服务治理),数据库会在首次启动时自动完成初始化和迁移。
|
||||
新增容器:`easyai-pgvector`(PostgreSQL)和 `easyai-asg`(Agent 服务治理),数据库会在首次启动时自动完成初始化和迁移。
|
||||
|
||||
**步骤五:验证**
|
||||
|
||||
```bash
|
||||
# 检查容器状态
|
||||
docker compose ps easyai-asg-pg easyai-asg
|
||||
docker compose ps easyai-pgvector easyai-asg
|
||||
|
||||
# 检查 ASG 服务健康状态
|
||||
curl http://127.0.0.1:3003/health
|
||||
@ -247,6 +341,46 @@ chmod +x start.sh
|
||||
```
|
||||
6. 脚本运行完成无错误,并且提示`EasyAI应用启动成功`表示应用启动成功,打开浏览器输入服务器的公网ip:3010或者局域网IP:3010,即可访问EasyAI应用
|
||||
|
||||
## Windows 一键启动
|
||||
|
||||
start.ps1 脚本用于 Windows 本机/局域网一键部署 EasyAI 应用。**不要直接双击运行**(会闪退),请按以下方式执行:
|
||||
|
||||
### 权限配置(首次执行必做)
|
||||
|
||||
PowerShell 默认可能禁止运行脚本,需先执行以下任一方式:
|
||||
|
||||
**方式一:临时允许本次运行(推荐)**
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||
```
|
||||
|
||||
**方式二:为当前用户永久放开脚本执行权限**
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
执行后再运行 `.\start.ps1` 即可。
|
||||
|
||||
### 启动步骤
|
||||
|
||||
1. 克隆并进入目录
|
||||
```powershell
|
||||
git clone https://git.51easyai.com/wangbo/easyai.git
|
||||
cd easyai
|
||||
```
|
||||
|
||||
2. 在 PowerShell 或 CMD 中运行(二选一)
|
||||
```powershell
|
||||
# 若已设置 ExecutionPolicy,可直接执行
|
||||
.\start.ps1
|
||||
|
||||
# 未设置时使用 Bypass 方式
|
||||
powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||
```
|
||||
|
||||
3. 按提示选择 [1] 本地访问 或 [2] 局域网访问,脚本将自动配置并启动 Docker 服务。
|
||||
|
||||
4. 若窗口闪退,可查看日志 `start.ps1.log`(与 start.ps1 同目录,或 `%TEMP%\start.ps1.log`)获取退出原因。
|
||||
|
||||
### 启用HTTPS
|
||||
1. [更改为你的域名]修改`easyai-proxy.conf`中域名`51easyai.com`为你的域名[可以使用Ctrl+F批量替换51easyai.com为你的域名]
|
||||
2. [修改.env文件]修改如下两个环境变量为如下的对应的值
|
||||
@ -269,7 +403,8 @@ chmod +x https.sh
|
||||
|
||||
### 更新升级
|
||||
update.sh 脚本用于自动更新 EasyAI 应用,包含以下功能:
|
||||
- 自动检查和更新 `docker-compose.yml` 文件(从远程仓库获取最新版本)
|
||||
- **拉取整个仓库**:执行 `git pull` 获取最新代码(docker-compose.yml、start.sh、.env.*.sample 等全部文件)
|
||||
- 自动补齐缺失的环境配置文件(.env、.env.tools、.env.ASG、.env.AMS,从 .sample 生成且不覆盖已有文件)
|
||||
- 兼容 `docker compose` 和 `docker-compose` 两种命令格式
|
||||
- 自动拉取最新镜像并重启服务
|
||||
|
||||
@ -279,32 +414,32 @@ update.sh 脚本用于自动更新 EasyAI 应用,包含以下功能:
|
||||
chmod +x update.sh
|
||||
```
|
||||
|
||||
2. 执行更新(默认会检查并更新 docker-compose.yml)
|
||||
2. 执行更新(默认会 `git pull` 拉取整个仓库)
|
||||
```bash
|
||||
./update.sh
|
||||
```
|
||||
|
||||
#### 参数选项
|
||||
- **跳过 docker-compose.yml 更新**:如果你已经手动修改了 `docker-compose.yml` 文件,可以使用 `-s` 或 `--skip-compose-update` 参数跳过更新
|
||||
```bash
|
||||
# 跳过 docker-compose.yml 更新,仅更新容器镜像
|
||||
./update.sh -s
|
||||
# 或
|
||||
./update.sh --skip-compose-update
|
||||
```
|
||||
> **注意**:update.sh 需要在 Git 克隆的目录下运行。若通过 zip 下载而非 git clone,请先使用 `git clone` 获取项目。
|
||||
|
||||
- **查看帮助信息**:
|
||||
```bash
|
||||
./update.sh -h
|
||||
# 或
|
||||
./update.sh --help
|
||||
```
|
||||
#### 使用方式
|
||||
- 执行 `./update.sh` 后会**命令行内选择**更新方式:
|
||||
- `[1]` 更新并拉取仓库(git pull)+ 更新镜像并重启(**默认**,回车即选)
|
||||
- `[2]` 仅更新镜像并重启(跳过 git pull,适用于有本地修改不想被覆盖的场景)
|
||||
|
||||
- **查看帮助**:`./update.sh -h` 或 `./update.sh --help`
|
||||
|
||||
#### 更新说明
|
||||
- 脚本会自动从远程仓库下载最新的 `docker-compose.yml` 文件
|
||||
- 如果本地文件与远程文件不同,会将原文件备份为 `docker-compose.yml.bak`
|
||||
- 如果本地文件已是最新版本,则跳过更新
|
||||
- 更新完成后会自动执行 `docker-compose pull` 和 `docker-compose up -d` 来重启服务
|
||||
- 脚本会执行 `git pull` 拉取整个仓库最新代码
|
||||
- 拉取后会检查并补齐缺失的 .env、.env.tools、.env.ASG、.env.AMS(不会覆盖已有文件)
|
||||
- 最后执行 `docker compose pull` 和 `docker compose up -d` 拉取镜像并重启服务
|
||||
|
||||
#### Windows 用户(update.ps1)
|
||||
Windows 下使用 `update.ps1`,功能与 Linux 版一致:
|
||||
```powershell
|
||||
.\update.ps1
|
||||
```
|
||||
- 执行后会**命令行内选择**:`[1]` 更新并拉取仓库 + 更新镜像(默认);`[2]` 仅更新镜像
|
||||
- 需在 Git 克隆的 easyai 目录下运行
|
||||
|
||||
|
||||
|
||||
|
||||
43
design.md
Normal file
43
design.md
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
## 部署配置,通过问答让用户选择
|
||||
|
||||
1. 通过IP地址还是通过域名访问?
|
||||
|
||||
2. (如果通过IP地址访问),输入服务器IP,并保证3001,3002,3003三个端口已经开放
|
||||
|
||||
3. 如果通过域名访问输入域名,不含https://的前缀,例如:51easyai.com
|
||||
|
||||
3.1 是否启用https访问
|
||||
|
||||
4. 对于IP地址访问的情形,复制`.env.sample`为`.env`,并将NUXT_PUBLIC_BASE_APIURL、NUXT_PUBLIC_BASE_SOCKETURL、NUXT_PUBLIC_SG_APIURL三个分别进行如下设置
|
||||
|
||||
```bash
|
||||
NUXT_PUBLIC_BASE_APIURL=http://<用户输入的IP地址>:3001
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=ws://<用户输入的IP地址>:3002
|
||||
NUXT_PUBLIC_SG_APIURL=http://<用户输入的IP地址>:3003
|
||||
```
|
||||
5. 对于使用域名的情况下情况,复制`.env.sampla`为`.env`,将上述3个变量设置为:
|
||||
```bash
|
||||
NUXT_PUBLIC_BASE_APIURL=/api
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=wss://<用户输入的域名>/socket.io
|
||||
NUXT_PUBLIC_SG_APIURL=/asg-api
|
||||
```
|
||||
|
||||
6. 复制 `.env.tools.sample`为`.env.tools`,复制`.env.ASG.sample`为`.env.ASG.sample`
|
||||
|
||||
7. 对于使用域名的情况下,将`easyai-proxy.conf.sample`复制为`easyai-proxy.conf`,并将文件名修改为`用户输入的域名.conf`,并将`51easyai.com`替换为用户的域名
|
||||
|
||||
8. 执行原来的start脚本内容,包括安装docker安装和部署
|
||||
|
||||
9. 如果启用https访问,还要同步执行原来的https脚本
|
||||
|
||||
|
||||
## 平台要求,需要兼容主流的linux云平台
|
||||
|
||||
## 部署要求
|
||||
|
||||
直接通过一个命令访问,包含自动从https://git.51easyai.com/wangbo/easyai克隆项目
|
||||
|
||||
```
|
||||
bash -c https://git.51easyai.com/wangbo/easyai/src/branch/main/start.sh
|
||||
```
|
||||
@ -3,7 +3,7 @@
|
||||
#version: '3'
|
||||
services:
|
||||
comfyAI-web:
|
||||
container_name: comfyAI-web
|
||||
container_name: easyai-web
|
||||
#更新修改冒号后面的版本号
|
||||
image: registry.cn-shanghai.aliyuncs.com/comfy-ai/one-ai:${VERSION}
|
||||
#端口冲突时,只需要修改前面的端口比如修改为,3011:3010
|
||||
@ -47,7 +47,7 @@ services:
|
||||
reservations:
|
||||
memory: 600MB
|
||||
comfy-server:
|
||||
container_name: comfy-server
|
||||
container_name: easyai-server
|
||||
# 阿里云镜像地址
|
||||
image: registry.cn-shanghai.aliyuncs.com/comfy-ai/comfy-server:${VERSION} # 阿里云
|
||||
labels:
|
||||
@ -108,6 +108,9 @@ services:
|
||||
# 服务治理 TCP 连接
|
||||
- ASG_TCP_HOST=easyai-asg
|
||||
- ASG_TCP_PORT=4003
|
||||
# Agent 记忆服务 TCP 连接(来自 .env)
|
||||
- MEMORY_TCP_HOST=${MEMORY_TCP_HOST:-agent-memory}
|
||||
- MEMORY_TCP_PORT=${MEMORY_TCP_PORT:-4004}
|
||||
# 日志大小设置,避免日志文件过大
|
||||
env_file:
|
||||
- .env
|
||||
@ -204,8 +207,11 @@ services:
|
||||
max-size: "100m"
|
||||
max-file: "10"
|
||||
rabbitmq:
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/mq:latest #阿里云镜像加速
|
||||
# image: rabbitmq:4-management #官方原版镜像
|
||||
# image: rabbitmq:4-management # 官方地址,需可访问外网
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/mq:latest # 阿里云镜像
|
||||
# image: docker.m.daocloud.io/library/rabbitmq:4-management # 国内网络可访问
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
container_name: rabbitmq
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@ -244,25 +250,36 @@ services:
|
||||
video-edit:
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/videoedit:latest
|
||||
container_name: video-edit
|
||||
platform: linux/amd64
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
- 'com.centurylinklabs.watchtower.enable=true'
|
||||
volumes:
|
||||
- ./data/videoedit/temp:/app/temp
|
||||
ports:
|
||||
- "${VIDEO_EDIT_PORT}:8000"
|
||||
- '${VIDEO_EDIT_PORT}:8000'
|
||||
env_file:
|
||||
- .env.tools
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- OMP_NUM_THREADS=4
|
||||
- OPENBLAS_NUM_THREADS=4
|
||||
env_file:
|
||||
- .env.tools
|
||||
shm_size: 2g
|
||||
ipc: host
|
||||
shm_size: 3g
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
init: true
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
comfyai:
|
||||
ipv4_address: 172.21.0.10
|
||||
healthcheck:
|
||||
test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')" ]
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'python',
|
||||
'-c',
|
||||
"import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@ -279,6 +296,12 @@ services:
|
||||
- ./data:/data
|
||||
ports:
|
||||
- 8080:8080
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "3"
|
||||
sandbox:
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/sandbox:latest
|
||||
container_name: sandbox
|
||||
@ -330,16 +353,19 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
easyai-asg-pg:
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/postgres:18-alpine
|
||||
container_name: easyai-asg-pg
|
||||
easyai-pgvector:
|
||||
# 使用带 pgvector 的镜像,供 ASG 与 Agent 记忆服务共用
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/pgvector:0.8.2-pg18-trixie
|
||||
container_name: easyai-pgvector
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${ASG_POSTGRES_USER:-easyai}
|
||||
POSTGRES_PASSWORD: ${ASG_POSTGRES_PASSWORD:-easyai2025}
|
||||
POSTGRES_DB: ${ASG_POSTGRES_DB:-agent_governance}
|
||||
volumes:
|
||||
- asg_postgres_data:/var/lib/postgresql/data
|
||||
- asg_postgres_data:/var/lib/postgresql
|
||||
# 首次启动时执行,创建 pgvector 扩展供记忆服务使用
|
||||
- ./docker/postgres/init-pgvector.sql:/docker-entrypoint-initdb.d/02-init-pgvector.sql
|
||||
networks:
|
||||
comfyai:
|
||||
ipv4_address: 172.21.0.13
|
||||
@ -365,7 +391,7 @@ services:
|
||||
comfyai:
|
||||
ipv4_address: 172.21.0.14
|
||||
depends_on:
|
||||
easyai-asg-pg:
|
||||
easyai-pgvector:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
@ -385,6 +411,38 @@ services:
|
||||
memory: 512MB
|
||||
reservations:
|
||||
memory: 128MB
|
||||
agent-memory:
|
||||
container_name: agent-memory
|
||||
image: registry.cn-shanghai.aliyuncs.com/easyaigc/agent-memory:${AMS_VERSION:-latest}
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
ports:
|
||||
- "${AMS_PORT:-3004}:3004"
|
||||
environment:
|
||||
- MEMORY_DATABASE_URL=${MEMORY_DATABASE_URL:-postgresql://easyai:easyai2025@easyai-pgvector:5432/easyai_memory}
|
||||
networks:
|
||||
comfyai:
|
||||
ipv4_address: 172.21.0.16
|
||||
depends_on:
|
||||
easyai-pgvector:
|
||||
condition: service_healthy
|
||||
comfy-server:
|
||||
condition: service_started
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.AMS
|
||||
- .env.ASG
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "10"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512MB
|
||||
reservations:
|
||||
memory: 128MB
|
||||
# portainer:
|
||||
# image: registry.cn-shanghai.aliyuncs.com/comfy-ai/portainer-ce:2.21.5
|
||||
# container_name: portainer
|
||||
|
||||
14
docker/postgres/init-pgvector.sql
Normal file
14
docker/postgres/init-pgvector.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- 首次启动时执行,与 ASG 共用同一 PostgreSQL 实例
|
||||
-- 1. 为 agent_governance 启用 pgvector(兼容共库场景)
|
||||
\c agent_governance
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
-- 2. 创建记忆服务独立数据库 easyai_memory
|
||||
CREATE DATABASE easyai_memory;
|
||||
GRANT ALL PRIVILEGES ON DATABASE easyai_memory TO easyai;
|
||||
|
||||
-- 3. 为 easyai_memory 启用 pgvector
|
||||
\c easyai_memory
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
33
docker/verify/Dockerfile
Normal file
33
docker/verify/Dockerfile
Normal file
@ -0,0 +1,33 @@
|
||||
# EasyAI 部署脚本验证环境
|
||||
# 在 Docker 容器内运行 start.sh,通过挂载 Docker Socket 使用宿主机 Docker 启动服务
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
lsb-release \
|
||||
git \
|
||||
sudo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装 Docker CLI(使用宿主机 Docker 守护进程)
|
||||
RUN install -m 0755 -d /etc/apt/keyrings \
|
||||
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
|
||||
&& chmod a+r /etc/apt/keyrings/docker.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 创建工作目录
|
||||
WORKDIR /workspace/easyai
|
||||
|
||||
# 复制项目文件(运行时通过 volume 挂载覆盖)
|
||||
COPY . /workspace/easyai/
|
||||
|
||||
# 允许以 root 运行(容器内通常为 root)
|
||||
ENV DEPLOY_ACCESS=ip
|
||||
ENV DEPLOY_IP=127.0.0.1
|
||||
24
docker/verify/run-verify-quick.sh
Executable file
24
docker/verify/run-verify-quick.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
# 快速验证:仅测试配置生成,不拉取镜像、不启动服务
|
||||
# 适合 CI 或快速检查脚本逻辑
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo "================================"
|
||||
echo " EasyAI 部署脚本快速验证"
|
||||
echo " (仅配置,不启动 Docker)"
|
||||
echo "================================"
|
||||
|
||||
# 使用管道模拟用户输入:1=IP模式, 127.0.0.1=IP地址
|
||||
printf '1\n127.0.0.1\n' | DEPLOY_DRY_RUN=1 DEPLOY_FORCE_RECONFIG=1 ./start.sh
|
||||
|
||||
echo ""
|
||||
echo "✅ 配置验证通过,检查生成的文件:"
|
||||
ls -la .env .env.tools .env.ASG .env.AMS 2>/dev/null || true
|
||||
echo ""
|
||||
grep -E "NUXT_PUBLIC_(BASE_APIURL|BASE_SOCKETURL|SG_APIURL)" .env 2>/dev/null | head -3
|
||||
46
docker/verify/run-verify.sh
Executable file
46
docker/verify/run-verify.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# 在 Docker 容器中验证 EasyAI 部署脚本
|
||||
# 用法: 在 easyai 项目根目录执行 ./docker/verify/run-verify.sh
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo "================================"
|
||||
echo " EasyAI 部署脚本 Docker 验证"
|
||||
echo "================================"
|
||||
echo "项目目录: $PROJECT_ROOT"
|
||||
echo ""
|
||||
|
||||
# 构建验证镜像
|
||||
echo "📦 构建验证镜像..."
|
||||
docker build -f docker/verify/Dockerfile -t easyai-deploy-verify:latest .
|
||||
|
||||
echo ""
|
||||
echo "🚀 运行部署脚本(非交互模式,挂载 Docker Socket)..."
|
||||
echo " 使用 DEPLOY_ACCESS=ip DEPLOY_IP=127.0.0.1"
|
||||
echo ""
|
||||
|
||||
# 挂载 Docker Socket,使容器内 docker 命令使用宿主机 Docker
|
||||
# 挂载项目目录,使用本地文件(避免容器内复制过时)
|
||||
docker run --rm -it \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v "$PROJECT_ROOT:/workspace/easyai" \
|
||||
-w /workspace/easyai \
|
||||
-e DEPLOY_ACCESS=ip \
|
||||
-e DEPLOY_IP=127.0.0.1 \
|
||||
-e DEPLOY_FORCE_RECONFIG=1 \
|
||||
easyai-deploy-verify:latest \
|
||||
bash -c './start.sh'
|
||||
|
||||
EXIT_CODE=$?
|
||||
echo ""
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✅ 部署脚本验证通过"
|
||||
else
|
||||
echo "❌ 部署脚本验证失败 (exit code: $EXIT_CODE)"
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
199
docs/Windows一键部署方案.md
Normal file
199
docs/Windows一键部署方案.md
Normal file
@ -0,0 +1,199 @@
|
||||
# EasyAI Windows 一键部署方案
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
`start.sh` 是 Linux 下的一键部署脚本,本方案为 Windows 平台提供等效的 `start.ps1` PowerShell 脚本。针对 Windows 典型场景做如下**精简**:
|
||||
|
||||
- **仅 IP 访问**:不含域名模式与 HTTPS
|
||||
- **本地访问无需放行端口**:选择本地 (127.0.0.1) 时,不涉及防火墙配置
|
||||
- **Docker 未安装时**:用户可选择手动或自动安装 **Docker Desktop for Windows**(winget/Chocolatey)
|
||||
|
||||
## 2. Linux start.sh 流程梳理
|
||||
|
||||
### 2.1 整体流程
|
||||
|
||||
| 步骤 | 功能 | 说明 |
|
||||
|------|------|------|
|
||||
| 1 | 项目初始化 | 校验当前目录下存在 `docker-compose.yml` |
|
||||
| 2 | 部署配置问答 | IP 或域名二选一,并采集对应参数 |
|
||||
| 3 | 配置文件生成 | 生成/更新 `.env`、`.env.tools`、`.env.ASG` |
|
||||
| 4 | Docker 安装与检查 | 检测并安装 Docker(Ubuntu/CentOS) |
|
||||
| 5 | 启动服务 | `docker compose pull && docker compose up -d` |
|
||||
| 6 | HTTPS 配置(可选) | 域名模式下执行 `https.sh` |
|
||||
|
||||
### 2.2 配置问答逻辑
|
||||
|
||||
- **IP 模式**:输入服务器 IP 地址,需要放行 3001、3002、3003 端口
|
||||
- **域名模式**:输入域名,可选是否启用 HTTPS,需放行 80、443 端口
|
||||
|
||||
### 2.3 环境变量写入 .env
|
||||
|
||||
- `NUXT_PUBLIC_BASE_APIURL`:API 地址
|
||||
- `NUXT_PUBLIC_BASE_SOCKETURL`:WebSocket 地址
|
||||
- `NUXT_PUBLIC_SG_APIURL`:Agent 服务治理 API 地址
|
||||
|
||||
**IP 模式**:
|
||||
```
|
||||
NUXT_PUBLIC_BASE_APIURL=http://<IP>:3001
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=ws://<IP>:3002
|
||||
NUXT_PUBLIC_SG_APIURL=http://<IP>:3003
|
||||
```
|
||||
|
||||
**域名模式**:
|
||||
```
|
||||
NUXT_PUBLIC_BASE_APIURL=/api
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=wss://<domain>/socket.io
|
||||
NUXT_PUBLIC_SG_APIURL=/asg-api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Windows 与 Linux 差异
|
||||
|
||||
| 项目 | Linux | Windows |
|
||||
|------|-------|---------|
|
||||
| 脚本语言 | Bash | PowerShell |
|
||||
| 文本替换 | sed | `(Get-Content) -replace` 或 `Set-Content` |
|
||||
| 用户输入 | `read -p` | `Read-Host` |
|
||||
| 访问方式 | IP + 域名 + HTTPS | **仅 IP**(本地 / 局域网) |
|
||||
| 端口放行 | 本地无特殊说明 | **本地访问无需放行**,局域网需放行 3001/3002/3003 |
|
||||
| Docker | apt/yum 安装 Linux Docker | **Docker Desktop for Windows**,未安装时可选择手动或自动安装 |
|
||||
|
||||
---
|
||||
|
||||
## 4. Windows IP 访问方式设计(核心差异)
|
||||
|
||||
Windows 版**仅支持 IP 访问**,不包含域名模式与 HTTPS 配置。用户访问方式分为两类:
|
||||
|
||||
| 选项 | 访问方式 | IP 值 | 端口说明 |
|
||||
|------|----------|-------|----------|
|
||||
| 1 | 本地访问 | `127.0.0.1` | 本机访问,**无需放行端口** |
|
||||
| 2 | 局域网访问 | 用户输入本机局域网 IP | 同网段设备访问,需放行 3001、3002、3003 |
|
||||
|
||||
**交互流程:**
|
||||
|
||||
1. `[1] 本地访问` → 自动使用 `127.0.0.1`,无需配置防火墙
|
||||
2. `[2] 局域网访问` → 提示用户输入局域网 IP(可提示 `ipconfig` 查看),并提醒放行上述端口
|
||||
|
||||
---
|
||||
|
||||
## 5. 实现方案
|
||||
|
||||
### 5.1 脚本文件
|
||||
|
||||
- 文件路径:`easyai/start.ps1`
|
||||
- 一行命令示例:
|
||||
```powershell
|
||||
git clone https://git.51easyai.com/wangbo/easyai; cd easyai; .\start.ps1
|
||||
```
|
||||
|
||||
### 5.2 模块划分
|
||||
|
||||
| 模块 | 函数/区块 | 功能 |
|
||||
|------|-----------|------|
|
||||
| 项目初始化 | `Init-ProjectDir` | 检查 `docker-compose.yml`,切换到项目根目录 |
|
||||
| 配置问答 | `Run-DeployQuestions` | 本地访问 / 局域网访问选择(含 IP 输入) |
|
||||
| 环境变量 | `PromptOrEnv` | 支持环境变量覆盖,用于 CI/自动化 |
|
||||
| 配置文件 | `Setup-EnvFiles` | 复制 sample 并修改 `.env`、`.env.tools`、`.env.ASG` |
|
||||
| Docker 检查 | `Test-Docker` | 检测 `docker`,未安装则让用户选择**手动安装**或**自动安装** |
|
||||
| 启动服务 | `Start-Services` | `docker compose pull` 与 `docker compose up -d` |
|
||||
| 主流程 | `Main` | 串联上述步骤 |
|
||||
|
||||
### 5.3 环境变量支持(非交互模式)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `DEPLOY_IP` | 访问 IP(本地填 `127.0.0.1`,局域网填实际 IP) |
|
||||
| `DEPLOY_DRY_RUN` | 1 时只生成配置,不安装/启动 Docker |
|
||||
| `DEPLOY_FORCE_RECONFIG` | 非空时强制重新配置 |
|
||||
|
||||
### 5.4 配置文件修改实现(仅 IP 模式)
|
||||
|
||||
使用 PowerShell 替换 `.env` 中相关行:
|
||||
|
||||
```powershell
|
||||
$content = Get-Content .env -Raw -Encoding UTF8
|
||||
$content = $content -replace 'NUXT_PUBLIC_BASE_APIURL=.*', "NUXT_PUBLIC_BASE_APIURL=http://${DEPLOY_IP}:3001"
|
||||
$content = $content -replace 'NUXT_PUBLIC_BASE_SOCKETURL=.*', "NUXT_PUBLIC_BASE_SOCKETURL=ws://${DEPLOY_IP}:3002"
|
||||
$content = $content -replace 'NUXT_PUBLIC_SG_APIURL=.*', "NUXT_PUBLIC_SG_APIURL=http://${DEPLOY_IP}:3003"
|
||||
Set-Content .env -Value $content -Encoding UTF8 -NoNewline
|
||||
```
|
||||
|
||||
### 5.5 Docker 处理策略
|
||||
|
||||
- **目标**:检测并安装 **Docker Desktop for Windows**(Windows 版 Docker)
|
||||
- **检测**:执行 `docker --version` 或 `docker compose version`
|
||||
- **未安装时**: prompt 让用户选择
|
||||
- `[1] 手动安装`:输出安装说明及 Docker Desktop for Windows 下载链接,退出脚本
|
||||
- `[2] 自动安装`:通过 winget 或 Chocolatey 安装 Docker Desktop for Windows,安装后需用户重启终端/机器再继续
|
||||
|
||||
### 5.6 执行策略
|
||||
|
||||
PowerShell 默认可能禁止执行脚本,建议在文档中说明:
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
|
||||
或在脚本开头提示用户使用:
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 部署完成输出
|
||||
|
||||
成功部署后输出访问地址,例如:
|
||||
|
||||
- 本地访问:`http://127.0.0.1:3010`
|
||||
- 局域网访问:`http://<LAN_IP>:3010`
|
||||
|
||||
---
|
||||
|
||||
## 7. 实现清单
|
||||
|
||||
- [ ] 创建 `start.ps1` 主脚本
|
||||
- [ ] 实现 `Init-ProjectDir`:项目目录校验
|
||||
- [ ] 实现 `Run-DeployQuestions`:本地 / 局域网 IP 选择(无域名/HTTPS)
|
||||
- [ ] 实现 `Setup-EnvFiles`:配置文件生成与替换
|
||||
- [ ] 实现 `Test-Docker`:Docker 检测,未安装时 prompt「手动安装」或「自动安装」
|
||||
- [ ] 实现 `Install-Docker`:自动安装 **Docker Desktop for Windows**(winget / Chocolatey)
|
||||
- [ ] 实现 `Start-Services`:`docker compose` 启动
|
||||
- [ ] 实现 `Main`:主流程串联
|
||||
- [ ] 支持 `DEPLOY_DRY_RUN`、`DEPLOY_IP` 环境变量非交互模式
|
||||
- [x] 在 README 或文档中补充 Windows 部署说明与执行策略
|
||||
- [x] 创建 `scripts/test-start-ps1-env.py` 验证 .env 替换逻辑
|
||||
|
||||
---
|
||||
|
||||
## 8. Windows 测试说明
|
||||
|
||||
### 本地测试(需 Windows 或 WSL + PowerShell)
|
||||
|
||||
```powershell
|
||||
# 非交互 + 仅生成配置(不启动 Docker)
|
||||
$env:DEPLOY_DRY_RUN = "1"
|
||||
$env:DEPLOY_IP = "127.0.0.1"
|
||||
.\start.ps1
|
||||
|
||||
# 检查 .env 是否已正确写入
|
||||
Select-String -Path .env -Pattern "NUXT_PUBLIC_BASE"
|
||||
```
|
||||
|
||||
### 完整部署测试(需 Windows + Docker Desktop)
|
||||
|
||||
```powershell
|
||||
.\start.ps1
|
||||
# 按提示选择 [1] 本地访问 或 [2] 局域网访问
|
||||
# 若未安装 Docker,选择 [1] 手动 或 [2] 自动安装
|
||||
```
|
||||
|
||||
### 执行策略(若提示无法运行脚本)
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
# 或
|
||||
powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||
```
|
||||
@ -91,6 +91,19 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Agent 记忆服务 API(可选,用于健康检查或外部调用)
|
||||
location /ams-api/ {
|
||||
proxy_pass http://127.0.0.1:3004/;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 20M;
|
||||
proxy_redirect off;
|
||||
proxy_set_header X-Original-Prefix '/ams-api';
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location /plugins/ {
|
||||
proxy_pass http://127.0.0.1:3020/plugins/;
|
||||
proxy_redirect off;
|
||||
@ -144,4 +157,17 @@ server {
|
||||
proxy_buffering off; # 对于 WebSocket 连接禁用缓冲
|
||||
}
|
||||
|
||||
# 沙箱环境 API(脚本执行、下载、安装依赖等),需在 docker-compose 中取消 SANDBOX_PORT 映射
|
||||
location /sandbox/ {
|
||||
proxy_pass http://127.0.0.1:8081/;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 50M;
|
||||
proxy_redirect off;
|
||||
proxy_set_header X-Original-Prefix '/sandbox';
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
https.sh
11
https.sh
@ -104,7 +104,13 @@ else
|
||||
fi
|
||||
|
||||
echo "🚀 复制当前目录的配置文件到nginx配置文件目录"
|
||||
cp -r ./easyai-proxy.conf /etc/nginx/conf.d/
|
||||
# 支持 EASYAI_PROXY_CONF 指定配置文件(如 51easyai.com.conf)
|
||||
CONF_FILE="${EASYAI_PROXY_CONF:-easyai-proxy.conf}"
|
||||
if [ -f "./$CONF_FILE" ]; then
|
||||
cp "./$CONF_FILE" "/etc/nginx/conf.d/$CONF_FILE"
|
||||
else
|
||||
cp -r ./easyai-proxy.conf /etc/nginx/conf.d/ 2>/dev/null || { echo "❌ 未找到 nginx 配置文件"; exit 1; }
|
||||
fi
|
||||
echo "🚀 重载nginx"
|
||||
sudo nginx -s reload
|
||||
|
||||
@ -113,7 +119,8 @@ sudo nginx -s stop
|
||||
|
||||
echo "🚀 使用certbot 自动配置证书"
|
||||
# 从 Nginx 配置文件中提取所有域名
|
||||
DOMAINS=$(find /etc/nginx/conf.d/ -name "easyai-proxy.conf" -type f -exec grep "server_name" {} \; | \
|
||||
CONF_FILE="${EASYAI_PROXY_CONF:-easyai-proxy.conf}"
|
||||
DOMAINS=$(grep "server_name" /etc/nginx/conf.d/"$CONF_FILE" 2>/dev/null || find /etc/nginx/conf.d/ -name "easyai-proxy.conf" -exec grep "server_name" {} \; | \
|
||||
grep -v "#" | \
|
||||
awk '{for(i=2;i<=NF;i++) if($i!=";") print $i}' | \
|
||||
sed 's/;//g' | \
|
||||
|
||||
67
reset-docker-network.ps1
Normal file
67
reset-docker-network.ps1
Normal file
@ -0,0 +1,67 @@
|
||||
#Requires -RunAsAdministrator
|
||||
# WSL 与 Docker 网络配置重置脚本
|
||||
# 需以管理员身份运行
|
||||
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host " WSL & Docker 网络配置重置" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# 1. 关闭 WSL
|
||||
Write-Host "[1/5] 关闭 WSL..." -ForegroundColor Yellow
|
||||
wsl --shutdown
|
||||
Start-Sleep -Seconds 3
|
||||
Write-Host " 完成" -ForegroundColor Green
|
||||
|
||||
# 2. 重置 Windows 网络栈(可选,可能需重启)
|
||||
Write-Host "[2/5] 重置 Windows 网络栈..." -ForegroundColor Yellow
|
||||
try {
|
||||
netsh winsock reset 2>$null
|
||||
netsh int ip reset 2>$null
|
||||
Write-Host " 完成 (如有提示重启,建议执行)" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " 跳过: $($_.Exception.Message)" -ForegroundColor Gray
|
||||
}
|
||||
|
||||
# 3. 清理 Docker 网络
|
||||
Write-Host "[3/5] 清理 Docker 未使用网络..." -ForegroundColor Yellow
|
||||
$dockerOk = $false
|
||||
try {
|
||||
$null = docker info 2>&1
|
||||
$dockerOk = $true
|
||||
} catch { }
|
||||
if ($dockerOk) {
|
||||
docker network prune -f 2>$null
|
||||
Write-Host " 完成" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " 跳过 (Docker 未运行,请先启动 Docker Desktop)" -ForegroundColor Gray
|
||||
}
|
||||
|
||||
# 4. 清除 Docker 端口代理残留(若有)
|
||||
Write-Host "[4/5] 检查端口代理..." -ForegroundColor Yellow
|
||||
$proxies = netsh interface portproxy show all 2>$null
|
||||
if ($proxies -and $proxies -match "3010|3001|3002|3003") {
|
||||
Write-Host " 发现相关端口代理,请手动在管理员 CMD 执行:" -ForegroundColor Yellow
|
||||
Write-Host " netsh interface portproxy reset" -ForegroundColor White
|
||||
} else {
|
||||
Write-Host " 无异常端口代理" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 5. 提示
|
||||
Write-Host "[5/5] 下一步操作:" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host " 1. 启动 Docker Desktop(若已关闭)" -ForegroundColor White
|
||||
Write-Host " 2. 等待 Docker 完全就绪后,执行:" -ForegroundColor White
|
||||
Write-Host " cd D:\01一键部署包\easyai" -ForegroundColor Cyan
|
||||
Write-Host " docker compose up -d" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host " 3. 若端口仍不可访问,在 Docker Desktop 中:" -ForegroundColor White
|
||||
Write-Host " [设置] -> [Troubleshoot] -> [Reset to factory defaults]" -ForegroundColor Cyan
|
||||
Write-Host " (会清除所有容器/镜像,谨慎使用)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " 4. 若执行了 netsh winsock/int ip reset,建议重启电脑" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Read-Host "按 Enter 键退出"
|
||||
27
scripts/test-start-ps1-env.py
Normal file
27
scripts/test-start-ps1-env.py
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# 验证 start.ps1 的 .env 替换逻辑(与 PowerShell 等效)
|
||||
import re
|
||||
|
||||
DEPLOY_IP = "192.168.1.100"
|
||||
content = """NUXT_PUBLIC_BASE_APIURL=http://127.0.0.1:3001
|
||||
#NUXT_PUBLIC_BASE_APIURL=/api
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=ws://127.0.0.1:3002
|
||||
NUXT_PUBLIC_SG_APIURL=http://127.0.0.1:3003
|
||||
"""
|
||||
|
||||
content = re.sub(r'^NUXT_PUBLIC_BASE_APIURL=.*', f'NUXT_PUBLIC_BASE_APIURL=http://{DEPLOY_IP}:3001', content, flags=re.MULTILINE)
|
||||
content = re.sub(r'^NUXT_PUBLIC_BASE_SOCKETURL=.*', f'NUXT_PUBLIC_BASE_SOCKETURL=ws://{DEPLOY_IP}:3002', content, flags=re.MULTILINE)
|
||||
content = re.sub(r'^NUXT_PUBLIC_SG_APIURL=.*', f'NUXT_PUBLIC_SG_APIURL=http://{DEPLOY_IP}:3003', content, flags=re.MULTILINE)
|
||||
|
||||
expected = f"""NUXT_PUBLIC_BASE_APIURL=http://{DEPLOY_IP}:3001
|
||||
#NUXT_PUBLIC_BASE_APIURL=/api
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=ws://{DEPLOY_IP}:3002
|
||||
NUXT_PUBLIC_SG_APIURL=http://{DEPLOY_IP}:3003
|
||||
"""
|
||||
# 注释行不应被替换,检查第一行和第三行
|
||||
lines = content.split('\n')
|
||||
assert lines[0] == f'NUXT_PUBLIC_BASE_APIURL=http://{DEPLOY_IP}:3001', f"Got {lines[0]}"
|
||||
assert lines[2] == f'NUXT_PUBLIC_BASE_SOCKETURL=ws://{DEPLOY_IP}:3002', f"Got {lines[2]}"
|
||||
assert lines[3] == f'NUXT_PUBLIC_SG_APIURL=http://{DEPLOY_IP}:3003', f"Got {lines[3]}"
|
||||
assert lines[1] == '#NUXT_PUBLIC_BASE_APIURL=/api', f"Comment should not change: {lines[1]}"
|
||||
print("OK: .env replacement logic validated")
|
||||
440
start.ps1
Normal file
440
start.ps1
Normal file
@ -0,0 +1,440 @@
|
||||
#Requires -Version 5.1
|
||||
# EasyAI Windows 一键部署脚本
|
||||
# 仅支持 IP 访问(本地/局域网),不含域名与 HTTPS
|
||||
# 一行命令: git clone https://git.51easyai.com/wangbo/easyai; cd easyai; .\start.ps1
|
||||
|
||||
# 设置控制台编码为 UTF-8,确保中文正确显示
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$script:LogDir = $PSScriptRoot
|
||||
if (-not $script:LogDir) { $script:LogDir = $env:TEMP }
|
||||
$script:LogFile = Join-Path $script:LogDir "start.ps1.log"
|
||||
# 尽早写入启动标记,便于闪退时排查
|
||||
try { "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] === 脚本加载 ===" | Out-File -FilePath $script:LogFile -Append -Encoding utf8 } catch { }
|
||||
|
||||
function Write-Log {
|
||||
param([string]$Msg)
|
||||
try {
|
||||
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Msg"
|
||||
Add-Content -Path $script:LogFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
|
||||
} catch { }
|
||||
}
|
||||
|
||||
trap {
|
||||
$errMsg = $_.Exception.Message
|
||||
$errStack = if ($_.ScriptStackTrace) { " | 堆栈: $($_.ScriptStackTrace)" } else { "" }
|
||||
try {
|
||||
$logPath = $script:LogFile
|
||||
if (-not $logPath) { $logPath = Join-Path $env:TEMP "start.ps1.log" }
|
||||
$logLine = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 错误退出: $errMsg$errStack"
|
||||
Add-Content -Path $logPath -Value $logLine -Encoding UTF8 -ErrorAction SilentlyContinue
|
||||
} catch { }
|
||||
try {
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host " 发生错误,详见日志: $script:LogFile" -ForegroundColor Red
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host $errMsg -ForegroundColor Red
|
||||
if ($errStack) { Write-Host $errStack }
|
||||
} catch { }
|
||||
Wait-ForExit
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 结束时保持窗口不关闭,便于查看输出和 Debug(CI 环境自动跳过)
|
||||
function Wait-ForExit {
|
||||
if ($env:CI -eq "true") { return }
|
||||
Write-Host ""
|
||||
Read-Host "按 Enter 键退出"
|
||||
}
|
||||
|
||||
# 仅配置模式:DEPLOY_DRY_RUN=1 只生成配置文件,不执行 Docker 安装和启动
|
||||
$script:DeployDryRun = if ($env:DEPLOY_DRY_RUN -eq "1") { $true } else { $false }
|
||||
$script:DeployIP = ""
|
||||
$script:DeployModeSkip = $false
|
||||
|
||||
# Docker Desktop for Windows 下载链接
|
||||
$DockerDesktopUrl = "https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe"
|
||||
|
||||
function Write-Step { param($Msg) Write-Host $Msg }
|
||||
function Write-Ok { param($Msg) Write-Host " ✓ $Msg" -ForegroundColor Green }
|
||||
function Write-Err { param($Msg) Write-Host "❌ $Msg" -ForegroundColor Red; throw $Msg }
|
||||
function Write-Warn { param($Msg) Write-Host "⚠️ $Msg" -ForegroundColor Yellow }
|
||||
|
||||
# ==================== 项目初始化 ====================
|
||||
function Init-ProjectDir {
|
||||
$scriptDir = $PSScriptRoot
|
||||
if (-not $scriptDir) { throw "无法获取脚本所在目录。请在 easyai 目录下打开 PowerShell 执行: .\start.ps1" }
|
||||
$composePath = Join-Path $scriptDir "docker-compose.yml"
|
||||
if (Test-Path $composePath) {
|
||||
Set-Location $scriptDir
|
||||
Write-Step "📁 项目目录: $scriptDir"
|
||||
return
|
||||
}
|
||||
Write-Err "未找到 docker-compose.yml,请在 easyai 项目目录下运行 start.ps1"
|
||||
}
|
||||
|
||||
# ==================== 配置问答 ====================
|
||||
function Run-DeployQuestions {
|
||||
Write-Host ""
|
||||
Write-Host "================================"
|
||||
Write-Host " EasyAI 部署配置(Windows)"
|
||||
Write-Host "================================"
|
||||
Write-Host ""
|
||||
|
||||
# 非交互模式:环境变量 DEPLOY_IP 已设置
|
||||
if ($env:DEPLOY_IP) {
|
||||
$script:DeployIP = $env:DEPLOY_IP.Trim()
|
||||
Write-Step "使用环境变量: DEPLOY_IP=$($script:DeployIP)"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "请选择访问方式:"
|
||||
Write-Host " [1] 本地访问 (127.0.0.1) - 仅本机访问,无需放行端口"
|
||||
Write-Host " [2] 局域网访问 - 同网段设备访问,需放行 3001、3002、3003 端口"
|
||||
$choice = Read-Host "请选择 [1/2]"
|
||||
|
||||
switch ($choice) {
|
||||
"1" {
|
||||
$script:DeployIP = "127.0.0.1"
|
||||
Write-Step "已选择本地访问"
|
||||
}
|
||||
"2" {
|
||||
# 自动获取局域网 IP(排除回环和 APIPA 地址)
|
||||
$detectedIp = $null
|
||||
try {
|
||||
$addrs = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object {
|
||||
$_.InterfaceAlias -notlike "*Loopback*" -and
|
||||
$_.IPAddress -notmatch "^127\." -and
|
||||
$_.IPAddress -notmatch "^169\.254\."
|
||||
}
|
||||
$detectedIp = ($addrs | Sort-Object InterfaceIndex | Select-Object -First 1).IPAddress
|
||||
} catch { }
|
||||
if ($detectedIp) {
|
||||
$defaultHint = "回车使用 [$detectedIp]"
|
||||
Write-Host " 检测到局域网 IP: $detectedIp"
|
||||
$inputIp = Read-Host "请输入本机局域网 IP($defaultHint 或手动输入)"
|
||||
$inputIp = $inputIp.Trim()
|
||||
$script:DeployIP = if ([string]::IsNullOrWhiteSpace($inputIp)) { $detectedIp } else { $inputIp }
|
||||
} else {
|
||||
Write-Host " 提示: 未自动检测到局域网 IP,可在本机运行 ipconfig 查看"
|
||||
$lanIp = Read-Host "请输入本机局域网 IP 地址"
|
||||
$lanIp = $lanIp.Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($lanIp)) { Write-Err "IP 不能为空" }
|
||||
$script:DeployIP = $lanIp
|
||||
}
|
||||
Write-Warn "请确保防火墙已放行 3001、3002、3003 端口"
|
||||
}
|
||||
default {
|
||||
Write-Err "无效选择"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 生成配置文件 ====================
|
||||
function Setup-EnvFiles {
|
||||
Write-Host ""
|
||||
Write-Step "📝 配置环境文件..."
|
||||
|
||||
# 复制 .env.tools、.env.ASG、.env.AMS(无 example 后缀的从 .sample 生成)
|
||||
if (-not (Test-Path ".env.tools")) {
|
||||
Copy-Item ".env.tools.sample" ".env.tools"
|
||||
Write-Ok ".env.tools"
|
||||
}
|
||||
if (-not (Test-Path ".env.ASG")) {
|
||||
Copy-Item ".env.ASG.sample" ".env.ASG"
|
||||
Write-Ok ".env.ASG"
|
||||
}
|
||||
if (-not (Test-Path ".env.AMS")) {
|
||||
Copy-Item ".env.AMS.sample" ".env.AMS"
|
||||
Write-Ok ".env.AMS"
|
||||
}
|
||||
|
||||
# 配置 .env
|
||||
if (-not (Test-Path ".env")) {
|
||||
Copy-Item ".env.sample" ".env"
|
||||
}
|
||||
|
||||
$content = Get-Content ".env" -Raw -Encoding UTF8
|
||||
if (-not $content) { $content = "" }
|
||||
|
||||
$content = $content -replace '(?m)^NUXT_PUBLIC_BASE_APIURL=.*', "NUXT_PUBLIC_BASE_APIURL=http://$($script:DeployIP):3001"
|
||||
$content = $content -replace '(?m)^NUXT_PUBLIC_BASE_SOCKETURL=.*', "NUXT_PUBLIC_BASE_SOCKETURL=ws://$($script:DeployIP):3002"
|
||||
$content = $content -replace '(?m)^NUXT_PUBLIC_SG_APIURL=.*', "NUXT_PUBLIC_SG_APIURL=http://$($script:DeployIP):3003"
|
||||
|
||||
# 保持文件末尾换行
|
||||
if ($content -and -not $content.EndsWith("`n")) { $content += "`n" }
|
||||
$envPath = Join-Path (Get-Location) ".env"
|
||||
[System.IO.File]::WriteAllText($envPath, $content, [System.Text.UTF8Encoding]::new($false))
|
||||
Write-Ok ".env 已配置为 IP 模式 ($($script:DeployIP))"
|
||||
}
|
||||
|
||||
# ==================== Docker 检测 ====================
|
||||
function Test-DockerInstalled {
|
||||
$docker = Get-Command docker -ErrorAction SilentlyContinue
|
||||
if (-not $docker) { return $false }
|
||||
try {
|
||||
$null = & docker --version 2>&1
|
||||
return $true
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 检查 Docker 引擎是否已启动并可响应(带超时,避免 docker info 卡住)
|
||||
function Test-DockerRunning {
|
||||
param([int]$TimeoutSeconds = 10)
|
||||
try {
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.FileName = "docker"
|
||||
$psi.Arguments = "info"
|
||||
$psi.RedirectStandardOutput = $true
|
||||
$psi.RedirectStandardError = $true
|
||||
$psi.UseShellExecute = $false
|
||||
$psi.CreateNoWindow = $true
|
||||
$p = [System.Diagnostics.Process]::Start($psi)
|
||||
$exited = $p.WaitForExit($TimeoutSeconds * 1000)
|
||||
if (-not $exited) {
|
||||
try { $p.Kill() } catch { }
|
||||
return $false
|
||||
}
|
||||
return ($p.ExitCode -eq 0)
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 启动 Docker Desktop 并等待就绪
|
||||
function Start-DockerDesktopAndWait {
|
||||
param(
|
||||
[int]$MaxWaitSeconds = 120,
|
||||
[int]$CheckIntervalSeconds = 5
|
||||
)
|
||||
|
||||
Write-Step "Docker 未运行,正在启动 Docker Desktop..."
|
||||
$dockerDesktopPath = "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe"
|
||||
if (-not (Test-Path $dockerDesktopPath)) {
|
||||
$dockerDesktopPath = "${env:ProgramFiles(x86)}\Docker\Docker\Docker Desktop.exe"
|
||||
}
|
||||
if (-not (Test-Path $dockerDesktopPath)) {
|
||||
Write-Warn "未找到 Docker Desktop 可执行文件"
|
||||
Write-Host "请手动启动 Docker Desktop 后重新运行本脚本。"
|
||||
return $false
|
||||
}
|
||||
|
||||
# 尝试使用 docker desktop start(Docker Desktop 4.38+ 支持)
|
||||
$useCli = $false
|
||||
try {
|
||||
$null = & docker desktop start 2>&1
|
||||
$useCli = $true
|
||||
Write-Step "已通过 docker desktop start 发送启动命令"
|
||||
} catch {
|
||||
# 回退到直接启动进程
|
||||
}
|
||||
|
||||
if (-not $useCli) {
|
||||
Start-Process -FilePath $dockerDesktopPath -WindowStyle Hidden
|
||||
Write-Step "已启动 Docker Desktop 进程"
|
||||
}
|
||||
|
||||
Write-Host "等待 Docker 引擎就绪(最长 $MaxWaitSeconds 秒)..." -ForegroundColor Cyan
|
||||
$elapsed = 0
|
||||
while ($elapsed -lt $MaxWaitSeconds) {
|
||||
Start-Sleep -Seconds $CheckIntervalSeconds
|
||||
$elapsed += $CheckIntervalSeconds
|
||||
Write-Host " 已等待 $elapsed 秒..." -NoNewline
|
||||
if (Test-DockerRunning) {
|
||||
Write-Host " 完成" -ForegroundColor Green
|
||||
Write-Ok "Docker 已就绪"
|
||||
return $true
|
||||
}
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Warn "等待超时,Docker 可能仍在启动中"
|
||||
Write-Host "请稍后手动执行 docker info 确认,或重新运行本脚本。"
|
||||
return $false
|
||||
}
|
||||
|
||||
# 确保 Docker 已安装且运行,若未运行则尝试启动
|
||||
function Ensure-DockerRunning {
|
||||
if (-not (Test-DockerInstalled)) {
|
||||
return $false
|
||||
}
|
||||
if (Test-DockerRunning) {
|
||||
Write-Ok "Docker 引擎已运行"
|
||||
$v = docker --version 2>$null
|
||||
if ($v) { Write-Host " $v" }
|
||||
return $true
|
||||
}
|
||||
return (Start-DockerDesktopAndWait)
|
||||
}
|
||||
|
||||
function Install-DockerDesktop {
|
||||
$isWin = ($env:OS -eq "Windows_NT") -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)
|
||||
if (-not $isWin) {
|
||||
Write-Warn "自动安装仅支持 Windows。请手动安装 Docker Desktop。"
|
||||
Write-Host "下载地址: $DockerDesktopUrl"
|
||||
Wait-ForExit; exit 1
|
||||
}
|
||||
|
||||
# 优先 winget,其次 Chocolatey
|
||||
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
||||
Write-Step "正在通过 winget 安装 Docker Desktop for Windows..."
|
||||
winget install --id Docker.DockerDesktop -e --accept-source-agreements --accept-package-agreements
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Ok "安装已启动。安装完成后请重启终端,然后重新运行本脚本。"
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
if (Get-Command choco -ErrorAction SilentlyContinue) {
|
||||
Write-Step "正在通过 Chocolatey 安装 Docker Desktop for Windows..."
|
||||
choco install docker-desktop -y
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Ok "安装已启动。安装完成后请重启终端,然后重新运行本脚本。"
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
Write-Warn "未找到 winget 或 Chocolatey,请手动安装 Docker Desktop。"
|
||||
Write-Host "下载地址: $DockerDesktopUrl"
|
||||
Wait-ForExit; exit 1
|
||||
}
|
||||
|
||||
function Test-Docker {
|
||||
Write-Host ""
|
||||
Write-Host "================================"
|
||||
Write-Host " Docker 检查"
|
||||
Write-Host "================================"
|
||||
Write-Host ""
|
||||
|
||||
if (Test-DockerInstalled) {
|
||||
Write-Ok "Docker 已安装"
|
||||
# 检查并确保 Docker 引擎运行
|
||||
if (-not (Ensure-DockerRunning)) {
|
||||
Write-Warn "Docker 未能自动启动"
|
||||
Write-Host ""
|
||||
Write-Host "请手动启动 Docker Desktop,确认其完全启动后再重新运行本脚本。"
|
||||
Wait-ForExit
|
||||
exit 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Write-Warn "未检测到 Docker Desktop for Windows"
|
||||
Write-Host ""
|
||||
Write-Host "请选择:"
|
||||
Write-Host " [1] 手动安装 - 打开下载页面,退出脚本"
|
||||
Write-Host " [2] 自动安装 - 通过 winget 或 Chocolatey 安装(需管理员权限)"
|
||||
$choice = Read-Host "请选择 [1/2]"
|
||||
|
||||
switch ($choice) {
|
||||
"1" {
|
||||
Write-Host ""
|
||||
Write-Host "Docker Desktop for Windows 下载地址:"
|
||||
Write-Host " $DockerDesktopUrl"
|
||||
Write-Host ""
|
||||
Write-Host "安装完成后请重新运行本脚本。"
|
||||
if ($env:OS -eq "Windows_NT") {
|
||||
Start-Process $DockerDesktopUrl
|
||||
}
|
||||
Wait-ForExit; exit 1
|
||||
}
|
||||
"2" {
|
||||
Install-DockerDesktop
|
||||
}
|
||||
default {
|
||||
Write-Err "无效选择"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 启动服务 ====================
|
||||
function Start-Services {
|
||||
Write-Host ""
|
||||
Write-Step "🚀 启动 EasyAI 服务..."
|
||||
|
||||
$hasComposeV2 = $false
|
||||
try {
|
||||
$null = docker compose version 2>&1
|
||||
$hasComposeV2 = $true
|
||||
} catch { }
|
||||
|
||||
if ($hasComposeV2) {
|
||||
docker compose pull
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker compose pull 失败" }
|
||||
docker compose up -d
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker compose up 失败" }
|
||||
} else {
|
||||
docker-compose pull
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose pull 失败" }
|
||||
docker-compose up -d
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose up 失败" }
|
||||
}
|
||||
Write-Host "🎉 EasyAI 应用启动成功"
|
||||
}
|
||||
|
||||
# ==================== 主流程 ====================
|
||||
function Main {
|
||||
Init-ProjectDir
|
||||
|
||||
# 检查是否已有 .env 且非强制重新配置
|
||||
if ((Test-Path ".env") -and -not $env:DEPLOY_FORCE_RECONFIG -and -not $env:DEPLOY_IP) {
|
||||
Write-Step "📁 检测到已有 .env 配置"
|
||||
$reconfigure = Read-Host "是否重新配置访问方式?[y/N]"
|
||||
$trimmed = $reconfigure.Trim().ToLower()
|
||||
$startsWithY = ($trimmed.Length -gt 0) -and ($trimmed[0] -eq [char]121)
|
||||
if (-not $startsWithY) {
|
||||
Write-Step "使用现有配置继续..."
|
||||
# 从现有 .env 读取 IP(用于最后输出)
|
||||
$line = Get-Content ".env" -Encoding UTF8 | Where-Object { $_ -like "NUXT_PUBLIC_BASE_APIURL=*" } | Select-Object -First 1
|
||||
if ($line -and $line -like "*:3001*") {
|
||||
$prefix = ".*http://"
|
||||
$suffix = ":3001.*"
|
||||
$script:DeployIP = ($line -replace $prefix, "" -replace $suffix, "").Trim()
|
||||
}
|
||||
$script:DeployModeSkip = $true
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $script:DeployModeSkip) {
|
||||
Run-DeployQuestions
|
||||
}
|
||||
|
||||
if (-not $script:DeployModeSkip) {
|
||||
Setup-EnvFiles
|
||||
} else {
|
||||
if (-not (Test-Path ".env.tools")) { Copy-Item ".env.tools.sample" ".env.tools" }
|
||||
if (-not (Test-Path ".env.ASG")) { Copy-Item ".env.ASG.sample" ".env.ASG" }
|
||||
if (-not (Test-Path ".env.AMS")) { Copy-Item ".env.AMS.sample" ".env.AMS" }
|
||||
}
|
||||
|
||||
if ($script:DeployDryRun) {
|
||||
Write-Host ""
|
||||
Write-Warn "dry-run 模式:跳过 Docker 安装和服务启动"
|
||||
Write-Host " 配置文件已生成,可直接运行 .\start.ps1 完成部署"
|
||||
} else {
|
||||
Test-Docker
|
||||
Start-Services
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "================================"
|
||||
Write-Host " 部署完成"
|
||||
Write-Host "================================"
|
||||
$accessIp = if ($script:DeployIP) { $script:DeployIP } else { "127.0.0.1" }
|
||||
Write-Host "访问地址: http://${accessIp}:3010"
|
||||
Write-Host "默认登录账户: admin"
|
||||
Write-Host "默认登录密码: 123456"
|
||||
Write-Host ""
|
||||
Wait-ForExit
|
||||
}
|
||||
|
||||
Write-Log "=== 脚本启动 ==="
|
||||
Write-Host "EasyAI Windows 部署脚本启动中..." -ForegroundColor Cyan
|
||||
Write-Host "日志文件: $script:LogFile"
|
||||
Write-Host ""
|
||||
Main
|
||||
Write-Log "=== 脚本正常结束 ==="
|
||||
428
start.sh
428
start.sh
@ -1,186 +1,298 @@
|
||||
#!/bin/bash
|
||||
# EasyAI 一键部署脚本
|
||||
# 支持交互式问答配置,兼容 IP 与域名两种访问方式
|
||||
# 一行命令: git clone https://git.51easyai.com/wangbo/easyai && cd easyai && chmod +x ./start.sh && ./start.sh
|
||||
|
||||
set -e # 发生错误时终止脚本执行
|
||||
set -e
|
||||
|
||||
echo "==========================="
|
||||
echo "🚀 开始自动安装 Docker 和 Docker Compose"
|
||||
echo "==========================="
|
||||
# 仅配置模式(验证用):DEPLOY_DRY_RUN=1 只生成配置文件,不执行 Docker 安装和启动
|
||||
DEPLOY_DRY_RUN="${DEPLOY_DRY_RUN:-0}"
|
||||
|
||||
# 获取操作系统类型和版本
|
||||
OS_FAMILY=$(hostnamectl | grep "Operating System" | awk '{print $3}')
|
||||
OS_VERSION_ID=$(grep -oP '(?<=^VERSION_ID=")[0-9.]+' /etc/os-release | cut -d'.' -f1)
|
||||
OS_CODENAME=""
|
||||
if [[ "$OS_FAMILY" == "Ubuntu" ]]; then
|
||||
OS_CODENAME=$(lsb_release -cs)
|
||||
fi
|
||||
|
||||
# 定义国内镜像源
|
||||
# Ubuntu 镜像源
|
||||
UBUNTU_DOCKER_MIRROR_URL="https://mirrors.ustc.edu.cn/docker-ce" # 中科大
|
||||
UBUNTU_DOCKER_GPG_URL="https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg"
|
||||
|
||||
# CentOS 镜像源 (选择一个即可,这里提供清华和阿里云)
|
||||
CENTOS_DOCKER_MIRROR_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce" # 清华大学
|
||||
# CENTOS_DOCKER_MIRROR_URL="https://mirrors.aliyun.com/docker-ce" # 阿里云 - 如果清华源不稳定可以尝试这个
|
||||
|
||||
# 函数:检查并等待网络连接
|
||||
check_network() {
|
||||
echo "🌐 检查网络连接到 Docker 官方仓库 (备用)..."
|
||||
if curl -sSf https://download.docker.com/ &> /dev/null; then
|
||||
echo "✅ 网络连接到 Docker 官方仓库正常。"
|
||||
# ==================== 项目初始化 ====================
|
||||
init_project_dir() {
|
||||
local script_source
|
||||
script_source="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
||||
if [ -f "${script_source}/docker-compose.yml" ]; then
|
||||
echo "📁 项目目录: ${script_source}"
|
||||
cd "$script_source"
|
||||
return 0
|
||||
fi
|
||||
echo "❌ 未找到 docker-compose.yml,请在 easyai 项目目录下运行 start.sh"
|
||||
echo " 启动命令: git clone https://git.51easyai.com/wangbo/easyai && cd easyai && chmod +x ./start.sh && ./start.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ==================== 配置变量(支持环境变量非交互模式) ====================
|
||||
DEPLOY_MODE="" # ip | domain
|
||||
DEPLOY_IP=""
|
||||
DEPLOY_DOMAIN=""
|
||||
DEPLOY_HTTPS=false
|
||||
|
||||
prompt_or_env() {
|
||||
local var_name=$1
|
||||
local prompt_text=$2
|
||||
local env_name=$3
|
||||
local default=$4
|
||||
|
||||
if [ -n "${!env_name}" ]; then
|
||||
eval "$var_name=\"${!env_name}\""
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -n "$default" ]; then
|
||||
read -r -p "${prompt_text} [$default]: " input
|
||||
eval "$var_name=\"${input:-$default}\""
|
||||
else
|
||||
echo "⚠️ 无法连接到 Docker 官方仓库。将尝试使用国内镜像源。"
|
||||
# 即使无法连接官方源,也继续尝试国内源
|
||||
return 0
|
||||
read -r -p "${prompt_text}: " input
|
||||
eval "$var_name=\"$input\""
|
||||
fi
|
||||
}
|
||||
|
||||
# 预检网络连接(非阻塞,仅作为提示)
|
||||
check_network
|
||||
run_deploy_questions() {
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " EasyAI 部署配置(问答模式)"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
# Docker 安装
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "✅ Docker 已安装,跳过安装步骤"
|
||||
else
|
||||
if [[ "$OS_FAMILY" == "Ubuntu" ]]; then
|
||||
echo "📦 安装依赖 (Ubuntu)..."
|
||||
sudo apt update -y
|
||||
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
|
||||
|
||||
echo "🔑 添加 Docker GPG 密钥 (Ubuntu) - 优先使用国内镜像源..."
|
||||
# 现代 Ubuntu 推荐使用 gpg --dearmor
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
if ! curl -fsSL "$UBUNTU_DOCKER_GPG_URL" | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg; then
|
||||
echo "❌ 无法从国内镜像源获取 GPG 密钥,尝试从官方源获取..."
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg || {
|
||||
echo "❌ 无法获取 Docker GPG 密钥,请检查网络或 GPG 密钥 URL。"
|
||||
exit 1
|
||||
}
|
||||
# 非交互模式:环境变量已完整设置则直接使用(CI/自动化部署)
|
||||
if [ -n "$DEPLOY_ACCESS" ]; then
|
||||
if [ "$DEPLOY_ACCESS" = "ip" ] && [ -n "$DEPLOY_IP" ]; then
|
||||
DEPLOY_MODE="ip"
|
||||
echo "使用环境变量: IP 模式, IP=$DEPLOY_IP"
|
||||
return
|
||||
fi
|
||||
sudo chmod a+r /etc/apt/keyrings/docker.gpg # 确保可读权限
|
||||
|
||||
echo "🌍 添加 Docker 源 (Ubuntu) - 使用国内镜像源..."
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $UBUNTU_DOCKER_MIRROR_URL/linux/ubuntu \
|
||||
$OS_CODENAME stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update -y
|
||||
|
||||
echo "🐳 安装 Docker (Ubuntu)..."
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || {
|
||||
echo "❌ Docker 或其组件安装失败。请检查错误日志或尝试手动安装。"
|
||||
exit 1
|
||||
}
|
||||
|
||||
elif [[ "$OS_FAMILY" == "CentOS" ]]; then
|
||||
echo "📦 安装依赖 (CentOS)..."
|
||||
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
|
||||
|
||||
echo "🌍 添加 Docker 源 (CentOS) - 使用国内镜像源..."
|
||||
# 添加 Docker CE 官方 repo
|
||||
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||||
|
||||
# 替换为国内镜像源
|
||||
echo "🔄 替换 Docker 源为国内镜像 ($CENTOS_DOCKER_MIRROR_URL)..."
|
||||
sudo sed -i "s+https://download.docker.com+$CENTOS_DOCKER_MIRROR_URL+" /etc/yum.repos.d/docker-ce.repo || {
|
||||
echo "❌ 替换 Docker 源失败,可能 repo 文件路径不正确或无权限。"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# CentOS 8+ 可能会遇到 module 冲突,禁用默认的 container-tools 模块
|
||||
if [[ "$OS_VERSION_ID" -ge "8" ]]; then
|
||||
echo "⚙️ 禁用 CentOS 8+ 默认的 container-tools 模块以避免冲突..."
|
||||
sudo yum module disable -y container-tools
|
||||
sudo yum module enable -y container-tools:docker
|
||||
if [ "$DEPLOY_ACCESS" = "domain" ] && [ -n "$DEPLOY_DOMAIN" ]; then
|
||||
DEPLOY_MODE="domain"
|
||||
DEPLOY_HTTPS="${DEPLOY_HTTPS_INPUT:-false}"
|
||||
echo "使用环境变量: 域名模式, 域名=$DEPLOY_DOMAIN"
|
||||
return
|
||||
fi
|
||||
sudo yum makecache
|
||||
|
||||
echo "🐳 安装 Docker (CentOS)..."
|
||||
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || {
|
||||
echo "❌ Docker 或其组件安装失败。请检查错误日志或尝试手动安装。"
|
||||
echo "提示:如果仍遇到下载问题,请检查网络、防火墙或尝试切换另一个国内镜像源。"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# 1. IP 或域名访问
|
||||
if [ -z "$DEPLOY_ACCESS" ]; then
|
||||
echo "1. 通过 IP 地址还是域名访问?"
|
||||
echo " [1] IP 地址(需开放 3001、3002、3003 端口)"
|
||||
echo " [2] 域名"
|
||||
read -r -p "请选择 [1/2]: " choice
|
||||
case "$choice" in
|
||||
1) DEPLOY_MODE="ip" ;;
|
||||
2) DEPLOY_MODE="domain" ;;
|
||||
*) echo "❌ 无效选择"; exit 1 ;;
|
||||
esac
|
||||
else
|
||||
echo "❌ 未知操作系统 ($OS_FAMILY),无法安装 Docker。"
|
||||
DEPLOY_MODE="$DEPLOY_ACCESS"
|
||||
fi
|
||||
|
||||
if [ "$DEPLOY_MODE" = "ip" ]; then
|
||||
# 2. 输入服务器 IP
|
||||
prompt_or_env DEPLOY_IP "2. 请输入服务器 IP 地址" "DEPLOY_IP" ""
|
||||
[ -z "$DEPLOY_IP" ] && { echo "❌ IP 不能为空"; exit 1; }
|
||||
echo " 请确保防火墙已放行 3001、3002、3003 端口"
|
||||
else
|
||||
# 3. 输入域名
|
||||
prompt_or_env DEPLOY_DOMAIN "3. 请输入域名(不含 https:// 前缀,如 51easyai.com)" "DEPLOY_DOMAIN" ""
|
||||
[ -z "$DEPLOY_DOMAIN" ] && { echo "❌ 域名不能为空"; exit 1; }
|
||||
|
||||
# 3.1 是否启用 HTTPS
|
||||
if [ -n "$DEPLOY_HTTPS_INPUT" ]; then
|
||||
DEPLOY_HTTPS=$DEPLOY_HTTPS_INPUT
|
||||
else
|
||||
read -r -p "3.1 是否启用 HTTPS?[y/N]: " https_choice
|
||||
DEPLOY_HTTPS=false
|
||||
[[ "$https_choice" =~ ^[yY] ]] && DEPLOY_HTTPS=true
|
||||
fi
|
||||
[ "$DEPLOY_HTTPS" = true ] && echo " 启用 HTTPS 需确保防火墙已放行 80、443 端口"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==================== 生成配置文件 ====================
|
||||
setup_env_files() {
|
||||
echo ""
|
||||
echo "📝 配置环境文件..."
|
||||
|
||||
# 6. 复制 .env.tools、.env.ASG、.env.AMS(无 example 后缀的从 .sample 生成)
|
||||
[ ! -f .env.tools ] && cp .env.tools.sample .env.tools && echo " ✓ .env.tools"
|
||||
[ ! -f .env.ASG ] && cp .env.ASG.sample .env.ASG && echo " ✓ .env.ASG"
|
||||
[ ! -f .env.AMS ] && cp .env.AMS.sample .env.AMS && echo " ✓ .env.AMS"
|
||||
|
||||
# 4/5. 配置 .env
|
||||
if [ ! -f .env ]; then
|
||||
cp .env.sample .env
|
||||
fi
|
||||
|
||||
if [ "$DEPLOY_MODE" = "ip" ]; then
|
||||
# IP 模式
|
||||
sed -i.bak "s|^NUXT_PUBLIC_BASE_APIURL=.*|NUXT_PUBLIC_BASE_APIURL=http://${DEPLOY_IP}:3001|" .env
|
||||
sed -i.bak "s|^NUXT_PUBLIC_BASE_SOCKETURL=.*|NUXT_PUBLIC_BASE_SOCKETURL=ws://${DEPLOY_IP}:3002|" .env
|
||||
sed -i.bak "s|^NUXT_PUBLIC_SG_APIURL=.*|NUXT_PUBLIC_SG_APIURL=http://${DEPLOY_IP}:3003|" .env
|
||||
echo " ✓ .env 已配置为 IP 模式 (${DEPLOY_IP})"
|
||||
else
|
||||
# 域名模式
|
||||
sed -i.bak "s|^NUXT_PUBLIC_BASE_APIURL=.*|NUXT_PUBLIC_BASE_APIURL=/api|" .env
|
||||
sed -i.bak "s|^NUXT_PUBLIC_BASE_SOCKETURL=.*|NUXT_PUBLIC_BASE_SOCKETURL=wss://${DEPLOY_DOMAIN}/socket.io|" .env
|
||||
sed -i.bak "s|^NUXT_PUBLIC_SG_APIURL=.*|NUXT_PUBLIC_SG_APIURL=/asg-api|" .env
|
||||
echo " ✓ .env 已配置为域名模式 (${DEPLOY_DOMAIN})"
|
||||
|
||||
# 7. Nginx 配置(域名模式)
|
||||
PROXY_CONF="${DEPLOY_DOMAIN}.conf"
|
||||
if [ ! -f "$PROXY_CONF" ]; then
|
||||
sed "s/51easyai.com/${DEPLOY_DOMAIN}/g" easyai-proxy.conf.sample > "$PROXY_CONF"
|
||||
echo " ✓ Nginx 配置已生成: $PROXY_CONF"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f .env.bak
|
||||
}
|
||||
|
||||
# ==================== Docker 安装(复用原 start.sh 逻辑) ====================
|
||||
install_docker() {
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " Docker 安装与检查"
|
||||
echo "================================"
|
||||
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
case "${ID:-}" in
|
||||
ubuntu|debian) OS_FAMILY="Ubuntu" ;;
|
||||
centos|rhel|fedora) OS_FAMILY="CentOS" ;;
|
||||
*) OS_FAMILY="${ID:-Unknown}" ;;
|
||||
esac
|
||||
else
|
||||
OS_FAMILY=$(hostnamectl 2>/dev/null | grep "Operating System" | awk '{print $3}' || echo "Unknown")
|
||||
fi
|
||||
OS_VERSION_ID=$(grep -oP '(?<=^VERSION_ID=")[0-9.]+' /etc/os-release 2>/dev/null | cut -d'.' -f1 || echo "0")
|
||||
OS_CODENAME=""
|
||||
[[ "$OS_FAMILY" == "Ubuntu" ]] && OS_CODENAME=$(lsb_release -cs 2>/dev/null || (grep VERSION_CODENAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"'))
|
||||
|
||||
UBUNTU_DOCKER_MIRROR_URL="https://mirrors.ustc.edu.cn/docker-ce"
|
||||
UBUNTU_DOCKER_GPG_URL="https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg"
|
||||
CENTOS_DOCKER_MIRROR_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
|
||||
|
||||
check_network() {
|
||||
if curl -sSf --connect-timeout 5 https://download.docker.com/ &>/dev/null; then
|
||||
echo "✅ 网络连接正常"
|
||||
return 0
|
||||
fi
|
||||
echo "⚠️ 将使用国内镜像源"
|
||||
return 0
|
||||
}
|
||||
check_network
|
||||
|
||||
if command -v docker &>/dev/null; then
|
||||
echo "✅ Docker 已安装"
|
||||
else
|
||||
if [[ "$OS_FAMILY" == "Ubuntu" ]]; then
|
||||
sudo apt update -y
|
||||
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL "$UBUNTU_DOCKER_GPG_URL" | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null || \
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $UBUNTU_DOCKER_MIRROR_URL/linux/ubuntu $OS_CODENAME stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
elif [[ "$OS_FAMILY" == "CentOS" ]]; then
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||||
sudo sed -i "s+https://download.docker.com+$CENTOS_DOCKER_MIRROR_URL+g" /etc/yum.repos.d/docker-ce.repo
|
||||
[[ "$OS_VERSION_ID" -ge "8" ]] && sudo yum module disable -y container-tools 2>/dev/null || true
|
||||
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
else
|
||||
echo "❌ 不支持的操作系统: $OS_FAMILY"
|
||||
exit 1
|
||||
fi
|
||||
sudo systemctl enable docker
|
||||
sudo systemctl start docker
|
||||
getent group docker | grep -qw "$USER" || sudo usermod -aG docker "$USER"
|
||||
fi
|
||||
|
||||
if docker compose version &>/dev/null; then
|
||||
echo "✅ Docker Compose 已就绪"
|
||||
elif command -v docker-compose &>/dev/null; then
|
||||
echo "✅ Docker Compose (兼容模式) 已就绪"
|
||||
else
|
||||
echo "❌ 请安装 docker-compose-plugin"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "✅ 启动并设置 Docker 开机自启..."
|
||||
sudo systemctl enable docker
|
||||
sudo systemctl start docker
|
||||
|
||||
# 将当前用户添加到 docker 组,以便无需 sudo 运行 Docker 命令
|
||||
if ! getent group docker | grep -qw "$USER"; then
|
||||
echo "👥 将当前用户 ($USER) 添加到 docker 组..."
|
||||
sudo usermod -aG docker "$USER"
|
||||
echo "🔔 请注销并重新登录,以便更改生效。"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Docker Compose 检查和安装 (优先使用 docker-compose-plugin)
|
||||
if command -v docker &> /dev/null && docker compose version &> /dev/null; then
|
||||
echo "✅ Docker Compose (插件版) 已安装,跳过安装步骤"
|
||||
elif command -v docker-compose &> /dev/null && ! command -v docker &> /dev/null; then
|
||||
echo "⚠️ 检测到旧版 Docker Compose 独立安装,但未检测到 Docker 插件版。"
|
||||
echo "建议在安装 Docker CE 时一起安装 docker-compose-plugin。"
|
||||
else
|
||||
echo "⚙️ 安装 Docker Compose (插件版)..."
|
||||
PLUGIN_INSTALL_SUCCESS=1
|
||||
|
||||
# 如果 Docker CE 安装成功,docker-compose-plugin 应该已经安装了。
|
||||
# 这里是额外的检查,以防万一。
|
||||
if [[ "$OS_FAMILY" == "Ubuntu" ]]; then
|
||||
sudo apt-get install -y docker-compose-plugin || PLUGIN_INSTALL_SUCCESS=0
|
||||
elif [[ "$OS_FAMILY" == "CentOS" ]]; then
|
||||
sudo yum install -y docker-compose-plugin || PLUGIN_INSTALL_SUCCESS=0
|
||||
# ==================== 启动服务 ====================
|
||||
start_services() {
|
||||
echo ""
|
||||
echo "🚀 启动 EasyAI 服务..."
|
||||
if docker compose version &>/dev/null; then
|
||||
sudo docker compose pull && sudo docker compose up -d
|
||||
else
|
||||
echo "❌ 未知操作系统,无法安装 Docker Compose 插件版。"
|
||||
PLUGIN_INSTALL_SUCCESS=0
|
||||
sudo docker-compose pull && sudo docker-compose up -d
|
||||
fi
|
||||
echo "🎉 EasyAI 应用启动成功"
|
||||
}
|
||||
|
||||
# 检查插件版安装是否成功
|
||||
if [[ $PLUGIN_INSTALL_SUCCESS -eq 1 ]]; then
|
||||
echo "✅ Docker Compose 插件版安装成功"
|
||||
else
|
||||
echo "⚠️ Docker Compose 插件版安装失败,尝试使用本地二进制文件安装..."
|
||||
#将文件移动至/usr/bin目录下,并重命名
|
||||
sudo cp ./docker-compose-linux-x86_64 /usr/bin/docker-compose
|
||||
# 添加执行权限
|
||||
sudo chmod +x /usr/bin/docker-compose
|
||||
|
||||
# 验证安装
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
echo "✅ Docker Compose 二进制文件安装成功"
|
||||
# ==================== 执行 HTTPS 配置 ====================
|
||||
run_https_setup() {
|
||||
if [ "$DEPLOY_HTTPS" = true ] && [ -n "$DEPLOY_DOMAIN" ]; then
|
||||
echo ""
|
||||
echo "🔒 执行 HTTPS 配置(请确保服务器已放行 80、443 端口)..."
|
||||
if [ -f "./https.sh" ]; then
|
||||
# https.sh 依赖 easyai-proxy.conf,需使用生成的域名配置文件
|
||||
export EASYAI_PROXY_CONF="${DEPLOY_DOMAIN}.conf"
|
||||
bash ./https.sh
|
||||
else
|
||||
echo "❌ Docker Compose 二进制文件安装失败,请手动检查。"
|
||||
exit 1
|
||||
echo "⚠️ 未找到 https.sh,请手动配置 HTTPS"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ==================== 主流程 ====================
|
||||
main() {
|
||||
init_project_dir
|
||||
|
||||
echo "📌 Docker 运行状态:"
|
||||
sudo systemctl status docker --no-pager || true
|
||||
# 检查是否已有 .env 且非强制重新配置
|
||||
if [ -f .env ] && [ -z "$DEPLOY_FORCE_RECONFIG" ] && [ -z "$DEPLOY_ACCESS" ]; then
|
||||
echo "📁 检测到已有 .env 配置"
|
||||
read -r -p "是否重新配置部署方式?[y/N]: " reconfigure
|
||||
if [[ ! "$reconfigure" =~ ^[yY] ]]; then
|
||||
echo "⏭️ 使用现有配置继续..."
|
||||
DEPLOY_MODE="skip"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "📌 Docker Compose 版本:"
|
||||
# 优先使用 docker compose 命令(新版),如果不行再尝试 docker-compose(旧版或别名)
|
||||
if command -v docker &> /dev/null && docker compose version &> /dev/null; then
|
||||
docker compose version
|
||||
elif command -v docker-compose &> /dev/null; then
|
||||
docker-compose -v
|
||||
else
|
||||
echo "❌ 无法检测 Docker Compose 版本。"
|
||||
fi
|
||||
if [ "$DEPLOY_MODE" != "skip" ]; then
|
||||
run_deploy_questions
|
||||
fi
|
||||
|
||||
echo "🎉 Docker 和 Docker Compose 已就绪!"
|
||||
if [ "$DEPLOY_MODE" != "skip" ]; then
|
||||
setup_env_files
|
||||
else
|
||||
[ ! -f .env.tools ] && cp .env.tools.sample .env.tools
|
||||
[ ! -f .env.ASG ] && cp .env.ASG.sample .env.ASG
|
||||
[ ! -f .env.AMS ] && cp .env.AMS.sample .env.AMS
|
||||
fi
|
||||
|
||||
echo "🚀 启动EasyAI"
|
||||
# 对于新版 docker-compose-plugin,命令是 'docker compose'
|
||||
if command -v docker &> /dev/null && docker compose version &> /dev/null; then
|
||||
sudo docker compose pull && sudo docker compose up -d
|
||||
else # 兼容旧版独立安装的 docker-compose
|
||||
sudo docker-compose pull && sudo docker-compose up -d
|
||||
fi
|
||||
echo "🎉EasyAI应用启动成功"
|
||||
if [ "$DEPLOY_DRY_RUN" = "1" ]; then
|
||||
echo ""
|
||||
echo "⚠️ dry-run 模式:跳过 Docker 安装和服务启动"
|
||||
echo " 配置文件已生成,可直接运行 ./start.sh 完成部署"
|
||||
else
|
||||
install_docker
|
||||
start_services
|
||||
run_https_setup
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo " 部署完成"
|
||||
echo "================================"
|
||||
if [ "$DEPLOY_MODE" = "ip" ] && [ -n "$DEPLOY_IP" ]; then
|
||||
echo "访问地址: http://${DEPLOY_IP}:3010"
|
||||
elif [ "$DEPLOY_MODE" = "domain" ] && [ -n "$DEPLOY_DOMAIN" ]; then
|
||||
echo "访问地址: http://${DEPLOY_DOMAIN} (配置 Nginx 后)"
|
||||
[ "$DEPLOY_HTTPS" = true ] && echo "HTTPS 已启用"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
201
update.ps1
Normal file
201
update.ps1
Normal file
@ -0,0 +1,201 @@
|
||||
#Requires -Version 5.1
|
||||
# EasyAI Windows 更新脚本
|
||||
# 拉取仓库最新代码,更新镜像并重启服务
|
||||
# 用法: .\update.ps1
|
||||
|
||||
# 设置控制台编码为 UTF-8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$script:LogDir = $PSScriptRoot
|
||||
if (-not $script:LogDir) { $script:LogDir = $env:TEMP }
|
||||
$script:LogFile = Join-Path $script:LogDir "update.ps1.log"
|
||||
|
||||
function Write-Log {
|
||||
param([string]$Msg)
|
||||
try {
|
||||
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Msg"
|
||||
Add-Content -Path $script:LogFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
|
||||
} catch { }
|
||||
}
|
||||
|
||||
trap {
|
||||
$errMsg = $_.Exception.Message
|
||||
try {
|
||||
Add-Content -Path $script:LogFile -Value "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 错误: $errMsg" -Encoding UTF8 -ErrorAction SilentlyContinue
|
||||
} catch { }
|
||||
Write-Host ""
|
||||
Write-Host "发生错误: $errMsg" -ForegroundColor Red
|
||||
if ($env:CI -ne "true") { Read-Host "按 Enter 键退出" }
|
||||
exit 1
|
||||
}
|
||||
|
||||
function Wait-ForExit {
|
||||
if ($env:CI -eq "true") { return }
|
||||
Write-Host ""
|
||||
Read-Host "按 Enter 键退出"
|
||||
}
|
||||
|
||||
function Write-Step { param($Msg) Write-Host $Msg }
|
||||
function Write-Ok { param($Msg) Write-Host " ✓ $Msg" -ForegroundColor Green }
|
||||
function Write-Err { param($Msg) Write-Host "❌ $Msg" -ForegroundColor Red; throw $Msg }
|
||||
function Write-Warn { param($Msg) Write-Host "⚠️ $Msg" -ForegroundColor Yellow }
|
||||
|
||||
# -h/--help
|
||||
if ($args -contains "-h" -or $args -contains "--help") {
|
||||
Write-Host "用法: .\update.ps1"
|
||||
Write-Host ""
|
||||
Write-Host "脚本将提示选择更新方式,默认拉取仓库并更新镜像。"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 进入项目目录
|
||||
$scriptDir = $PSScriptRoot
|
||||
if (-not $scriptDir) { Write-Err "无法获取脚本所在目录" }
|
||||
$composePath = Join-Path $scriptDir "docker-compose.yml"
|
||||
if (-not (Test-Path $composePath)) { Write-Err "未找到 docker-compose.yml,请在 easyai 目录下运行 update.ps1" }
|
||||
Set-Location $scriptDir
|
||||
Write-Step "📁 项目目录: $scriptDir"
|
||||
|
||||
# ==================== 命令行内选择 ====================
|
||||
Write-Host ""
|
||||
Write-Host "请选择更新方式:"
|
||||
Write-Host " [1] 更新并拉取仓库(git pull)+ 更新镜像并重启(默认)"
|
||||
Write-Host " [2] 仅更新镜像并重启(跳过 git pull)"
|
||||
$choice = Read-Host "请选择 [1/2,回车默认 1]"
|
||||
if ([string]::IsNullOrWhiteSpace($choice)) { $choice = "1" }
|
||||
|
||||
$skipRepoUpdate = $false
|
||||
switch ($choice) {
|
||||
"2" { $skipRepoUpdate = $true }
|
||||
"1" { }
|
||||
default {
|
||||
Write-Warn "无效选择,将使用默认:更新并拉取仓库"
|
||||
$skipRepoUpdate = $false
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 拉取仓库 ====================
|
||||
if (-not $skipRepoUpdate) {
|
||||
Write-Host ""
|
||||
Write-Host "==========================="
|
||||
Write-Host "📥 拉取仓库最新代码"
|
||||
Write-Host "==========================="
|
||||
|
||||
if (-not (Test-Path ".git")) {
|
||||
Write-Err "当前目录不是 Git 仓库,请使用 git clone 克隆项目后使用 update.ps1"
|
||||
}
|
||||
|
||||
Write-Step "📥 正在执行 git pull..."
|
||||
try {
|
||||
$output = git pull 2>&1
|
||||
if ($LASTEXITCODE -ne 0) { throw "git pull 返回 $LASTEXITCODE" }
|
||||
Write-Ok "仓库已更新到最新版本"
|
||||
} catch {
|
||||
Write-Err "git pull 失败,请检查网络或远程仓库配置"
|
||||
}
|
||||
|
||||
# 确保环境配置文件存在
|
||||
Write-Host ""
|
||||
Write-Step "📝 检查环境配置文件..."
|
||||
if (-not (Test-Path ".env") -and (Test-Path ".env.sample")) {
|
||||
Copy-Item ".env.sample" ".env"
|
||||
Write-Ok ".env"
|
||||
}
|
||||
if (-not (Test-Path ".env.tools") -and (Test-Path ".env.tools.sample")) {
|
||||
Copy-Item ".env.tools.sample" ".env.tools"
|
||||
Write-Ok ".env.tools"
|
||||
}
|
||||
if (-not (Test-Path ".env.ASG") -and (Test-Path ".env.ASG.sample")) {
|
||||
Copy-Item ".env.ASG.sample" ".env.ASG"
|
||||
Write-Ok ".env.ASG"
|
||||
}
|
||||
if (-not (Test-Path ".env.AMS") -and (Test-Path ".env.AMS.sample")) {
|
||||
Copy-Item ".env.AMS.sample" ".env.AMS"
|
||||
Write-Ok ".env.AMS"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Step "⏭️ 跳过仓库更新,仅更新镜像并重启"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# ==================== Docker 检查 ====================
|
||||
function Test-DockerInstalled {
|
||||
$docker = Get-Command docker -ErrorAction SilentlyContinue
|
||||
if (-not $docker) { return $false }
|
||||
try { $null = & docker --version 2>&1; return $true } catch { return $false }
|
||||
}
|
||||
|
||||
function Test-DockerRunning {
|
||||
param([int]$TimeoutSeconds = 10)
|
||||
try {
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.FileName = "docker"; $psi.Arguments = "info"
|
||||
$psi.RedirectStandardOutput = $true; $psi.RedirectStandardError = $true
|
||||
$psi.UseShellExecute = $false; $psi.CreateNoWindow = $true
|
||||
$p = [System.Diagnostics.Process]::Start($psi)
|
||||
$exited = $p.WaitForExit($TimeoutSeconds * 1000)
|
||||
if (-not $exited) { try { $p.Kill() } catch { }; return $false }
|
||||
return ($p.ExitCode -eq 0)
|
||||
} catch { return $false }
|
||||
}
|
||||
|
||||
function Ensure-DockerRunning {
|
||||
if (-not (Test-DockerInstalled)) { return $false }
|
||||
if (Test-DockerRunning) {
|
||||
Write-Ok "Docker 引擎已运行"
|
||||
$v = docker --version 2>$null; if ($v) { Write-Host " $v" }
|
||||
return $true
|
||||
}
|
||||
Write-Warn "Docker 未运行,请手动启动 Docker Desktop 后重新执行"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host "==========================="
|
||||
Write-Host "🚀 检查 Docker"
|
||||
Write-Host "==========================="
|
||||
Write-Host ""
|
||||
|
||||
if (-not (Test-DockerInstalled)) {
|
||||
Write-Err "未检测到 Docker,请先运行 start.ps1 完成首次部署"
|
||||
}
|
||||
|
||||
if (-not (Ensure-DockerRunning)) {
|
||||
Write-Host ""
|
||||
Write-Host "请启动 Docker Desktop,确认其完全就绪后再重新运行 update.ps1"
|
||||
Wait-ForExit
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检测 docker compose
|
||||
$hasComposeV2 = $false
|
||||
try { $null = docker compose version 2>&1; $hasComposeV2 = $true } catch { }
|
||||
|
||||
# ==================== 更新并启动 ====================
|
||||
Write-Host ""
|
||||
Write-Step "🚀 重新启动 EasyAI..."
|
||||
if ($hasComposeV2) {
|
||||
docker compose pull
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker compose pull 失败" }
|
||||
docker compose up -d
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker compose up 失败" }
|
||||
} else {
|
||||
docker-compose pull
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose pull 失败" }
|
||||
docker-compose up -d
|
||||
if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose up 失败" }
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "================================"
|
||||
Write-Host " 更新完成"
|
||||
Write-Host "================================"
|
||||
Write-Host "🎉 EasyAI 应用更新成功"
|
||||
Write-Host "访问地址: http://127.0.0.1:3010"
|
||||
Write-Host ""
|
||||
Write-Log "=== 更新完成 ==="
|
||||
Wait-ForExit
|
||||
108
update.sh
108
update.sh
@ -2,75 +2,65 @@
|
||||
|
||||
set -e # 发生错误时终止脚本执行
|
||||
|
||||
# 参数解析
|
||||
SKIP_COMPOSE_UPDATE=false
|
||||
# 仅支持 -h/--help 快速查看
|
||||
if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then
|
||||
echo "用法: $0"
|
||||
echo ""
|
||||
echo "脚本将提示选择更新方式,默认拉取仓库并更新镜像。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--skip-compose-update|-s)
|
||||
SKIP_COMPOSE_UPDATE=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -s, --skip-compose-update 跳过 docker-compose.yml 的更新"
|
||||
echo " -h, --help 显示此帮助信息"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "❌ 未知参数: $1"
|
||||
echo "使用 --help 或 -h 查看帮助信息"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# 进入脚本所在目录(项目根目录)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# 更新 docker-compose.yml 文件
|
||||
if [ "$SKIP_COMPOSE_UPDATE" = false ]; then
|
||||
# 命令行内选择更新方式(默认:更新并拉取仓库)
|
||||
echo ""
|
||||
echo "请选择更新方式:"
|
||||
echo " [1] 更新并拉取仓库(git pull)+ 更新镜像并重启(默认)"
|
||||
echo " [2] 仅更新镜像并重启(跳过 git pull)"
|
||||
read -r -p "请选择 [1/2,回车默认 1]: " choice
|
||||
choice="${choice:-1}"
|
||||
SKIP_REPO_UPDATE=false
|
||||
case "$choice" in
|
||||
2) SKIP_REPO_UPDATE=true ;;
|
||||
1) ;;
|
||||
*) echo "❌ 无效选择,将使用默认:更新并拉取仓库"; SKIP_REPO_UPDATE=false ;;
|
||||
esac
|
||||
|
||||
# 拉取整个仓库更新
|
||||
if [ "$SKIP_REPO_UPDATE" = false ]; then
|
||||
echo ""
|
||||
echo "==========================="
|
||||
echo "📄 检查并更新 docker-compose.yml"
|
||||
echo "📥 拉取仓库最新代码"
|
||||
echo "==========================="
|
||||
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
COMPOSE_URL="https://git.51easyai.com/wangbo/easyai/raw/main/docker-compose.yml"
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
# 检查本地文件是否存在
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo "⚠️ 本地 $COMPOSE_FILE 不存在,直接下载最新版本..."
|
||||
curl -fsSL "$COMPOSE_URL" -o "$COMPOSE_FILE"
|
||||
echo "✅ $COMPOSE_FILE 已下载"
|
||||
else
|
||||
echo "📥 正在下载远程 $COMPOSE_FILE..."
|
||||
if curl -fsSL "$COMPOSE_URL" -o "$TEMP_FILE"; then
|
||||
# 比较本地文件和远程文件的内容
|
||||
if cmp -s "$COMPOSE_FILE" "$TEMP_FILE"; then
|
||||
echo "✅ 本地 $COMPOSE_FILE 已是最新版本,无需更新"
|
||||
rm -f "$TEMP_FILE"
|
||||
else
|
||||
echo "🔄 检测到新版本,正在更新..."
|
||||
# 备份原文件
|
||||
BACKUP_FILE="${COMPOSE_FILE}.bak"
|
||||
cp "$COMPOSE_FILE" "$BACKUP_FILE"
|
||||
echo "💾 原文件已备份为 $BACKUP_FILE"
|
||||
# 替换为新文件
|
||||
mv "$TEMP_FILE" "$COMPOSE_FILE"
|
||||
echo "✅ $COMPOSE_FILE 已更新到最新版本"
|
||||
fi
|
||||
else
|
||||
echo "❌ 无法下载远程文件,跳过更新步骤"
|
||||
rm -f "$TEMP_FILE"
|
||||
fi
|
||||
if [ ! -d .git ]; then
|
||||
echo "❌ 当前目录不是 Git 仓库,无法执行 git pull"
|
||||
echo " 请使用 git clone 克隆项目后即可使用 update.sh 更新"
|
||||
echo " 克隆命令: git clone https://git.51easyai.com/wangbo/easyai && cd easyai"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📥 正在执行 git pull..."
|
||||
if git pull; then
|
||||
echo "✅ 仓库已更新到最新版本"
|
||||
else
|
||||
echo "❌ git pull 失败,请检查网络或远程仓库配置"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 确保环境配置文件存在(从 .sample 生成,不覆盖已有文件)
|
||||
echo ""
|
||||
echo "📝 检查环境配置文件..."
|
||||
[ ! -f .env ] && [ -f .env.sample ] && cp .env.sample .env && echo " ✓ .env"
|
||||
[ ! -f .env.tools ] && [ -f .env.tools.sample ] && cp .env.tools.sample .env.tools && echo " ✓ .env.tools"
|
||||
[ ! -f .env.ASG ] && [ -f .env.ASG.sample ] && cp .env.ASG.sample .env.ASG && echo " ✓ .env.ASG"
|
||||
[ ! -f .env.AMS ] && [ -f .env.AMS.sample ] && cp .env.AMS.sample .env.AMS && echo " ✓ .env.AMS"
|
||||
|
||||
echo ""
|
||||
else
|
||||
echo "⏭️ 跳过 docker-compose.yml 更新(已使用 --skip-compose-update 参数)"
|
||||
echo "⏭️ 跳过仓库更新,仅更新镜像并重启"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user