Compare commits
2 Commits
v2026.05.1
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
502d1341fb | |
|
|
78ff4b01b6 |
|
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
||||||
111
README.md
111
README.md
|
|
@ -1,34 +1,61 @@
|
||||||
# Scripts — 部署与运维脚本
|
# Scripts — 部署与运维脚本
|
||||||
|
|
||||||
多站点 Docker 项目的运维脚本集合,涵盖本地开发管理、云端部署、数据库备份恢复、内容编辑等。
|
多站点 Docker 项目的运维脚本集合,涵盖本地开发管理、云服务器运维、云端部署、数据库备份恢复。
|
||||||
|
|
||||||
## 脚本清单
|
## 脚本清单
|
||||||
|
|
||||||
| 脚本 | 用途 | 目标环境 |
|
| 脚本 | 用途 | 目标环境 |
|
||||||
|------|------|---------|
|
|------|------|---------|
|
||||||
| `local.sh` | 本地站点启停管理 | 本地 Docker |
|
| `sites-manage-local.sh` | 本地站点启停(npm run dev + Docker) | 本地 |
|
||||||
|
| `sites-manage-cloud.sh` | 云服务器站点启停(SSH / 服务器本地运行) | 云端 |
|
||||||
| `deploy-ai.sh` | 部署站点到 tlyq.ai(txjp 服务器) | 云端 |
|
| `deploy-ai.sh` | 部署站点到 tlyq.ai(txjp 服务器) | 云端 |
|
||||||
| `deploy-cc.sh` | 部署站点到 tlyq.cc(tgz 服务器) | 云端 |
|
| `deploy-cc.sh` | 部署站点到 tlyq.cc(tgz 服务器) | 云端 |
|
||||||
| `edit-site-cc.sh` | www.tlyq.cc 官网内容管理(新闻、联系方式等) | 云端 |
|
| `backup-db.sh` | 备份数据库(assets / issue / ldap) | 云端 → 本地 |
|
||||||
| `backup-db.sh` | 备份 issue-ai 和 assets-ai 数据库 | 云端 → 本地 |
|
| `restore-db.sh` | 恢复数据库到本地或云端 | 本地 / 云端 |
|
||||||
| `restore-db.sh` | 恢复数据库(本地或云端) | 本地 / 云端 |
|
| `edit-site-cc.sh` | www.tlyq.cc 官网内容管理 | 云端 |
|
||||||
|
|
||||||
## 详细说明
|
---
|
||||||
|
|
||||||
### local.sh — 本地站点管理
|
## sites-manage-local.sh — 本地站点管理
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/local.sh start # 启动所有站点
|
bash scripts/sites-manage-local.sh start # 启动全部
|
||||||
bash scripts/local.sh stop # 停止所有站点
|
bash scripts/sites-manage-local.sh start ai # 启动 ai 全部
|
||||||
bash scripts/local.sh restart issue # 重启指定站点
|
bash scripts/sites-manage-local.sh start ldap oa # 启动指定站点
|
||||||
bash scripts/local.sh status # 查看运行状态
|
bash scripts/sites-manage-local.sh stop oa # 停止指定站点
|
||||||
|
bash scripts/sites-manage-local.sh restart ldap # 重启指定站点
|
||||||
|
bash scripts/sites-manage-local.sh status # 查看状态
|
||||||
```
|
```
|
||||||
|
|
||||||
支持站点:www、cloud、token、issue、assets、ldap、oa。
|
站点启动方式:
|
||||||
|
|
||||||
启动前自动检查是否有线上域名残留,防止本地开发时跳转到生产环境。
|
| 站点 | 方式 | 端口 |
|
||||||
|
|------|------|------|
|
||||||
|
| ldap | Docker | 6178(Web UI)、3890(LDAP) |
|
||||||
|
| oa | `npm run dev`(宿主机) | 6179 |
|
||||||
|
| issue | `npm run dev`(宿主机) | 6176 |
|
||||||
|
| assets | `npm run dev`(宿主机) | 6177 |
|
||||||
|
| www / cloud / token | Docker | 6173–6175 |
|
||||||
|
|
||||||
### deploy-ai.sh — tlyq.ai 部署
|
## sites-manage-cloud.sh — 云服务器站点管理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/sites-manage-cloud.sh start ai # 启动全部
|
||||||
|
bash scripts/sites-manage-cloud.sh restart nginx # 重启 nginx
|
||||||
|
bash scripts/sites-manage-cloud.sh stop oa issue # 停指定站点
|
||||||
|
bash scripts/sites-manage-cloud.sh status # 查看状态
|
||||||
|
```
|
||||||
|
|
||||||
|
自动检测运行环境:本地 Mac 运行时通过 SSH 操作 txjp,在云服务器上运行时直接操作本地 Docker。也可显式指定:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OA_REMOTE=local bash scripts/sites-manage-cloud.sh status # 强制本地模式
|
||||||
|
OA_REMOTE=ssh bash scripts/sites-manage-cloud.sh status # 强制 SSH 模式
|
||||||
|
```
|
||||||
|
|
||||||
|
站点全部以 Docker 方式运行,依赖关系:`ldap → oa / issue / assets → nginx`。
|
||||||
|
|
||||||
|
## deploy-ai.sh — tlyq.ai 部署
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/deploy-ai.sh # 自动检测:增量 or 完整构建
|
bash scripts/deploy-ai.sh # 自动检测:增量 or 完整构建
|
||||||
|
|
@ -36,60 +63,52 @@ bash scripts/deploy-ai.sh --force # 强制完整构建
|
||||||
bash scripts/deploy-ai.sh --restart # 仅重启容器
|
bash scripts/deploy-ai.sh --restart # 仅重启容器
|
||||||
```
|
```
|
||||||
|
|
||||||
交互式选择站点后执行:源码打包 → 上传 → 服务器构建 → 容器重启。部署 issue/assets 后自动验证 API 连通性(带重试机制)。
|
交互式选择站点(1–7):www / cloud / token / issue / assets / oa / ldap。
|
||||||
|
|
||||||
> 详细文档见 [deploy-ai-readme.md](deploy-ai-readme.md)
|
流程:源码打包 → 上传 → 服务器 `npm install` → `npm run build` → 容器重建。部署后自动验证。
|
||||||
|
|
||||||
### deploy-cc.sh — tlyq.cc 部署
|
LDAP 部署特殊处理:上传 Dockerfile + docker-compose.yml,自动创建 .env,去除端口映射,`--build` 构建含 sqlite3 的镜像。
|
||||||
|
|
||||||
```bash
|
## backup-db.sh — 数据库备份
|
||||||
bash scripts/deploy-cc.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
交互式选择站点后部署到 tlyq.cc(tgz 服务器),逻辑与 deploy-ai.sh 类似。
|
|
||||||
|
|
||||||
### edit-site-cc.sh — tlyq.cc 官网内容编辑
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash scripts/edit-site-cc.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
交互式菜单编辑 www.tlyq.cc 的联系方式、新闻动态、时间线等内容,修改后自动部署。
|
|
||||||
|
|
||||||
### backup-db.sh — 数据库备份
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/backup-db.sh
|
bash scripts/backup-db.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
一键备份 issue-ai 和 assets-ai 的生产数据库:
|
一键备份三个数据库:
|
||||||
|
|
||||||
1. 执行 WAL checkpoint 确保数据一致性
|
| 数据库 | 服务 | 内容 |
|
||||||
2. 在云端 `/root/docker/db-backups/` 保留一份
|
|--------|------|------|
|
||||||
3. 下载到本地 `~/programs/docker/db-backups/`
|
| assets-{时间戳}.db | assets-ai | 设备资产 |
|
||||||
4. 验证备份文件为有效 SQLite 数据库
|
| issue-{时间戳}.db | issue-ai | 工单、配件 |
|
||||||
|
| ldap-{时间戳}.db | lldap | **全部用户账号** |
|
||||||
|
|
||||||
### restore-db.sh — 数据库恢复
|
流程:WAL checkpoint → 云端打包 → 云端持久保留 → 下载到本地 → 完整性验证。
|
||||||
|
|
||||||
|
> 详细文档见 [BACKUP-RESTORE.md](../docs/BACKUP-RESTORE.md)
|
||||||
|
|
||||||
|
## restore-db.sh — 数据库恢复
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 恢复本地数据库
|
|
||||||
bash scripts/restore-db.sh local issue issue-20260515_163059.db
|
|
||||||
|
|
||||||
# 恢复云端数据库
|
# 恢复云端数据库
|
||||||
bash scripts/restore-db.sh cloud assets assets-20260515_163059.db
|
bash scripts/restore-db.sh cloud issue issue-20260518_1200.db
|
||||||
|
bash scripts/restore-db.sh cloud ldap ldap-20260518_1200.db
|
||||||
|
|
||||||
|
# 恢复本地数据库
|
||||||
|
bash scripts/restore-db.sh local assets assets-20260518_1200.db
|
||||||
```
|
```
|
||||||
|
|
||||||
恢复流程:停止容器 → 备份当前库 → 替换 → 清理 WAL/SHM → 启动 → 验证。支持 `issue` 和 `assets` 两个服务。
|
支持服务:`assets` / `issue` / `ldap`。流程:检查 → 验证 → 确认 → 备份当前库 → 停止容器 → 替换 → 清理 WAL → 启动 → 验证。
|
||||||
|
|
||||||
## 备份存储路径
|
## 备份存储路径
|
||||||
|
|
||||||
| 位置 | 路径 |
|
| 位置 | 路径 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 云端持久备份 | `/root/docker/db-backups/` |
|
| 云端持久备份 | `/root/docker/db-backups/` |
|
||||||
| 本地备份 | `~/programs/docker/db-backups/` |
|
| 本地备份 | `db-backups/` |
|
||||||
| 云端临时(传输中) | `/tmp/db-backup/`(完成后清理) |
|
| 云端临时 | `/tmp/db-backup/`(完成后清理) |
|
||||||
|
|
||||||
## 依赖
|
## 依赖
|
||||||
|
|
||||||
- **本地**:macOS / Linux,bash,ssh(配置 `txjp` 和 `tgz` 别名),scp,docker
|
- **本地**:macOS / Linux,bash,ssh(配置 `txjp` 和 `tgz` 别名),scp,docker,sqlite3
|
||||||
- **云端**:Docker Compose,Node.js,better-sqlite3
|
- **云端**:Docker Compose,Node.js,better-sqlite3
|
||||||
|
|
|
||||||
19
backup-db.sh
19
backup-db.sh
|
|
@ -16,11 +16,14 @@ log "执行 WAL checkpoint (assets)..."
|
||||||
ssh txjp "docker exec assets-ai node -e \"const D=require('better-sqlite3');const db=new D('/app/data/assets.db');db.pragma('wal_checkpoint(TRUNCATE)');db.close();\""
|
ssh txjp "docker exec assets-ai node -e \"const D=require('better-sqlite3');const db=new D('/app/data/assets.db');db.pragma('wal_checkpoint(TRUNCATE)');db.close();\""
|
||||||
log "执行 WAL checkpoint (issue)..."
|
log "执行 WAL checkpoint (issue)..."
|
||||||
ssh txjp "docker exec issue-ai node -e \"const D=require('better-sqlite3');const db=new D('/app/data/issue.db');db.pragma('wal_checkpoint(TRUNCATE)');db.close();\""
|
ssh txjp "docker exec issue-ai node -e \"const D=require('better-sqlite3');const db=new D('/app/data/issue.db');db.pragma('wal_checkpoint(TRUNCATE)');db.close();\""
|
||||||
|
log "执行 WAL checkpoint (LLDAP)..."
|
||||||
|
ssh txjp "docker exec lldap sqlite3 /data/users.db 'PRAGMA wal_checkpoint(TRUNCATE);' 2>/dev/null || true"
|
||||||
|
|
||||||
# 云端打包
|
# 云端打包(使用 docker cp 从容器内复制,确保拿到真实数据而非旧宿主机文件)
|
||||||
ssh txjp "mkdir -p /tmp/db-backup && \
|
ssh txjp "mkdir -p /tmp/db-backup && \
|
||||||
cp /root/docker/assets-ai/data/assets.db /tmp/db-backup/assets-${TIMESTAMP}.db && \
|
docker cp assets-ai:/app/data/assets.db /tmp/db-backup/assets-${TIMESTAMP}.db && \
|
||||||
cp /root/docker/issue-ai/data/issue.db /tmp/db-backup/issue-${TIMESTAMP}.db"
|
docker cp issue-ai:/app/data/issue.db /tmp/db-backup/issue-${TIMESTAMP}.db && \
|
||||||
|
docker cp lldap:/data/users.db /tmp/db-backup/ldap-${TIMESTAMP}.db"
|
||||||
|
|
||||||
# 确保本地备份目录存在
|
# 确保本地备份目录存在
|
||||||
mkdir -p "${LOCAL_DIR}"
|
mkdir -p "${LOCAL_DIR}"
|
||||||
|
|
@ -29,7 +32,8 @@ mkdir -p "${LOCAL_DIR}"
|
||||||
CLOUD_DIR="/root/docker/db-backups"
|
CLOUD_DIR="/root/docker/db-backups"
|
||||||
ssh txjp "mkdir -p ${CLOUD_DIR} && \
|
ssh txjp "mkdir -p ${CLOUD_DIR} && \
|
||||||
cp /tmp/db-backup/assets-${TIMESTAMP}.db ${CLOUD_DIR}/assets-${TIMESTAMP}.db && \
|
cp /tmp/db-backup/assets-${TIMESTAMP}.db ${CLOUD_DIR}/assets-${TIMESTAMP}.db && \
|
||||||
cp /tmp/db-backup/issue-${TIMESTAMP}.db ${CLOUD_DIR}/issue-${TIMESTAMP}.db"
|
cp /tmp/db-backup/issue-${TIMESTAMP}.db ${CLOUD_DIR}/issue-${TIMESTAMP}.db && \
|
||||||
|
cp /tmp/db-backup/ldap-${TIMESTAMP}.db ${CLOUD_DIR}/ldap-${TIMESTAMP}.db"
|
||||||
log "云端备份已保存至: ${CLOUD_DIR}"
|
log "云端备份已保存至: ${CLOUD_DIR}"
|
||||||
|
|
||||||
# 复制到本地
|
# 复制到本地
|
||||||
|
|
@ -37,10 +41,11 @@ log "复制到本地..."
|
||||||
mkdir -p "${LOCAL_DIR}"
|
mkdir -p "${LOCAL_DIR}"
|
||||||
scp "txjp:/tmp/db-backup/assets-${TIMESTAMP}.db" "${LOCAL_DIR}/"
|
scp "txjp:/tmp/db-backup/assets-${TIMESTAMP}.db" "${LOCAL_DIR}/"
|
||||||
scp "txjp:/tmp/db-backup/issue-${TIMESTAMP}.db" "${LOCAL_DIR}/"
|
scp "txjp:/tmp/db-backup/issue-${TIMESTAMP}.db" "${LOCAL_DIR}/"
|
||||||
|
scp "txjp:/tmp/db-backup/ldap-${TIMESTAMP}.db" "${LOCAL_DIR}/"
|
||||||
|
|
||||||
# 验证备份文件为有效 SQLite 数据库
|
# 验证备份文件为有效 SQLite 数据库
|
||||||
log "验证备份完整性..."
|
log "验证备份完整性..."
|
||||||
for f in "${LOCAL_DIR}/assets-${TIMESTAMP}.db" "${LOCAL_DIR}/issue-${TIMESTAMP}.db"; do
|
for f in "${LOCAL_DIR}/assets-${TIMESTAMP}.db" "${LOCAL_DIR}/issue-${TIMESTAMP}.db" "${LOCAL_DIR}/ldap-${TIMESTAMP}.db"; do
|
||||||
if ! sqlite3 "$f" "SELECT count(*) FROM sqlite_master" > /dev/null 2>&1; then
|
if ! sqlite3 "$f" "SELECT count(*) FROM sqlite_master" > /dev/null 2>&1; then
|
||||||
echo "[错误] 备份文件无效或损坏: $f"
|
echo "[错误] 备份文件无效或损坏: $f"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -53,6 +58,6 @@ ssh txjp "rm -rf /tmp/db-backup"
|
||||||
|
|
||||||
log "备份完成:${TIMESTAMP}"
|
log "备份完成:${TIMESTAMP}"
|
||||||
echo " 云端: ${CLOUD_DIR}/"
|
echo " 云端: ${CLOUD_DIR}/"
|
||||||
ssh txjp "ls -lh ${CLOUD_DIR}/assets-${TIMESTAMP}.db ${CLOUD_DIR}/issue-${TIMESTAMP}.db 2>/dev/null"
|
ssh txjp "ls -lh ${CLOUD_DIR}/assets-${TIMESTAMP}.db ${CLOUD_DIR}/issue-${TIMESTAMP}.db ${CLOUD_DIR}/ldap-${TIMESTAMP}.db 2>/dev/null"
|
||||||
echo " 本地:"
|
echo " 本地:"
|
||||||
ls -lh "${LOCAL_DIR}"/assets-${TIMESTAMP}.db "${LOCAL_DIR}"/issue-${TIMESTAMP}.db 2>/dev/null
|
ls -lh "${LOCAL_DIR}"/assets-${TIMESTAMP}.db "${LOCAL_DIR}"/issue-${TIMESTAMP}.db "${LOCAL_DIR}"/ldap-${TIMESTAMP}.db 2>/dev/null
|
||||||
|
|
|
||||||
28
deploy-ai.sh
28
deploy-ai.sh
|
|
@ -66,8 +66,9 @@ echo " 3) token.tlyq.ai (Token工厂)"
|
||||||
echo " 4) issue.tlyq.ai (工单系统)"
|
echo " 4) issue.tlyq.ai (工单系统)"
|
||||||
echo " 5) assets.tlyq.ai (资产管理系统)"
|
echo " 5) assets.tlyq.ai (资产管理系统)"
|
||||||
echo " 6) oa.tlyq.ai (OA 统一门户 — 含 nginx 配置)"
|
echo " 6) oa.tlyq.ai (OA 统一门户 — 含 nginx 配置)"
|
||||||
|
echo " 7) ldap.tlyq.ai (LLDAP 用户目录服务)"
|
||||||
echo ""
|
echo ""
|
||||||
printf "请输入编号 (1/2/3/4/5/6): "
|
printf "请输入编号 (1/2/3/4/5/6/7): "
|
||||||
read choice
|
read choice
|
||||||
|
|
||||||
SITE=""; LOCAL_DIR=""; REMOTE_DIR=""; CONTAINER=""
|
SITE=""; LOCAL_DIR=""; REMOTE_DIR=""; CONTAINER=""
|
||||||
|
|
@ -78,6 +79,7 @@ case "$choice" in
|
||||||
4) SITE="issue"; LOCAL_DIR="/Users/niuniu/programs/docker/issue-ai"; REMOTE_DIR="/root/docker/issue-ai"; CONTAINER="issue-ai" ;;
|
4) SITE="issue"; LOCAL_DIR="/Users/niuniu/programs/docker/issue-ai"; REMOTE_DIR="/root/docker/issue-ai"; CONTAINER="issue-ai" ;;
|
||||||
5) SITE="assets"; LOCAL_DIR="/Users/niuniu/programs/docker/assets-ai"; REMOTE_DIR="/root/docker/assets-ai"; CONTAINER="assets-ai" ;;
|
5) SITE="assets"; LOCAL_DIR="/Users/niuniu/programs/docker/assets-ai"; REMOTE_DIR="/root/docker/assets-ai"; CONTAINER="assets-ai" ;;
|
||||||
6) SITE="oa"; LOCAL_DIR="/Users/niuniu/programs/docker/oa-ai"; REMOTE_DIR="/root/docker/oa-ai"; CONTAINER="oa-ai" ;;
|
6) SITE="oa"; LOCAL_DIR="/Users/niuniu/programs/docker/oa-ai"; REMOTE_DIR="/root/docker/oa-ai"; CONTAINER="oa-ai" ;;
|
||||||
|
7) SITE="ldap"; LOCAL_DIR="/Users/niuniu/programs/docker/ldap-ai"; REMOTE_DIR="/root/docker/ldap-ai"; CONTAINER="lldap" ;;
|
||||||
*) echo "无效选择"; exit 1 ;;
|
*) echo "无效选择"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -419,6 +421,23 @@ CHECKEOF
|
||||||
info "部署 nginx 配置..."
|
info "部署 nginx 配置..."
|
||||||
ssh txjp "cp $REMOTE_DIR/../nginx-proxy-ai/conf.d/oa-ai.conf /root/docker/nginx-proxy-ai/conf.d/ 2>/dev/null || true
|
ssh txjp "cp $REMOTE_DIR/../nginx-proxy-ai/conf.d/oa-ai.conf /root/docker/nginx-proxy-ai/conf.d/ 2>/dev/null || true
|
||||||
docker exec nginx-ai nginx -t && docker exec nginx-ai nginx -s reload" ;;
|
docker exec nginx-ai nginx -t && docker exec nginx-ai nginx -s reload" ;;
|
||||||
|
ldap)
|
||||||
|
info "部署 LLDAP..."
|
||||||
|
scp "$LOCAL_DIR/docker-compose.yml" txjp:$REMOTE_DIR/docker-compose.yml
|
||||||
|
scp "$LOCAL_DIR/Dockerfile" txjp:$REMOTE_DIR/Dockerfile
|
||||||
|
ssh txjp "cd $REMOTE_DIR && \
|
||||||
|
if [ ! -f .env ]; then \
|
||||||
|
echo 'LLDAP_JWT_SECRET='\$(docker exec lldap printenv LLDAP_JWT_SECRET 2>/dev/null) > .env && \
|
||||||
|
echo 'LLDAP_LDAP_USER_PASS='\$(docker exec lldap printenv LLDAP_LDAP_USER_PASS 2>/dev/null) >> .env && \
|
||||||
|
echo 'LLDAP_LDAP_BASE_DN=dc=tlyq,dc=ai' >> .env && \
|
||||||
|
echo 'LLDAP_HTTP_PORT=17170' >> .env && \
|
||||||
|
echo 'LLDAP_LDAP_PORT=3890' >> .env && \
|
||||||
|
echo 'LLDAP_DATABASE_PATH=/data/users.db' >> .env && \
|
||||||
|
echo 'LLDAP_ADMIN_PASSWORD='\$(docker exec lldap printenv LLDAP_ADMIN_PASSWORD 2>/dev/null) >> .env && \
|
||||||
|
fi && \
|
||||||
|
sed -i '/3890:3890/d' docker-compose.yml 2>/dev/null || true && \
|
||||||
|
docker compose up -d --build && docker compose restart"
|
||||||
|
log "LLDAP 已部署" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
@ -437,9 +456,14 @@ case "$SITE" in
|
||||||
issue) URL="https://issue.tlyq.ai" ;;
|
issue) URL="https://issue.tlyq.ai" ;;
|
||||||
assets) URL="https://assets.tlyq.ai" ;;
|
assets) URL="https://assets.tlyq.ai" ;;
|
||||||
oa) URL="https://oa.tlyq.ai" ;;
|
oa) URL="https://oa.tlyq.ai" ;;
|
||||||
|
ldap) URL="http://localhost:6178" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
STATUS=$(ssh txjp "curl -s -o /dev/null -w '%{http_code}' -k '$URL' 2>/dev/null" 2>/dev/null || echo "???")
|
if [[ "$SITE" == "ldap" ]]; then
|
||||||
|
STATUS=$(ssh txjp "docker exec lldap wget -q -O - http://127.0.0.1:17170 > /dev/null 2>&1 && echo 200 || echo 000" 2>/dev/null || echo "???")
|
||||||
|
else
|
||||||
|
STATUS=$(ssh txjp "curl -s -o /dev/null -w '%{http_code}' -k '$URL' 2>/dev/null" 2>/dev/null || echo "???")
|
||||||
|
fi
|
||||||
if [[ "$STATUS" == "200" || "$STATUS" == "307" || "$STATUS" == "301" ]]; then
|
if [[ "$STATUS" == "200" || "$STATUS" == "307" || "$STATUS" == "301" ]]; then
|
||||||
log "部署成功!总耗时: ${TOTAL_DUR}s | 访问 $URL"
|
log "部署成功!总耗时: ${TOTAL_DUR}s | 访问 $URL"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,23 @@
|
||||||
# 恢复本地数据库: bash scripts/restore-db.sh local <服务名> <备份文件名>
|
# 恢复本地数据库: bash scripts/restore-db.sh local <服务名> <备份文件名>
|
||||||
# 恢复云端数据库: bash scripts/restore-db.sh cloud <服务名> <备份文件名>
|
# 恢复云端数据库: bash scripts/restore-db.sh cloud <服务名> <备份文件名>
|
||||||
#
|
#
|
||||||
# 服务名: issue / assets
|
# 服务名: issue / assets / ldap
|
||||||
#
|
#
|
||||||
# 示例:
|
# 示例:
|
||||||
# bash scripts/restore-db.sh local issue issue-2026-04-29_1753.db
|
# bash scripts/restore-db.sh local issue issue-2026-04-29_1753.db
|
||||||
# bash scripts/restore-db.sh cloud assets assets-2026-04-29_1753.db
|
# bash scripts/restore-db.sh cloud assets assets-2026-04-29_1753.db
|
||||||
|
# bash scripts/restore-db.sh cloud ldap ldap-2026-05-15_1200.db
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# ---- 参数检查 ----
|
# ---- 参数检查 ----
|
||||||
if [ $# -lt 3 ]; then
|
if [ $# -lt 3 ]; then
|
||||||
echo "用法: $0 <local|cloud> <issue|assets> <备份文件名>"
|
echo "用法: $0 <local|cloud> <issue|assets|ldap> <备份文件名>"
|
||||||
echo ""
|
echo ""
|
||||||
echo "示例:"
|
echo "示例:"
|
||||||
echo " $0 local issue issue-2026-04-29_1753.db"
|
echo " $0 local issue issue-2026-04-29_1753.db"
|
||||||
|
echo " $0 cloud ldap ldap-2026-05-15_1200.db"
|
||||||
echo " $0 cloud assets assets-2026-04-29_1753.db"
|
echo " $0 cloud assets assets-2026-04-29_1753.db"
|
||||||
echo ""
|
echo ""
|
||||||
echo "可用备份文件:"
|
echo "可用备份文件:"
|
||||||
|
|
@ -52,8 +54,14 @@ case $SERVICE in
|
||||||
DB_PATH_IN_CONTAINER="/app/data/assets.db"
|
DB_PATH_IN_CONTAINER="/app/data/assets.db"
|
||||||
LOCAL_DB_PATH="/Users/niuniu/programs/docker/assets-ai/data/assets.db"
|
LOCAL_DB_PATH="/Users/niuniu/programs/docker/assets-ai/data/assets.db"
|
||||||
;;
|
;;
|
||||||
|
ldap)
|
||||||
|
PROJECT_DIR="ldap-ai"
|
||||||
|
CONTAINER="lldap"
|
||||||
|
DB_PATH_IN_CONTAINER="/data/users.db"
|
||||||
|
LOCAL_DB_PATH="/Users/niuniu/programs/docker/ldap-ai/data/lldap/users.db"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "错误: 服务名必须是 issue 或 assets"
|
echo "错误: 服务名必须是 issue、assets 或 ldap"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# 站点管理脚本(自动适配本地/云端)
|
||||||
|
#
|
||||||
|
# 本地 Mac 运行 → 通过 SSH 操作 txjp 服务器
|
||||||
|
# 云服务器运行 → 直接操作本地 Docker
|
||||||
|
#
|
||||||
|
# 用法:bash sites-manage-cloud.sh <命令> [站点名...]
|
||||||
|
# 站点名:ldap www cloud token gitea issue assets oa nginx
|
||||||
|
# ai(上述全部)、all(同 ai)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 自动检测运行环境
|
||||||
|
#
|
||||||
|
# 优先级:
|
||||||
|
# 1. OA_REMOTE 环境变量 — "local" 强制本地 / "ssh" 强制远程
|
||||||
|
# 2. hostname 匹配 — 包含 "Tencent" 则判为云服务器本地
|
||||||
|
# 3. 以上都不满足 → SSH 远程模式
|
||||||
|
# ============================================================
|
||||||
|
THIS_HOST=$(hostname 2>/dev/null || uname -n 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ "$OA_REMOTE" = "local" ]; then
|
||||||
|
IS_LOCAL_SERVER=true
|
||||||
|
LABEL="txjp(本地运行 · 手动指定)"
|
||||||
|
elif [ "$OA_REMOTE" = "ssh" ]; then
|
||||||
|
IS_LOCAL_SERVER=false
|
||||||
|
SERVER="txjp"
|
||||||
|
LABEL="txjp(SSH 远程 · 手动指定)"
|
||||||
|
elif echo "$THIS_HOST" | grep -qi "Tencent"; then
|
||||||
|
IS_LOCAL_SERVER=true
|
||||||
|
LABEL="txjp(本地运行 · 自动检测)"
|
||||||
|
else
|
||||||
|
IS_LOCAL_SERVER=false
|
||||||
|
SERVER="txjp"
|
||||||
|
LABEL="txjp(SSH 远程 · 自动检测)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 执行命令:服务器本地直接跑,否则 SSH
|
||||||
|
run() {
|
||||||
|
if $IS_LOCAL_SERVER; then
|
||||||
|
bash -c "$*"
|
||||||
|
else
|
||||||
|
ssh $SERVER "$*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE="/root/docker"
|
||||||
|
|
||||||
|
# 站点 → 目录 & 容器名
|
||||||
|
declare -A SITE_DIR
|
||||||
|
SITE_DIR[ldap]="ldap-ai"
|
||||||
|
SITE_DIR[www]="www-ai"
|
||||||
|
SITE_DIR[cloud]="cloud-ai"
|
||||||
|
SITE_DIR[token]="token-ai"
|
||||||
|
SITE_DIR[gitea]="gitea-ai"
|
||||||
|
SITE_DIR[issue]="issue-ai"
|
||||||
|
SITE_DIR[assets]="assets-ai"
|
||||||
|
SITE_DIR[oa]="oa-ai"
|
||||||
|
SITE_DIR[nginx]="nginx-proxy-ai"
|
||||||
|
SITE_DIR[sso]="sso-ai"
|
||||||
|
|
||||||
|
declare -A SITE_CONTAINER
|
||||||
|
SITE_CONTAINER[ldap]="lldap"
|
||||||
|
SITE_CONTAINER[www]="www-ai"
|
||||||
|
SITE_CONTAINER[cloud]="cloud-ai"
|
||||||
|
SITE_CONTAINER[token]="token-ai"
|
||||||
|
SITE_CONTAINER[gitea]="gitea-ai"
|
||||||
|
SITE_CONTAINER[issue]="issue-ai"
|
||||||
|
SITE_CONTAINER[assets]="assets-ai"
|
||||||
|
SITE_CONTAINER[oa]="oa-ai"
|
||||||
|
SITE_CONTAINER[nginx]="nginx-ai"
|
||||||
|
SITE_CONTAINER[sso]="sso-ai"
|
||||||
|
|
||||||
|
AI_SITES="ldap www cloud token gitea issue assets oa nginx"
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; RED='\033[0;31m'; NC='\033[0m'
|
||||||
|
log() { printf "${GREEN}[✓]${NC} %s\n" "$1"; }
|
||||||
|
warn() { printf "${YELLOW}[!]${NC} %s\n" "$1"; }
|
||||||
|
err() { printf "${RED}[✗]${NC} %s\n" "$1"; }
|
||||||
|
|
||||||
|
expand_sites() {
|
||||||
|
local result=()
|
||||||
|
for s in "$@"; do
|
||||||
|
case $s in
|
||||||
|
ai|all) for x in $AI_SITES; do result+=("$x"); done ;;
|
||||||
|
*) result+=("$s") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
echo "${result[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作
|
||||||
|
# ============================================================
|
||||||
|
remote_start() {
|
||||||
|
local name=$1 dir="${BASE}/${SITE_DIR[$name]}"
|
||||||
|
local container="${SITE_CONTAINER[$name]}"
|
||||||
|
|
||||||
|
if run "docker ps --format '{{.Names}}' | grep -q '^${container}$'" 2>/dev/null; then
|
||||||
|
warn "$name 已在运行"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log "启动 $name ..."
|
||||||
|
run "cd $dir && docker compose up -d" 2>/dev/null && log "$name 已启动" || err "$name 启动失败"
|
||||||
|
}
|
||||||
|
|
||||||
|
remote_stop() {
|
||||||
|
local name=$1 dir="${BASE}/${SITE_DIR[$name]}"
|
||||||
|
local container="${SITE_CONTAINER[$name]}"
|
||||||
|
|
||||||
|
if ! run "docker ps --format '{{.Names}}' | grep -q '^${container}$'" 2>/dev/null; then
|
||||||
|
warn "$name 未在运行"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log "停止 $name ..."
|
||||||
|
run "cd $dir && docker compose down" 2>/dev/null && log "$name 已停止" || err "$name 停止失败"
|
||||||
|
}
|
||||||
|
|
||||||
|
remote_status() {
|
||||||
|
local targets=("$@")
|
||||||
|
echo ""
|
||||||
|
printf "${CYAN}=========================================${NC}\n"
|
||||||
|
printf "${CYAN} 站点状态 — ${LABEL}${NC}\n"
|
||||||
|
printf "${CYAN}=========================================${NC}\n"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for name in "${targets[@]}"; do
|
||||||
|
local container="${SITE_CONTAINER[$name]}"
|
||||||
|
local status
|
||||||
|
status=$(run "docker ps --format '{{.Status}}' -f name=^${container}$" 2>/dev/null || echo "")
|
||||||
|
if [ -n "$status" ]; then
|
||||||
|
printf " ${GREEN}●${NC} %-8s 运行中 (%s)\n" "$name" "$status"
|
||||||
|
else
|
||||||
|
printf " ${RED}●${NC} %-8s 未运行\n" "$name"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
echo ""
|
||||||
|
printf "${CYAN}用法:${NC} bash sites-manage-cloud.sh <命令> [站点名...]\n"
|
||||||
|
echo ""
|
||||||
|
echo " 当前环境: ${LABEL}"
|
||||||
|
echo ""
|
||||||
|
echo " 命令:"
|
||||||
|
echo " start 启动站点"
|
||||||
|
echo " stop 停止站点"
|
||||||
|
echo " restart 重启站点"
|
||||||
|
echo " status 查看状态"
|
||||||
|
echo ""
|
||||||
|
echo " 站点名(可指定多个,空格分隔):"
|
||||||
|
echo " ldap LLDAP 用户目录"
|
||||||
|
echo " www tlyq.ai 官网"
|
||||||
|
echo " cloud 云平台登录页"
|
||||||
|
echo " token Token 工厂登录页"
|
||||||
|
echo " gitea Gitea 代码托管"
|
||||||
|
echo " issue 工单系统"
|
||||||
|
echo " assets 设备资产管理"
|
||||||
|
echo " oa OA 统一门户"
|
||||||
|
echo " nginx 反向代理"
|
||||||
|
echo " ai 全部 tlyq.ai 站点"
|
||||||
|
echo ""
|
||||||
|
echo "示例:"
|
||||||
|
echo " bash sites-manage-cloud.sh start ai # 启动全部"
|
||||||
|
echo " bash sites-manage-cloud.sh restart nginx # 重启 nginx"
|
||||||
|
echo " bash sites-manage-cloud.sh stop oa issue # 停 OA + 工单"
|
||||||
|
echo " bash sites-manage-cloud.sh status # 查看所有状态"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 入口
|
||||||
|
# ============================================================
|
||||||
|
ACTION=${1:-}
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
if [ -z "$ACTION" ]; then
|
||||||
|
show_help
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mapfile -t TARGETS < <(expand_sites "$@")
|
||||||
|
if [ ${#TARGETS[@]} -eq 0 ]; then
|
||||||
|
mapfile -t TARGETS < <(expand_sites ai)
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $ACTION in
|
||||||
|
start)
|
||||||
|
for s in "${TARGETS[@]}"; do remote_start "$s"; done
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
for s in "${TARGETS[@]}"; do remote_stop "$s"; done
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
for s in "${TARGETS[@]}"; do remote_stop "$s"; done
|
||||||
|
echo ""
|
||||||
|
log "等待 2 秒后启动..."
|
||||||
|
sleep 2
|
||||||
|
for s in "${TARGETS[@]}"; do remote_start "$s"; done
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
remote_status "${TARGETS[@]}"
|
||||||
|
;;
|
||||||
|
help|--help|-h)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "未知命令: $ACTION"
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# 本地站点管理:启动 / 停止 / 重启 / 状态
|
# 本地站点管理:启动 / 停止 / 重启 / 状态
|
||||||
# 用法:bash local.sh [start|stop|restart|status] [站点名...]
|
# 用法:bash sites-manage-local.sh [start|stop|restart|status] [站点名...]
|
||||||
# 不加参数则交互选择
|
# 不加参数则交互选择
|
||||||
#
|
#
|
||||||
# 站点名:
|
# 站点名:
|
||||||
|
|
@ -237,7 +237,7 @@ show_status() {
|
||||||
# ============================================================
|
# ============================================================
|
||||||
show_help() {
|
show_help() {
|
||||||
echo ""
|
echo ""
|
||||||
printf "${CYAN}用法:${NC} bash local.sh <命令> [站点名...]\n"
|
printf "${CYAN}用法:${NC} bash sites-manage-local.sh <命令> [站点名...]\n"
|
||||||
echo ""
|
echo ""
|
||||||
echo " 命令:"
|
echo " 命令:"
|
||||||
echo " start 启动站点"
|
echo " start 启动站点"
|
||||||
|
|
@ -253,11 +253,11 @@ show_help() {
|
||||||
echo " all — 全部站点"
|
echo " all — 全部站点"
|
||||||
echo ""
|
echo ""
|
||||||
echo "示例:"
|
echo "示例:"
|
||||||
echo " bash local.sh start all # 启动全部"
|
echo " bash sites-manage-local.sh start all # 启动全部"
|
||||||
echo " bash local.sh start ai # 启动 ai 全部"
|
echo " bash sites-manage-local.sh start ai # 启动 ai 全部"
|
||||||
echo " bash local.sh start issue assets # 只启 issue + assets"
|
echo " bash sites-manage-local.sh start issue assets # 只启 issue + assets"
|
||||||
echo " bash local.sh stop www-ai # 只停 www-ai"
|
echo " bash sites-manage-local.sh stop www-ai # 只停 www-ai"
|
||||||
echo " bash local.sh status # 查看所有状态"
|
echo " bash sites-manage-local.sh status # 查看所有状态"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue