重构install_jsite.sh增加mode参数支持不同任务类型
This commit is contained in:
parent
018555a401
commit
84ad3031c2
189
README.md
189
README.md
@ -1,2 +1,189 @@
|
||||
# jshells
|
||||
# jsite 自动化部署和管理脚本
|
||||
|
||||
这是一个集成了完整部署和日常管理功能的自动化脚本,支持多种任务模式。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🚀 **完整部署**:一键部署完整的jsite环境
|
||||
- 🏗️ **网站管理**:创建、删除、构建、启动、停止、重启网站
|
||||
- 📊 **状态监控**:查看网站运行状态和日志
|
||||
- 🔧 **环境管理**:Docker、Node.js、PM2、Traefik等环境组件
|
||||
- ⚡ **智能端口分配**:自动分配和管理端口资源
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本语法
|
||||
|
||||
```bash
|
||||
./install_jsite.sh --mode <任务模式> [其他参数]
|
||||
```
|
||||
|
||||
### 支持的任务模式
|
||||
|
||||
| 模式 | 功能 | 示例 |
|
||||
|------|------|------|
|
||||
| `deploy` | 完整部署模式(默认) | `--mode deploy --site-name myproject` |
|
||||
| `create` | 创建新网站 | `--mode create --site-name myproject --git-repo <仓库地址>` |
|
||||
| `delete` | 删除网站 | `--mode delete --site-name myproject` |
|
||||
| `build` | 构建网站 | `--mode build --site-name myproject` |
|
||||
| `start` | 启动网站 | `--mode start --site-name myproject` |
|
||||
| `stop` | 停止网站 | `--mode stop --site-name myproject` |
|
||||
| `restart` | 重启网站 | `--mode restart --site-name myproject` |
|
||||
| `status` | 查看网站状态 | `--mode status --site-name myproject` |
|
||||
| `list` | 列出所有网站 | `--mode list` |
|
||||
| `logs` | 查看网站日志 | `--mode logs --site-name myproject` |
|
||||
|
||||
### 常用参数
|
||||
|
||||
| 参数 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `--mode` | 任务模式 | `deploy` |
|
||||
| `--site-name` | 网站名称 | `jingrow` |
|
||||
| `--git-repo` | Git仓库地址 | `http://git.jingrow.com:3000/jsite/jingrow` |
|
||||
| `--node-version` | Node.js版本 | `22` |
|
||||
| `--start-port` | 起始端口 | `3001` |
|
||||
| `--site-url` | 网站域名 | `starrbud.com` |
|
||||
|
||||
### 使用示例
|
||||
|
||||
#### 1. 完整部署新项目
|
||||
|
||||
```bash
|
||||
./install_jsite.sh \
|
||||
--mode deploy \
|
||||
--site-name myproject \
|
||||
--git-repo http://git.example.com/myproject \
|
||||
--site-url myproject.com \
|
||||
--public-ip 8.217.167.199
|
||||
```
|
||||
|
||||
#### 2. 创建新网站
|
||||
|
||||
```bash
|
||||
./install_jsite.sh \
|
||||
--mode create \
|
||||
--site-name myproject \
|
||||
--git-repo http://git.example.com/myproject \
|
||||
--site-url myproject.com
|
||||
```
|
||||
|
||||
#### 3. 构建和启动网站
|
||||
|
||||
```bash
|
||||
# 构建项目
|
||||
./install_jsite.sh --mode build --site-name myproject
|
||||
|
||||
# 启动项目
|
||||
./install_jsite.sh --mode start --site-name myproject
|
||||
```
|
||||
|
||||
#### 4. 管理网站状态
|
||||
|
||||
```bash
|
||||
# 查看状态
|
||||
./install_jsite.sh --mode status --site-name myproject
|
||||
|
||||
# 停止网站
|
||||
./install_jsite.sh --mode stop --site-name myproject
|
||||
|
||||
# 重启网站
|
||||
./install_jsite.sh --mode restart --site-name myproject
|
||||
```
|
||||
|
||||
#### 5. 查看所有网站
|
||||
|
||||
```bash
|
||||
./install_jsite.sh --mode list
|
||||
```
|
||||
|
||||
#### 6. 查看日志
|
||||
|
||||
```bash
|
||||
./install_jsite.sh --mode logs --site-name myproject
|
||||
```
|
||||
|
||||
## 工作流程
|
||||
|
||||
### 完整部署流程 (deploy)
|
||||
1. 创建jingrow用户
|
||||
2. 安装Docker(可选跳过)
|
||||
3. 安装Node.js和NVM
|
||||
4. 克隆项目代码
|
||||
5. 创建环境配置文件
|
||||
6. 安装项目依赖
|
||||
7. 安装PM2并启动项目
|
||||
8. 安装Traefik并配置反向代理
|
||||
|
||||
### 网站管理流程
|
||||
- **create**: 克隆代码 → 分配端口 → 创建配置 → 提示下一步
|
||||
- **build**: 安装依赖 → 构建项目
|
||||
- **start**: 检查状态 → 分配端口 → 启动PM2进程
|
||||
- **stop**: 停止PM2进程
|
||||
- **restart**: 重启PM2进程或启动(如果未运行)
|
||||
- **delete**: 停止进程 → 清理配置 → 删除文件
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
/home/jingrow/
|
||||
├── jsite/ # 网站项目目录
|
||||
│ ├── myproject1/ # 项目1
|
||||
│ ├── myproject2/ # 项目2
|
||||
│ └── site_port.json # 端口分配记录
|
||||
├── traefik-docker/ # Traefik配置目录
|
||||
│ └── conf.d/website/ # 网站配置文件
|
||||
└── .nvm/ # Node.js版本管理
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **权限要求**:脚本需要root权限运行
|
||||
2. **端口管理**:自动分配端口,避免冲突
|
||||
3. **环境依赖**:确保系统支持Docker和Node.js
|
||||
4. **域名配置**:需要正确配置DNS解析
|
||||
5. **SSL证书**:Traefik自动申请Let's Encrypt证书
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **端口被占用**
|
||||
- 检查端口分配文件:`/home/jingrow/jsite/site_port.json`
|
||||
- 手动修改端口或停止占用进程
|
||||
|
||||
2. **PM2启动失败**
|
||||
- 检查项目依赖是否正确安装
|
||||
- 查看PM2日志:`pm2 logs <项目名>`
|
||||
- 确认package.json中有start脚本
|
||||
|
||||
3. **Traefik配置问题**
|
||||
- 检查配置文件:`/home/jingrow/traefik-docker/conf.d/website/`
|
||||
- 重启Traefik服务
|
||||
|
||||
4. **域名访问问题**
|
||||
- 确认DNS解析正确
|
||||
- 检查防火墙设置
|
||||
- 验证SSL证书状态
|
||||
|
||||
### 日志查看
|
||||
|
||||
```bash
|
||||
# PM2日志
|
||||
pm2 logs <项目名>
|
||||
|
||||
# Traefik日志
|
||||
docker logs <traefik容器名>
|
||||
|
||||
# 系统日志
|
||||
journalctl -u docker
|
||||
```
|
||||
|
||||
## 更新日志
|
||||
|
||||
- **v2.0**: 整合jsite.sh功能,支持多种任务模式
|
||||
- **v1.0**: 基础部署功能
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
674
install_jsite.sh
674
install_jsite.sh
@ -22,6 +22,13 @@ NODE_VERSION="22"
|
||||
START_PORT=3001
|
||||
PORT_INCREMENT=1
|
||||
|
||||
# jsite管理相关配置
|
||||
JSITE_BASE_DIR="/home/jingrow/jsite"
|
||||
TRAEFIK_CONFIG_DIR="/home/jingrow/traefik-docker/conf.d/website"
|
||||
|
||||
# 任务模式
|
||||
MODE="deploy" # 默认模式:deploy, create, delete, build, start, stop, restart, status, list, logs
|
||||
|
||||
# 跳过选项
|
||||
SKIP_DOCKER=false
|
||||
SKIP_TRAEFIK=false
|
||||
@ -139,6 +146,12 @@ while [[ $# -gt 0 ]]; do
|
||||
shift
|
||||
;;
|
||||
|
||||
# 任务模式
|
||||
--mode)
|
||||
MODE="$2"
|
||||
shift 2
|
||||
;;
|
||||
|
||||
# 网络配置
|
||||
--public-ip)
|
||||
PUBLIC_IP="$2"
|
||||
@ -171,6 +184,18 @@ while [[ $# -gt 0 ]]; do
|
||||
-h|--help)
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "任务模式 (--mode):"
|
||||
echo " deploy 完整部署模式 (默认)"
|
||||
echo " create 创建新网站"
|
||||
echo " delete 删除网站"
|
||||
echo " build 构建网站"
|
||||
echo " start 启动网站"
|
||||
echo " stop 停止网站"
|
||||
echo " restart 重启网站"
|
||||
echo " status 查看网站状态"
|
||||
echo " list 列出所有网站"
|
||||
echo " logs 查看网站日志"
|
||||
echo ""
|
||||
echo "基础配置:"
|
||||
echo " --site-name NAME 项目名称 (默认: jingrow)"
|
||||
echo " --git-repo URL Git仓库地址"
|
||||
@ -198,9 +223,15 @@ while [[ $# -gt 0 ]]; do
|
||||
echo " -h, --help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 --site-name myproject --public-ip 8.217.167.199"
|
||||
echo " $0 --site-url example.com --api-key mykey123"
|
||||
echo " $0 --force-update --public-ip 203.0.113.1"
|
||||
echo " # 完整部署"
|
||||
echo " $0 --mode deploy --site-name myproject --public-ip 8.217.167.199"
|
||||
echo " # 创建网站"
|
||||
echo " $0 --mode create --site-name myproject --git-repo http://git.example.com/project"
|
||||
echo " # 管理网站"
|
||||
echo " $0 --mode start --site-name myproject"
|
||||
echo " $0 --mode stop --site-name myproject"
|
||||
echo " $0 --mode status --site-name myproject"
|
||||
echo " $0 --mode list"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
@ -263,6 +294,44 @@ check_root() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查jsite基础目录
|
||||
check_jsite_base_dir() {
|
||||
if [ ! -d "$JSITE_BASE_DIR" ]; then
|
||||
log_info "创建jsite基础目录: $JSITE_BASE_DIR"
|
||||
mkdir -p "$JSITE_BASE_DIR"
|
||||
chown "jingrow:jingrow" "$JSITE_BASE_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查网站目录是否存在
|
||||
check_site_exists() {
|
||||
local site_name="$1"
|
||||
[ -d "$JSITE_BASE_DIR/$site_name" ]
|
||||
}
|
||||
|
||||
# 获取网站状态
|
||||
get_site_status() {
|
||||
local site_name="$1"
|
||||
|
||||
if ! check_site_exists "$site_name"; then
|
||||
echo "not_exists"
|
||||
return
|
||||
fi
|
||||
|
||||
# 检查PM2进程状态
|
||||
local pm2_status=$(su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 list 2>/dev/null | grep -w '$site_name' | grep -c 'online' || echo '0'
|
||||
")
|
||||
|
||||
if [ "$pm2_status" != "0" ]; then
|
||||
echo "running"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. 创建jingrow用户
|
||||
create_jingrow_user() {
|
||||
log_info "开始创建jingrow用户..."
|
||||
@ -1319,89 +1388,560 @@ show_deployment_info() {
|
||||
log_success "部署脚本执行完成!"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始jsite前端自动化部署..."
|
||||
echo ""
|
||||
log_info "部署参数:"
|
||||
echo " - 项目名称: $SITE_NAME"
|
||||
echo " - Git仓库: $GIT_REPO"
|
||||
echo " - Node.js版本: $NODE_VERSION"
|
||||
echo " - 跳过Docker: $SKIP_DOCKER"
|
||||
echo " - 跳过Traefik: $SKIP_TRAEFIK"
|
||||
echo " - 跳过依赖安装: $SKIP_DEPENDENCIES"
|
||||
echo " - 跳过PM2: $SKIP_PM2"
|
||||
echo " - 强制更新: $FORCE_UPDATE"
|
||||
echo ""
|
||||
log_info "端口配置:"
|
||||
echo " - 起始端口: $START_PORT"
|
||||
echo " - 端口增量: $PORT_INCREMENT"
|
||||
echo ""
|
||||
log_info ".env文件参数:"
|
||||
echo " - 网站URL: $SITE_URL"
|
||||
echo " - 站点名称: $SITE_NAME"
|
||||
echo " - 服务器URL: $SERVER_URL"
|
||||
echo " - 重新验证令牌: $REVALIDATE_TOKEN"
|
||||
echo " - API密钥: $API_KEY"
|
||||
echo " - API密钥: $API_SECRET"
|
||||
echo ""
|
||||
# ========================================
|
||||
# jsite管理功能函数
|
||||
# ========================================
|
||||
|
||||
# 生成Host规则
|
||||
generate_host_rule() {
|
||||
local domain="$1"
|
||||
|
||||
check_root
|
||||
create_jingrow_user
|
||||
# 移除协议前缀
|
||||
domain=$(echo "$domain" | sed -E 's|^https?://||')
|
||||
# 移除端口号
|
||||
domain=$(echo "$domain" | sed -E 's|:[0-9]+$||')
|
||||
|
||||
if [ "$SKIP_DOCKER" = false ]; then
|
||||
install_docker
|
||||
# 检查是否为一级域名
|
||||
if [[ "$domain" =~ ^[^.]+\.[^.]+$ ]]; then
|
||||
echo "Host(\`$domain\`) || Host(\`www.$domain\`)"
|
||||
else
|
||||
log_warning "跳过Docker安装"
|
||||
echo "Host(\`$domain\`)"
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建traefik网站配置文件
|
||||
create_traefik_website_config_simple() {
|
||||
local site_name="$1"
|
||||
local domain="$2"
|
||||
local port="$3"
|
||||
|
||||
# 检查traefik配置目录是否存在
|
||||
if [ ! -d "$TRAEFIK_CONFIG_DIR" ]; then
|
||||
log_warning "Traefik配置目录不存在: $TRAEFIK_CONFIG_DIR"
|
||||
log_info "请先确保Traefik已正确安装"
|
||||
return 1
|
||||
fi
|
||||
|
||||
install_nodejs
|
||||
local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml"
|
||||
local host_rule=$(generate_host_rule "$domain")
|
||||
|
||||
if ! clone_jsite_project; then
|
||||
log_error "项目克隆失败,部署终止"
|
||||
exit 1
|
||||
# 获取本机IP地址
|
||||
local host_ip=$(get_optimal_host_ip)
|
||||
|
||||
# 创建配置文件
|
||||
cat > "$config_file" << EOF
|
||||
http:
|
||||
routers:
|
||||
main-https:
|
||||
rule: &host_rule $host_rule
|
||||
entryPoints:
|
||||
- websecure
|
||||
service: main-service
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
|
||||
main-http-redirect:
|
||||
rule: *host_rule
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
service: noop
|
||||
|
||||
services:
|
||||
main-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://$host_ip:$port"
|
||||
noop:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://127.0.0.1:65535" # 占位用,无实际后端,仅用于HTTP跳转
|
||||
|
||||
middlewares:
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
permanent: true
|
||||
EOF
|
||||
|
||||
chown "jingrow:jingrow" "$config_file" 2>/dev/null || true
|
||||
chmod 644 "$config_file"
|
||||
|
||||
log_success "Traefik配置文件已创建: $config_file"
|
||||
log_info "域名: $domain -> $host_ip:$port"
|
||||
}
|
||||
|
||||
# 删除traefik网站配置文件
|
||||
remove_traefik_website_config() {
|
||||
local site_name="$1"
|
||||
local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml"
|
||||
|
||||
if [ -f "$config_file" ]; then
|
||||
rm -f "$config_file"
|
||||
log_success "Traefik配置文件已删除: $config_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# 重启traefik服务以加载新配置
|
||||
restart_traefik() {
|
||||
local traefik_dir="/home/jingrow/traefik-docker"
|
||||
|
||||
if [ -d "$traefik_dir" ] && [ -f "$traefik_dir/docker-compose.yml" ]; then
|
||||
log_info "重启Traefik服务以加载新配置..."
|
||||
su - "jingrow" -c "cd '$traefik_dir' && docker compose restart" 2>/dev/null || {
|
||||
log_warning "无法重启Traefik服务,请手动重启"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建新网站
|
||||
create_site() {
|
||||
log_info "开始创建网站: $SITE_NAME"
|
||||
log_info "Git仓库: $GIT_REPO"
|
||||
log_info "域名: $SITE_URL"
|
||||
|
||||
# 检查网站是否已存在
|
||||
if check_site_exists "$SITE_NAME"; then
|
||||
log_error "网站 $SITE_NAME 已存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 获取并分配端口
|
||||
local port=$(get_or_assign_port "$SITE_NAME")
|
||||
|
||||
# 克隆项目
|
||||
log_info "克隆项目: $GIT_REPO"
|
||||
if ! su - "jingrow" -c "cd '$JSITE_BASE_DIR' && git clone '$GIT_REPO' '$SITE_NAME'"; then
|
||||
log_error "项目克隆失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查package.json是否存在
|
||||
if [ ! -f "$JSITE_BASE_DIR/$SITE_NAME/package.json" ]; then
|
||||
log_error "package.json文件不存在,这可能不是一个有效的Node.js项目"
|
||||
rm -rf "$JSITE_BASE_DIR/$SITE_NAME"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存端口分配
|
||||
save_port_assignment "$SITE_NAME" "$port"
|
||||
|
||||
# 创建.env文件
|
||||
create_env_file
|
||||
|
||||
if [ "$SKIP_DEPENDENCIES" = false ]; then
|
||||
if ! install_project_dependencies; then
|
||||
log_error "项目依赖安装失败,部署终止"
|
||||
exit 1
|
||||
# 创建traefik配置文件
|
||||
if create_traefik_website_config_simple "$SITE_NAME" "$SITE_URL" "$port"; then
|
||||
restart_traefik
|
||||
fi
|
||||
|
||||
log_success "网站 $SITE_NAME 创建成功"
|
||||
log_info "项目路径: $JSITE_BASE_DIR/$SITE_NAME"
|
||||
log_info "分配端口: $port"
|
||||
log_info "绑定域名: $SITE_URL"
|
||||
|
||||
# 提示下一步操作
|
||||
log_info "下一步操作:"
|
||||
echo " 1. 构建项目: $0 --mode build --site-name $SITE_NAME"
|
||||
echo " 2. 启动项目: $0 --mode start --site-name $SITE_NAME"
|
||||
}
|
||||
|
||||
# 删除网站
|
||||
delete_site() {
|
||||
log_info "即将删除网站: $SITE_NAME"
|
||||
read -p "确认删除?(y/N): " confirm
|
||||
|
||||
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
||||
log_info "取消删除操作"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 停止PM2进程
|
||||
local status=$(get_site_status "$SITE_NAME")
|
||||
if [ "$status" = "running" ]; then
|
||||
log_info "停止运行中的进程..."
|
||||
stop_site
|
||||
fi
|
||||
|
||||
# 删除PM2配置
|
||||
su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 delete '$SITE_NAME' 2>/dev/null || true
|
||||
"
|
||||
|
||||
# 删除traefik配置文件
|
||||
remove_traefik_website_config "$SITE_NAME"
|
||||
|
||||
# 删除端口分配记录
|
||||
local port_file="$JSITE_BASE_DIR/site_port.json"
|
||||
if [ -f "$port_file" ] && command -v jq &> /dev/null; then
|
||||
jq "del(.$SITE_NAME)" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file"
|
||||
chown "jingrow:jingrow" "$port_file" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 删除项目目录
|
||||
log_info "删除项目目录: $JSITE_BASE_DIR/$SITE_NAME"
|
||||
rm -rf "$JSITE_BASE_DIR/$SITE_NAME"
|
||||
|
||||
# 重启traefik以移除配置
|
||||
restart_traefik
|
||||
|
||||
log_success "网站 $SITE_NAME 已删除"
|
||||
}
|
||||
|
||||
# 构建网站
|
||||
build_site() {
|
||||
log_info "构建网站: $SITE_NAME"
|
||||
|
||||
# 安装依赖
|
||||
log_info "安装依赖..."
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
cd '$JSITE_BASE_DIR/$SITE_NAME'
|
||||
npm install
|
||||
"; then
|
||||
log_error "依赖安装失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 构建项目
|
||||
log_info "构建项目..."
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
cd '$JSITE_BASE_DIR/$SITE_NAME'
|
||||
npm run build
|
||||
"; then
|
||||
log_error "项目构建失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "网站 $SITE_NAME 构建完成"
|
||||
}
|
||||
|
||||
# 启动网站
|
||||
start_site() {
|
||||
local status=$(get_site_status "$SITE_NAME")
|
||||
if [ "$status" = "running" ]; then
|
||||
log_warning "网站 $SITE_NAME 已在运行中"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 获取分配的端口
|
||||
local port=$(get_or_assign_port "$SITE_NAME")
|
||||
|
||||
log_info "启动网站: $SITE_NAME (端口: $port)"
|
||||
|
||||
# 检查是否有构建产物
|
||||
if [ ! -d "$JSITE_BASE_DIR/$SITE_NAME/.next" ] && [ ! -d "$JSITE_BASE_DIR/$SITE_NAME/dist" ] && [ ! -d "$JSITE_BASE_DIR/$SITE_NAME/build" ]; then
|
||||
log_warning "未找到构建产物,建议先执行构建: $0 --mode build --site-name $SITE_NAME"
|
||||
fi
|
||||
|
||||
# 使用PM2启动,设置端口环境变量
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
cd '$JSITE_BASE_DIR/$SITE_NAME'
|
||||
PORT=$port pm2 start npm --name '$SITE_NAME' -- start
|
||||
"; then
|
||||
log_error "网站启动失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 等待启动完成
|
||||
sleep 3
|
||||
|
||||
local new_status=$(get_site_status "$SITE_NAME")
|
||||
if [ "$new_status" = "running" ]; then
|
||||
log_success "网站 $SITE_NAME 启动成功 (端口: $port)"
|
||||
else
|
||||
log_error "网站 $SITE_NAME 启动失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止网站
|
||||
stop_site() {
|
||||
local status=$(get_site_status "$SITE_NAME")
|
||||
if [ "$status" = "not_exists" ]; then
|
||||
log_error "网站 $SITE_NAME 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$status" = "stopped" ]; then
|
||||
log_warning "网站 $SITE_NAME 已停止"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "停止网站: $SITE_NAME"
|
||||
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 stop '$SITE_NAME'
|
||||
"; then
|
||||
log_error "网站停止失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "网站 $SITE_NAME 已停止"
|
||||
}
|
||||
|
||||
# 重启网站
|
||||
restart_site() {
|
||||
local status=$(get_site_status "$SITE_NAME")
|
||||
if [ "$status" = "not_exists" ]; then
|
||||
log_error "网站 $SITE_NAME 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "重启网站: $SITE_NAME"
|
||||
|
||||
if [ "$status" = "running" ]; then
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 restart '$SITE_NAME'
|
||||
"; then
|
||||
log_error "网站重启失败"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_warning "跳过项目依赖安装"
|
||||
# 如果未运行,则启动
|
||||
start_site
|
||||
return $?
|
||||
fi
|
||||
|
||||
# PM2安装和项目启动(在项目准备完成后)
|
||||
if [ "$SKIP_PM2" = false ]; then
|
||||
log_info "开始PM2安装和项目启动..."
|
||||
install_pm2
|
||||
create_pm2_config
|
||||
if ! start_project_with_pm2; then
|
||||
log_error "PM2启动失败,部署终止"
|
||||
log_info "请检查以下内容:"
|
||||
log_info "1. 项目依赖是否正确安装"
|
||||
log_info "2. package.json中是否有start脚本"
|
||||
log_info "3. 项目端口是否被占用"
|
||||
log_info "4. 查看PM2日志: pm2 logs $SITE_NAME"
|
||||
exit 1
|
||||
log_success "网站 $SITE_NAME 重启成功"
|
||||
}
|
||||
|
||||
# 查看网站状态
|
||||
show_site_status() {
|
||||
local status=$(get_site_status "$SITE_NAME")
|
||||
|
||||
case "$status" in
|
||||
"not_exists")
|
||||
log_error "网站 $SITE_NAME 不存在"
|
||||
return 1
|
||||
;;
|
||||
"running")
|
||||
log_success "网站 $SITE_NAME 正在运行"
|
||||
;;
|
||||
"stopped")
|
||||
log_warning "网站 $SITE_NAME 已停止"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 显示PM2详细状态
|
||||
if [ "$status" != "not_exists" ]; then
|
||||
log_info "PM2状态:"
|
||||
su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 list | grep -E '(App name|$SITE_NAME)' || echo '未找到PM2进程'
|
||||
"
|
||||
fi
|
||||
}
|
||||
|
||||
# 列出所有网站
|
||||
list_sites() {
|
||||
log_info "jsite网站列表:"
|
||||
|
||||
if [ ! -d "$JSITE_BASE_DIR" ] || [ -z "$(ls -A "$JSITE_BASE_DIR" 2>/dev/null)" ]; then
|
||||
echo " 暂无网站"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for site_dir in "$JSITE_BASE_DIR"/*; do
|
||||
if [ -d "$site_dir" ]; then
|
||||
local site_name=$(basename "$site_dir")
|
||||
local status=$(get_site_status "$site_name")
|
||||
|
||||
case "$status" in
|
||||
"running")
|
||||
echo " - $site_name (运行中)"
|
||||
;;
|
||||
"stopped")
|
||||
echo " - $site_name (已停止)"
|
||||
;;
|
||||
*)
|
||||
echo " - $site_name (未知状态)"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
log_warning "跳过PM2安装和项目启动"
|
||||
done
|
||||
}
|
||||
|
||||
# 查看网站日志
|
||||
show_site_logs() {
|
||||
local status=$(get_site_status "$SITE_NAME")
|
||||
if [ "$status" = "not_exists" ]; then
|
||||
log_error "网站 $SITE_NAME 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$SKIP_TRAEFIK" = false ]; then
|
||||
install_traefik
|
||||
create_traefik_website_config
|
||||
start_traefik
|
||||
else
|
||||
log_warning "跳过Traefik安装和启动"
|
||||
fi
|
||||
log_info "网站 $SITE_NAME 的日志:"
|
||||
su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 logs '$SITE_NAME'
|
||||
"
|
||||
}
|
||||
|
||||
# ========================================
|
||||
# 主函数
|
||||
# ========================================
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始jsite管理脚本..."
|
||||
echo ""
|
||||
log_info "执行模式: $MODE"
|
||||
log_info "目标网站: $SITE_NAME"
|
||||
echo ""
|
||||
|
||||
show_deployment_info
|
||||
# 检查用户和基础目录
|
||||
check_root
|
||||
create_jingrow_user
|
||||
check_jsite_base_dir
|
||||
|
||||
log_success "部署脚本执行完成!"
|
||||
# 根据mode执行不同的任务
|
||||
case "$MODE" in
|
||||
"deploy")
|
||||
# 完整部署模式
|
||||
log_info "执行完整部署模式..."
|
||||
echo ""
|
||||
log_info "部署参数:"
|
||||
echo " - 项目名称: $SITE_NAME"
|
||||
echo " - Git仓库: $GIT_REPO"
|
||||
echo " - Node.js版本: $NODE_VERSION"
|
||||
echo " - 跳过Docker: $SKIP_DOCKER"
|
||||
echo " - 跳过Traefik: $SKIP_TRAEFIK"
|
||||
echo " - 跳过依赖安装: $SKIP_DEPENDENCIES"
|
||||
echo " - 跳过PM2: $SKIP_PM2"
|
||||
echo " - 强制更新: $FORCE_UPDATE"
|
||||
echo ""
|
||||
log_info "端口配置:"
|
||||
echo " - 起始端口: $START_PORT"
|
||||
echo " - 端口增量: $PORT_INCREMENT"
|
||||
echo ""
|
||||
log_info ".env文件参数:"
|
||||
echo " - 网站URL: $SITE_URL"
|
||||
echo " - 站点名称: $SITE_NAME"
|
||||
echo " - 服务器URL: $SERVER_URL"
|
||||
echo " - 重新验证令牌: $REVALIDATE_TOKEN"
|
||||
echo " - API密钥: $API_KEY"
|
||||
echo " - API密钥: $API_SECRET"
|
||||
echo ""
|
||||
|
||||
if [ "$SKIP_DOCKER" = false ]; then
|
||||
install_docker
|
||||
else
|
||||
log_warning "跳过Docker安装"
|
||||
fi
|
||||
|
||||
install_nodejs
|
||||
|
||||
if ! clone_jsite_project; then
|
||||
log_error "项目克隆失败,部署终止"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create_env_file
|
||||
|
||||
if [ "$SKIP_DEPENDENCIES" = false ]; then
|
||||
if ! install_project_dependencies; then
|
||||
log_error "项目依赖安装失败,部署终止"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_warning "跳过项目依赖安装"
|
||||
fi
|
||||
|
||||
# PM2安装和项目启动(在项目准备完成后)
|
||||
if [ "$SKIP_PM2" = false ]; then
|
||||
log_info "开始PM2安装和项目启动..."
|
||||
install_pm2
|
||||
create_pm2_config
|
||||
if ! start_project_with_pm2; then
|
||||
log_error "PM2启动失败,部署终止"
|
||||
log_info "请检查以下内容:"
|
||||
log_info "1. 项目依赖是否正确安装"
|
||||
log_info "2. package.json中是否有start脚本"
|
||||
log_info "3. 项目端口是否被占用"
|
||||
log_info "4. 查看PM2日志: pm2 logs $SITE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_warning "跳过PM2安装和项目启动"
|
||||
fi
|
||||
|
||||
if [ "$SKIP_TRAEFIK" = false ]; then
|
||||
install_traefik
|
||||
create_traefik_website_config
|
||||
start_traefik
|
||||
else
|
||||
log_warning "跳过Traefik安装和启动"
|
||||
fi
|
||||
|
||||
show_deployment_info
|
||||
;;
|
||||
|
||||
"create")
|
||||
# 创建新网站
|
||||
create_site
|
||||
;;
|
||||
|
||||
"delete")
|
||||
# 删除网站
|
||||
delete_site
|
||||
;;
|
||||
|
||||
"build")
|
||||
# 构建网站
|
||||
build_site
|
||||
;;
|
||||
|
||||
"start")
|
||||
# 启动网站
|
||||
start_site
|
||||
;;
|
||||
|
||||
"stop")
|
||||
# 停止网站
|
||||
stop_site
|
||||
;;
|
||||
|
||||
"restart")
|
||||
# 重启网站
|
||||
restart_site
|
||||
;;
|
||||
|
||||
"status")
|
||||
# 查看网站状态
|
||||
show_site_status
|
||||
;;
|
||||
|
||||
"list")
|
||||
# 列出所有网站
|
||||
list_sites
|
||||
;;
|
||||
|
||||
"logs")
|
||||
# 查看网站日志
|
||||
show_site_logs
|
||||
;;
|
||||
|
||||
*)
|
||||
log_error "不支持的mode: $MODE"
|
||||
echo "支持的mode: deploy, create, delete, build, start, stop, restart, status, list, logs"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log_success "脚本执行完成!"
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
|
||||
878
jsite.sh
878
jsite.sh
@ -1,878 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ========================================
|
||||
# jsite网站管理脚本
|
||||
# 功能:新建、删除、构建、启动、停止、重启jsite网站
|
||||
# ========================================
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# ========================================
|
||||
# 默认配置
|
||||
# ========================================
|
||||
|
||||
# 基础配置
|
||||
DEFAULT_GIT_REPO="http://git.jingrow.com:3000/jsite/jingrow"
|
||||
JSITE_BASE_DIR="/home/jingrow/jsite"
|
||||
TRAEFIK_CONFIG_DIR="/home/jingrow/traefik-docker/conf.d/website"
|
||||
|
||||
# 默认配置参数
|
||||
SITE_NAME="jingrow"
|
||||
START_PORT=3001
|
||||
PORT_INCREMENT=1
|
||||
|
||||
# 网络配置
|
||||
PUBLIC_IP="" # 公网IP地址 (用于内网IP不可用时)
|
||||
|
||||
# .env文件参数
|
||||
SITE_URL="starrbud.com"
|
||||
REVALIDATE_TOKEN="535bc122f3e364c"
|
||||
SERVER_URL="https://admin.jingrow.com"
|
||||
API_KEY="535bc122f3e364c"
|
||||
API_SECRET="8629a3b12fc1cc2"
|
||||
|
||||
# ========================================
|
||||
# 日志函数
|
||||
# ========================================
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
# ========================================
|
||||
# 工具函数
|
||||
# ========================================
|
||||
|
||||
# 检查用户是否存在
|
||||
check_user() {
|
||||
if ! id "jingrow" &>/dev/null; then
|
||||
log_error "用户 jingrow 不存在,请先创建用户"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查jsite基础目录
|
||||
check_base_dir() {
|
||||
if [ ! -d "$JSITE_BASE_DIR" ]; then
|
||||
log_info "创建jsite基础目录: $JSITE_BASE_DIR"
|
||||
mkdir -p "$JSITE_BASE_DIR"
|
||||
chown "jingrow:jingrow" "$JSITE_BASE_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查网站目录是否存在
|
||||
check_site_exists() {
|
||||
local site_name="$1"
|
||||
[ -d "$JSITE_BASE_DIR/$site_name" ]
|
||||
}
|
||||
|
||||
# 获取网站状态
|
||||
get_site_status() {
|
||||
local site_name="$1"
|
||||
|
||||
if ! check_site_exists "$site_name"; then
|
||||
echo "not_exists"
|
||||
return
|
||||
fi
|
||||
|
||||
# 检查PM2进程状态
|
||||
local pm2_status=$(su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 list 2>/dev/null | grep -w '$site_name' | grep -c 'online' || echo '0'
|
||||
")
|
||||
|
||||
if [ "$pm2_status" != "0" ]; then
|
||||
echo "running"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
# 智能IP选择:优先使用内网IP,没有内网IP时使用公网IP
|
||||
get_optimal_host_ip() {
|
||||
local host_ip=""
|
||||
|
||||
# 方法1: 尝试获取内网IP
|
||||
local private_ip=$(ip route get 8.8.8.8 2>/dev/null | awk '{print $7}' | head -1)
|
||||
if [ -n "$private_ip" ] && [ "$private_ip" != "8.8.8.8" ]; then
|
||||
# 测试内网IP是否可达
|
||||
if ping -c 2 -W 1 "$private_ip" >/dev/null 2>&1; then
|
||||
host_ip="$private_ip"
|
||||
# 注意:这里不输出日志,避免污染返回值
|
||||
else
|
||||
# 注意:这里不输出日志,避免污染返回值
|
||||
:
|
||||
fi
|
||||
fi
|
||||
|
||||
# 方法2: 如果没有内网IP或内网IP不可达,使用公网IP
|
||||
if [ -z "$host_ip" ]; then
|
||||
if [ -n "$PUBLIC_IP" ]; then
|
||||
host_ip="$PUBLIC_IP"
|
||||
# 注意:这里不输出日志,避免污染返回值
|
||||
else
|
||||
# 尝试自动获取公网IP
|
||||
local auto_public_ip=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || curl -s --max-time 5 ipinfo.io/ip 2>/dev/null)
|
||||
if [ -n "$auto_public_ip" ]; then
|
||||
host_ip="$auto_public_ip"
|
||||
# 注意:这里不输出日志,避免污染返回值
|
||||
else
|
||||
# 最后回退:使用第一个可用IP
|
||||
host_ip=$(hostname -I | awk '{print $1}')
|
||||
# 注意:这里不输出日志,避免污染返回值
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$host_ip"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "用法: $0 <命令> [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " create 新建网站"
|
||||
echo " delete <name> 删除网站"
|
||||
echo " build <name> 构建网站"
|
||||
echo " start <name> 启动网站"
|
||||
echo " stop <name> 停止网站"
|
||||
echo " restart <name> 重启网站"
|
||||
echo " status <name> 查看网站状态"
|
||||
echo " list 列出所有网站"
|
||||
echo " logs <name> 查看网站日志"
|
||||
echo ""
|
||||
echo "create命令选项:"
|
||||
echo " --site-name NAME 网站名称 (必需)"
|
||||
echo " --git-repo URL Git仓库地址 (可选)"
|
||||
echo " --site-url URL 网站URL (可选)"
|
||||
echo ""
|
||||
echo "全局选项:"
|
||||
echo " -h, --help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 create --site-name mysite"
|
||||
echo " $0 create --site-name mysite --git-repo http://git.example.com/project"
|
||||
echo " $0 create --site-name mysite --site-url mysite.com"
|
||||
echo " $0 create --site-name mysite --git-repo http://git.example.com/project --site-url mysite.com"
|
||||
echo " $0 start mysite"
|
||||
echo " $0 stop mysite"
|
||||
echo " $0 restart mysite"
|
||||
echo " $0 status mysite"
|
||||
echo " $0 list"
|
||||
}
|
||||
|
||||
# 获取可用端口
|
||||
get_available_port() {
|
||||
local site_name="$1"
|
||||
local port_file="$JSITE_BASE_DIR/site_ports.json"
|
||||
|
||||
# 如果端口文件不存在,返回起始端口
|
||||
if [ ! -f "$port_file" ]; then
|
||||
echo "$START_PORT"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 检查网站是否已有分配的端口
|
||||
if command -v jq &> /dev/null; then
|
||||
local existing_port=$(jq -r ".$site_name // empty" "$port_file" 2>/dev/null)
|
||||
if [ -n "$existing_port" ] && [ "$existing_port" != "null" ] && [ "$existing_port" != "empty" ]; then
|
||||
echo "$existing_port"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 找到最大端口号并加1
|
||||
local max_port=$(jq -r 'max(.[])' "$port_file" 2>/dev/null || echo "$START_PORT")
|
||||
if [ -n "$max_port" ] && [ "$max_port" != "null" ]; then
|
||||
echo $((max_port + PORT_INCREMENT))
|
||||
else
|
||||
echo "$START_PORT"
|
||||
fi
|
||||
else
|
||||
# 如果没有jq,使用简单逻辑
|
||||
echo "$START_PORT"
|
||||
fi
|
||||
}
|
||||
|
||||
# 保存端口分配
|
||||
save_port_assignment() {
|
||||
local site_name="$1"
|
||||
local port="$2"
|
||||
local port_file="$JSITE_BASE_DIR/site_ports.json"
|
||||
|
||||
if command -v jq &> /dev/null; then
|
||||
if [ -f "$port_file" ]; then
|
||||
jq ". + {\"$site_name\": $port}" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file"
|
||||
else
|
||||
echo "{\"$site_name\": $port}" > "$port_file"
|
||||
fi
|
||||
else
|
||||
# 简单的JSON格式
|
||||
if [ -f "$port_file" ]; then
|
||||
grep -v "\"$site_name\"" "$port_file" > "${port_file}.tmp" 2>/dev/null || true
|
||||
sed -i "s/}$/,\"$site_name\": $port}/" "${port_file}.tmp" 2>/dev/null || echo "{\"$site_name\": $port}" > "${port_file}.tmp"
|
||||
mv "${port_file}.tmp" "$port_file"
|
||||
else
|
||||
echo "{\"$site_name\": $port}" > "$port_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
chown "jingrow:jingrow" "$port_file" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# 生成Host规则
|
||||
generate_host_rule() {
|
||||
local domain="$1"
|
||||
|
||||
# 移除协议前缀
|
||||
domain=$(echo "$domain" | sed -E 's|^https?://||')
|
||||
# 移除端口号
|
||||
domain=$(echo "$domain" | sed -E 's|:[0-9]+$||')
|
||||
|
||||
# 检查是否为一级域名
|
||||
if [[ "$domain" =~ ^[^.]+\.[^.]+$ ]]; then
|
||||
echo "Host(\`$domain\`) || Host(\`www.$domain\`)"
|
||||
else
|
||||
echo "Host(\`$domain\`)"
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建.env文件
|
||||
create_env_file() {
|
||||
local site_name="$1"
|
||||
local port="$2"
|
||||
|
||||
log_info "创建.env文件..."
|
||||
|
||||
cd "$JSITE_BASE_DIR/$site_name"
|
||||
|
||||
# 智能IP选择:优先使用内网IP,没有内网IP时使用公网IP(与traefik配置保持一致)
|
||||
local host_ip=$(get_optimal_host_ip)
|
||||
|
||||
# 输出IP选择结果
|
||||
if [[ "$host_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
log_success "使用IP地址: $host_ip"
|
||||
else
|
||||
log_warning "IP地址格式异常: $host_ip"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 构建PUBLIC_SITE_URL(优先使用内网IP,没有内网IP时使用公网IP)
|
||||
local public_site_url="http://$host_ip:$port"
|
||||
|
||||
# 检查.env文件是否已存在
|
||||
if [ -f "$JSITE_BASE_DIR/$site_name/.env" ]; then
|
||||
log_warning ".env文件已存在,将会覆盖..."
|
||||
fi
|
||||
|
||||
# 创建.env文件
|
||||
cat > "$JSITE_BASE_DIR/$site_name/.env" << EOF
|
||||
# 网站配置
|
||||
PUBLIC_SITE_URL=$public_site_url
|
||||
|
||||
# 重新验证令牌
|
||||
REVALIDATE_TOKEN=$REVALIDATE_TOKEN
|
||||
|
||||
# 项目配置
|
||||
JINGROW_SITE_NAME=$site_name
|
||||
|
||||
# 服务器配置
|
||||
JINGROW_SERVER_URL=$SERVER_URL
|
||||
JINGROW_API_KEY=$API_KEY
|
||||
JINGROW_API_SECRET=$API_SECRET
|
||||
EOF
|
||||
|
||||
# 设置文件权限
|
||||
chown "jingrow:jingrow" "$JSITE_BASE_DIR/$site_name/.env"
|
||||
chmod 600 "$JSITE_BASE_DIR/$site_name/.env"
|
||||
|
||||
log_success ".env文件创建完成 (端口: $port, PUBLIC_SITE_URL: $public_site_url)"
|
||||
}
|
||||
|
||||
# 创建traefik网站配置文件
|
||||
create_traefik_config() {
|
||||
local site_name="$1"
|
||||
local domain="$2"
|
||||
local port="$3"
|
||||
|
||||
# 检查traefik配置目录是否存在
|
||||
if [ ! -d "$TRAEFIK_CONFIG_DIR" ]; then
|
||||
log_warning "Traefik配置目录不存在: $TRAEFIK_CONFIG_DIR"
|
||||
log_info "请先确保Traefik已正确安装"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml"
|
||||
local host_rule=$(generate_host_rule "$domain")
|
||||
|
||||
# 获取本机IP地址
|
||||
local host_ip=$(get_optimal_host_ip)
|
||||
|
||||
# 创建配置文件
|
||||
cat > "$config_file" << EOF
|
||||
http:
|
||||
routers:
|
||||
main-https:
|
||||
rule: &host_rule $host_rule
|
||||
entryPoints:
|
||||
- websecure
|
||||
service: main-service
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
|
||||
main-http-redirect:
|
||||
rule: *host_rule
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
service: noop
|
||||
|
||||
services:
|
||||
main-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://$host_ip:$port"
|
||||
noop:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://127.0.0.1:65535" # 占位用,无实际后端,仅用于HTTP跳转
|
||||
|
||||
middlewares:
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
permanent: true
|
||||
EOF
|
||||
|
||||
chown "jingrow:jingrow" "$config_file" 2>/dev/null || true
|
||||
chmod 644 "$config_file"
|
||||
|
||||
log_success "Traefik配置文件已创建: $config_file"
|
||||
log_info "域名: $domain -> $host_ip:$port"
|
||||
}
|
||||
|
||||
# 删除traefik网站配置文件
|
||||
remove_traefik_config() {
|
||||
local site_name="$1"
|
||||
local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml"
|
||||
|
||||
if [ -f "$config_file" ]; then
|
||||
rm -f "$config_file"
|
||||
log_success "Traefik配置文件已删除: $config_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# 重启traefik服务以加载新配置
|
||||
restart_traefik() {
|
||||
local traefik_dir="/home/jingrow/traefik-docker"
|
||||
|
||||
if [ -d "$traefik_dir" ] && [ -f "$traefik_dir/docker-compose.yml" ]; then
|
||||
log_info "重启Traefik服务以加载新配置..."
|
||||
su - "jingrow" -c "cd '$traefik_dir' && docker compose restart" 2>/dev/null || {
|
||||
log_warning "无法重启Traefik服务,请手动重启"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# ========================================
|
||||
# 核心功能函数
|
||||
# ========================================
|
||||
|
||||
# 解析create命令的参数
|
||||
parse_create_args() {
|
||||
# 重置变量
|
||||
CREATE_SITE_NAME=""
|
||||
CREATE_GIT_REPO="$DEFAULT_GIT_REPO"
|
||||
CREATE_DOMAIN="$SITE_URL"
|
||||
|
||||
# 解析参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--site-name)
|
||||
CREATE_SITE_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--git-repo)
|
||||
CREATE_GIT_REPO="$2"
|
||||
shift 2
|
||||
;;
|
||||
--site-url)
|
||||
CREATE_DOMAIN="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "未知参数: $1"
|
||||
echo "使用 $0 create --help 查看帮助"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 验证必需参数
|
||||
if [ -z "$CREATE_SITE_NAME" ]; then
|
||||
log_error "必需参数: --site-name"
|
||||
echo "使用: $0 create --site-name <网站名称>"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 新建网站
|
||||
create_site() {
|
||||
local site_name="$CREATE_SITE_NAME"
|
||||
local git_repo="$CREATE_GIT_REPO"
|
||||
local domain="$CREATE_DOMAIN"
|
||||
|
||||
log_info "开始创建网站: $site_name"
|
||||
log_info "Git仓库: $git_repo"
|
||||
log_info "域名: $domain"
|
||||
|
||||
# 检查网站是否已存在
|
||||
if check_site_exists "$site_name"; then
|
||||
log_error "网站 $site_name 已存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 获取并分配端口
|
||||
local port=$(get_available_port "$site_name")
|
||||
|
||||
# 克隆项目
|
||||
log_info "克隆项目: $git_repo"
|
||||
if ! su - "jingrow" -c "cd '$JSITE_BASE_DIR' && git clone '$git_repo' '$site_name'"; then
|
||||
log_error "项目克隆失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查package.json是否存在
|
||||
if [ ! -f "$JSITE_BASE_DIR/$site_name/package.json" ]; then
|
||||
log_error "package.json文件不存在,这可能不是一个有效的Node.js项目"
|
||||
rm -rf "$JSITE_BASE_DIR/$site_name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 保存端口分配
|
||||
save_port_assignment "$site_name" "$port"
|
||||
|
||||
# 创建.env文件
|
||||
create_env_file "$site_name" "$port"
|
||||
|
||||
# 创建traefik配置文件
|
||||
if create_traefik_config "$site_name" "$domain" "$port"; then
|
||||
restart_traefik
|
||||
fi
|
||||
|
||||
log_success "网站 $site_name 创建成功"
|
||||
log_info "项目路径: $JSITE_BASE_DIR/$site_name"
|
||||
log_info "分配端口: $port"
|
||||
log_info "绑定域名: $domain"
|
||||
|
||||
# 提示下一步操作
|
||||
log_info "下一步操作:"
|
||||
echo " 1. 构建项目: $0 build $site_name"
|
||||
echo " 2. 启动项目: $0 start $site_name"
|
||||
}
|
||||
|
||||
# 删除网站
|
||||
delete_site() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_site_exists "$site_name"; then
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_warning "即将删除网站: $site_name"
|
||||
read -p "确认删除?(y/N): " confirm
|
||||
|
||||
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
||||
log_info "取消删除操作"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 停止PM2进程
|
||||
local status=$(get_site_status "$site_name")
|
||||
if [ "$status" = "running" ]; then
|
||||
log_info "停止运行中的进程..."
|
||||
stop_site "$site_name"
|
||||
fi
|
||||
|
||||
# 删除PM2配置
|
||||
su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 delete '$site_name' 2>/dev/null || true
|
||||
"
|
||||
|
||||
# 删除traefik配置文件
|
||||
remove_traefik_config "$site_name"
|
||||
|
||||
# 删除端口分配记录
|
||||
local port_file="$JSITE_BASE_DIR/site_ports.json"
|
||||
if [ -f "$port_file" ] && command -v jq &> /dev/null; then
|
||||
jq "del(.$site_name)" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file"
|
||||
chown "jingrow:jingrow" "$port_file" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 删除项目目录
|
||||
log_info "删除项目目录: $JSITE_BASE_DIR/$site_name"
|
||||
rm -rf "$JSITE_BASE_DIR/$site_name"
|
||||
|
||||
# 重启traefik以移除配置
|
||||
restart_traefik
|
||||
|
||||
log_success "网站 $site_name 已删除"
|
||||
}
|
||||
|
||||
# 构建网站
|
||||
build_site() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_site_exists "$site_name"; then
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "构建网站: $site_name"
|
||||
|
||||
# 安装依赖
|
||||
log_info "安装依赖..."
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
cd '$JSITE_BASE_DIR/$site_name'
|
||||
npm install
|
||||
"; then
|
||||
log_error "依赖安装失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 构建项目
|
||||
log_info "构建项目..."
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
cd '$JSITE_BASE_DIR/$site_name'
|
||||
npm run build
|
||||
"; then
|
||||
log_error "项目构建失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "网站 $site_name 构建完成"
|
||||
}
|
||||
|
||||
# 启动网站
|
||||
start_site() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! check_site_exists "$site_name"; then
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status=$(get_site_status "$site_name")
|
||||
if [ "$status" = "running" ]; then
|
||||
log_warning "网站 $site_name 已在运行中"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 获取分配的端口
|
||||
local port=$(get_available_port "$site_name")
|
||||
|
||||
log_info "启动网站: $site_name (端口: $port)"
|
||||
|
||||
# 检查是否有构建产物
|
||||
if [ ! -d "$JSITE_BASE_DIR/$site_name/.next" ] && [ ! -d "$JSITE_BASE_DIR/$site_name/dist" ] && [ ! -d "$JSITE_BASE_DIR/$site_name/build" ]; then
|
||||
log_warning "未找到构建产物,建议先执行构建: $0 build $site_name"
|
||||
fi
|
||||
|
||||
# 使用PM2启动,设置端口环境变量
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
cd '$JSITE_BASE_DIR/$site_name'
|
||||
PORT=$port pm2 start npm --name '$site_name' -- start
|
||||
"; then
|
||||
log_error "网站启动失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 等待启动完成
|
||||
sleep 3
|
||||
|
||||
local new_status=$(get_site_status "$site_name")
|
||||
if [ "$new_status" = "running" ]; then
|
||||
log_success "网站 $site_name 启动成功 (端口: $port)"
|
||||
else
|
||||
log_error "网站 $site_name 启动失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止网站
|
||||
stop_site() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status=$(get_site_status "$site_name")
|
||||
if [ "$status" = "not_exists" ]; then
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$status" = "stopped" ]; then
|
||||
log_warning "网站 $site_name 已停止"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "停止网站: $site_name"
|
||||
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 stop '$site_name'
|
||||
"; then
|
||||
log_error "网站停止失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "网站 $site_name 已停止"
|
||||
}
|
||||
|
||||
# 重启网站
|
||||
restart_site() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status=$(get_site_status "$site_name")
|
||||
if [ "$status" = "not_exists" ]; then
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "重启网站: $site_name"
|
||||
|
||||
if [ "$status" = "running" ]; then
|
||||
if ! su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 restart '$site_name'
|
||||
"; then
|
||||
log_error "网站重启失败"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# 如果未运行,则启动
|
||||
start_site "$site_name"
|
||||
return $?
|
||||
fi
|
||||
|
||||
log_success "网站 $site_name 重启成功"
|
||||
}
|
||||
|
||||
# 查看网站状态
|
||||
show_site_status() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status=$(get_site_status "$site_name")
|
||||
|
||||
case "$status" in
|
||||
"not_exists")
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
;;
|
||||
"running")
|
||||
log_success "网站 $site_name 正在运行"
|
||||
;;
|
||||
"stopped")
|
||||
log_warning "网站 $site_name 已停止"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 显示PM2详细状态
|
||||
if [ "$status" != "not_exists" ]; then
|
||||
log_info "PM2状态:"
|
||||
su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 list | grep -E '(App name|$site_name)' || echo '未找到PM2进程'
|
||||
"
|
||||
fi
|
||||
}
|
||||
|
||||
# 列出所有网站
|
||||
list_sites() {
|
||||
log_info "jsite网站列表:"
|
||||
|
||||
if [ ! -d "$JSITE_BASE_DIR" ] || [ -z "$(ls -A "$JSITE_BASE_DIR" 2>/dev/null)" ]; then
|
||||
echo " 暂无网站"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for site_dir in "$JSITE_BASE_DIR"/*; do
|
||||
if [ -d "$site_dir" ]; then
|
||||
local site_name=$(basename "$site_dir")
|
||||
local status=$(get_site_status "$site_name")
|
||||
|
||||
case "$status" in
|
||||
"running")
|
||||
echo " - $site_name (运行中)"
|
||||
;;
|
||||
"stopped")
|
||||
echo " - $site_name (已停止)"
|
||||
;;
|
||||
*)
|
||||
echo " - $site_name (未知状态)"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 查看网站日志
|
||||
show_site_logs() {
|
||||
local site_name="$1"
|
||||
|
||||
if [ -z "$site_name" ]; then
|
||||
log_error "请指定网站名称"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status=$(get_site_status "$site_name")
|
||||
if [ "$status" = "not_exists" ]; then
|
||||
log_error "网站 $site_name 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "网站 $site_name 的日志:"
|
||||
su - "jingrow" -c "
|
||||
export NVM_DIR=\"\$HOME/.nvm\"
|
||||
[ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\"
|
||||
pm2 logs '$site_name'
|
||||
"
|
||||
}
|
||||
|
||||
# ========================================
|
||||
# 主程序
|
||||
# ========================================
|
||||
|
||||
main() {
|
||||
# 检查参数
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 处理帮助选项
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# 检查用户和基础目录
|
||||
check_user
|
||||
check_base_dir
|
||||
|
||||
# 解析命令
|
||||
local command="$1"
|
||||
shift # 移除命令,剩下的都是参数
|
||||
|
||||
case "$command" in
|
||||
create)
|
||||
parse_create_args "$@"
|
||||
create_site
|
||||
;;
|
||||
delete)
|
||||
local site_name="$1"
|
||||
delete_site "$site_name"
|
||||
;;
|
||||
build)
|
||||
local site_name="$1"
|
||||
build_site "$site_name"
|
||||
;;
|
||||
start)
|
||||
local site_name="$1"
|
||||
start_site "$site_name"
|
||||
;;
|
||||
stop)
|
||||
local site_name="$1"
|
||||
stop_site "$site_name"
|
||||
;;
|
||||
restart)
|
||||
local site_name="$1"
|
||||
restart_site "$site_name"
|
||||
;;
|
||||
status)
|
||||
local site_name="$1"
|
||||
show_site_status "$site_name"
|
||||
;;
|
||||
list)
|
||||
list_sites
|
||||
;;
|
||||
logs)
|
||||
local site_name="$1"
|
||||
show_site_logs "$site_name"
|
||||
;;
|
||||
*)
|
||||
log_error "未知命令: $command"
|
||||
echo ""
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
Loading…
x
Reference in New Issue
Block a user