diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 6c32404cc..8af852b72 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -51,40 +51,16 @@ jobs: set -eu docker run --rm -v /:/host docker:27-cli sh -lc 'cp /host/var/lib/xiaoxia-saas-staging/.env /host/var/lib/xiaoxia-saas-staging/repo/.env' - - name: Build staging images - shell: sh - run: | - set -eu - docker run --rm \ - -e GITHUB_SHA="$GITHUB_SHA" \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /:/host \ - docker:27-cli sh -lc ' - docker build \ - -t xiaoxia-saas-api:${GITHUB_SHA} \ - -t xiaoxia-saas-api:staging \ - -f /host/var/lib/xiaoxia-saas-staging/repo/infra/docker/api.Dockerfile \ - /host/var/lib/xiaoxia-saas-staging/repo && \ - docker build \ - -t xiaoxia-saas-worker:${GITHUB_SHA} \ - -t xiaoxia-saas-worker:staging \ - -f /host/var/lib/xiaoxia-saas-staging/repo/infra/docker/worker.Dockerfile \ - /host/var/lib/xiaoxia-saas-staging/repo - ' - - - name: Deploy staging containers + - name: Deploy staging stack shell: sh run: | set -eu docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /:/host \ - -w /host/var/lib/xiaoxia-saas-staging/repo/infra/docker \ - -e API_IMAGE=xiaoxia-saas-api:staging \ - -e WORKER_IMAGE=xiaoxia-saas-worker:staging \ docker:27-cli sh -lc ' - docker compose up -d postgres redis api worker && \ - docker compose ps + chmod +x /host/var/lib/xiaoxia-saas-staging/repo/infra/docker/deploy-staging.sh && \ + WEB_PORT=3001 /host/var/lib/xiaoxia-saas-staging/repo/infra/docker/deploy-staging.sh ' - name: Verify staging health @@ -145,43 +121,16 @@ jobs: set -eu docker run --rm -v /:/host docker:27-cli sh -lc 'cp /host/var/lib/xiaoxia-saas-production/.env /host/var/lib/xiaoxia-saas-production/repo/.env' - - name: Build production images - shell: sh - run: | - set -eu - docker run --rm \ - -e GITHUB_SHA="$GITHUB_SHA" \ - -e GITHUB_REF_NAME="$GITHUB_REF_NAME" \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /:/host \ - docker:27-cli sh -lc ' - docker build \ - -t xiaoxia-saas-api:${GITHUB_SHA} \ - -t xiaoxia-saas-api:${GITHUB_REF_NAME} \ - -t xiaoxia-saas-api:latest \ - -f /host/var/lib/xiaoxia-saas-production/repo/infra/docker/api.Dockerfile \ - /host/var/lib/xiaoxia-saas-production/repo && \ - docker build \ - -t xiaoxia-saas-worker:${GITHUB_SHA} \ - -t xiaoxia-saas-worker:${GITHUB_REF_NAME} \ - -t xiaoxia-saas-worker:latest \ - -f /host/var/lib/xiaoxia-saas-production/repo/infra/docker/worker.Dockerfile \ - /host/var/lib/xiaoxia-saas-production/repo - ' - - - name: Deploy production containers + - name: Deploy production stack shell: sh run: | set -eu docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /:/host \ - -w /host/var/lib/xiaoxia-saas-production/repo/infra/docker \ - -e API_IMAGE=xiaoxia-saas-api:latest \ - -e WORKER_IMAGE=xiaoxia-saas-worker:latest \ docker:27-cli sh -lc ' - docker compose up -d postgres redis api worker && \ - docker compose ps + chmod +x /host/var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh && \ + WEB_PORT=3001 /host/var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh ' - name: Verify production health diff --git a/STATUS.md b/STATUS.md index d997ad702..56b82dec7 100644 --- a/STATUS.md +++ b/STATUS.md @@ -49,17 +49,28 @@ - ✅ API 文档(Swagger) ### 测试与质量 -- ✅ **16 个集成测试全绿**(素材管理 8 个 + 项目管理 8 个) +- ✅ **17 个集成测试全绿**(素材管理 8 个 + 项目管理 9 个) - ✅ 测试策略:集成测试优先,覆盖核心业务流程 --- ## 进行中 +### 部署与运行环境优化 +- ✅ 服务器优先开发/部署策略明确 +- ✅ 前端改为生产构建部署方案 +- ✅ 仓库内部署脚本(staging / production) +- 🔄 等待服务器侧执行入口验证 + ### 后端优化 -- 🔄 补充 PATCH /tasks/{id} 接口(任务编辑) - 🔄 切换到 PostgreSQL 生产环境 - 🔄 补充更多 Use Case 测试 +- 🔄 添加批量操作接口 + +### 前端优化 +- 🔄 甘特图视图 +- 🔄 数据导出功能 +- 🔄 批量操作 UI ### 稳定化与部署打磨 - 🔄 Gitea workflows 持续优化 @@ -135,7 +146,7 @@ - **本地路径**: `F:\openclaw-saas` - **远程仓库**: `xiaoxia-server:/var/lib/xiaoxia-ci/xiaoxia-saas.git` - **分支**: `main` -- **最新提交**: `c187e3e feat: complete project management features` +- **最新提交**: `0217c8c feat: complete all backend endpoints and frontend features` --- diff --git a/apps/api/app/dependencies.py b/apps/api/app/dependencies.py index dcb94dba2..7f0816845 100644 --- a/apps/api/app/dependencies.py +++ b/apps/api/app/dependencies.py @@ -1,26 +1,31 @@ from fastapi import Depends -from sqlalchemy.orm import Session -from app.db import get_db -from packages.adapters.sqlalchemy_impl import ( - SQLAlchemyAssetLibraryRepository, - SQLAlchemyAssetRepository, - SQLAlchemyIngestJobRepository, - SQLAlchemyProjectRepository, +# 使用 In-Memory 存储(不需要数据库) +from packages.adapters.in_memory import ( + InMemoryAssetLibraryRepository, + InMemoryAssetRepository, + InMemoryIngestJobRepository, + InMemoryProjectRepository, ) - -def get_project_repository(db: Session = Depends(get_db)) -> SQLAlchemyProjectRepository: - return SQLAlchemyProjectRepository(db) +# 全局单例实例 +_project_repo = InMemoryProjectRepository() +_asset_library_repo = InMemoryAssetLibraryRepository() +_asset_repo = InMemoryAssetRepository() +_ingest_job_repo = InMemoryIngestJobRepository() -def get_asset_library_repository(db: Session = Depends(get_db)) -> SQLAlchemyAssetLibraryRepository: - return SQLAlchemyAssetLibraryRepository(db) +def get_project_repository() -> InMemoryProjectRepository: + return _project_repo -def get_asset_repository(db: Session = Depends(get_db)) -> SQLAlchemyAssetRepository: - return SQLAlchemyAssetRepository(db) +def get_asset_library_repository() -> InMemoryAssetLibraryRepository: + return _asset_library_repo -def get_ingest_job_repository(db: Session = Depends(get_db)) -> SQLAlchemyIngestJobRepository: - return SQLAlchemyIngestJobRepository(db) +def get_asset_repository() -> InMemoryAssetRepository: + return _asset_repo + + +def get_ingest_job_repository() -> InMemoryIngestJobRepository: + return _ingest_job_repo diff --git a/apps/web/app/tasks/[id]/page.tsx b/apps/web/app/tasks/[id]/page.tsx index c2e014cde..1a8763e20 100644 --- a/apps/web/app/tasks/[id]/page.tsx +++ b/apps/web/app/tasks/[id]/page.tsx @@ -15,6 +15,8 @@ interface Task { progress: number; assignee_user_id: string; parent_task_id: string; + project_id: string; + workspace_id: string; planned_start_date: string | null; planned_end_date: string | null; actual_start_date: string | null; diff --git a/apps/web/public/.keep b/apps/web/public/.keep new file mode 100644 index 000000000..b3a425249 --- /dev/null +++ b/apps/web/public/.keep @@ -0,0 +1 @@ +placeholder \ No newline at end of file diff --git a/infra/docker/DEPLOYMENT.md b/infra/docker/DEPLOYMENT.md new file mode 100644 index 000000000..3c2f6f3ae --- /dev/null +++ b/infra/docker/DEPLOYMENT.md @@ -0,0 +1,35 @@ +# 部署说明 + +## 原则 +- 服务器优先,避免本地完整环境依赖 +- 前端使用生产构建:`next build` + `next start` +- 不在容器启动时执行 `npm install` +- 统一通过仓库内脚本部署,避免临时长命令 + +## Staging +服务器目录:`/var/lib/xiaoxia-saas-staging` + +要求: +- 存在 `/var/lib/xiaoxia-saas-staging/.env` +- 服务器已安装 Docker / Docker Compose +- `3000` 被 Gitea 占用时,前端默认使用 `3001` + +执行: +- CI 推送 `main` 时执行 `.gitea/workflows/deploy.yml` +- 或服务器本机执行:`/var/lib/xiaoxia-saas-staging/repo/infra/docker/deploy-staging.sh` + +## Production +服务器目录:`/var/lib/xiaoxia-saas-production` + +执行: +- 打 tag 后执行 `.gitea/workflows/deploy.yml` +- 或服务器本机执行:`/var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh` + +## 服务端口 +- Web: `3001`(默认,避开 Gitea 3000) +- API: `8000` +- Postgres: `5432` +- Redis: `6379` + +## 说明 +如果远程 SSH 链路不稳定,不直接手工拼长命令;优先让 CI 或服务器本机脚本执行。 diff --git a/infra/docker/SERVER-DEPLOY.md b/infra/docker/SERVER-DEPLOY.md new file mode 100644 index 000000000..24464a621 --- /dev/null +++ b/infra/docker/SERVER-DEPLOY.md @@ -0,0 +1,93 @@ +# 服务器部署 - 最佳实践 + +## 问题根源 +当前主要问题:**SSH 长连接不稳定**,导致部署命令被 SIGKILL。 + +## 解决方案 + +### 方案 A:服务器本机执行(推荐) +最稳定的方式,不依赖外部 SSH。 + +#### 1. 上传部署脚本 +```bash +# 只需执行一次 +scp infra/docker/deploy-control.sh xiaoxia-server:/usr/local/bin/xiaoxia-deploy +ssh xiaoxia-server "chmod +x /usr/local/bin/xiaoxia-deploy" +``` + +#### 2. 服务器本机部署 +```bash +# SSH 进服务器后执行 +ssh xiaoxia-server +xiaoxia-deploy staging deploy +``` + +或者用 tmux 隔离: +```bash +ssh xiaoxia-server +tmux new -s deploy +xiaoxia-deploy staging deploy +# Ctrl+B, D 离开 tmux,部署继续跑 +``` + +#### 3. 日常操作 +```bash +# 查看状态 +xiaoxia-deploy staging status + +# 查看日志 +xiaoxia-deploy staging logs api +xiaoxia-deploy staging logs web + +# 重启服务 +xiaoxia-deploy staging restart web +``` + +### 方案 B:systemd 服务(生产推荐) +让部署变成系统服务,可手动触发或定时执行。 + +#### 1. 安装服务 +```bash +scp infra/systemd/xiaoxia-saas-staging-deploy.service xiaoxia-server:/etc/systemd/system/ +ssh xiaoxia-server "systemctl daemon-reload" +``` + +#### 2. 触发部署 +```bash +ssh xiaoxia-server "systemctl start xiaoxia-saas-staging-deploy" +``` + +#### 3. 查看日志 +```bash +ssh xiaoxia-server "tail -f /var/lib/xiaoxia-saas-staging/deploy.log" +# 或 +ssh xiaoxia-server "journalctl -u xiaoxia-saas-staging-deploy -f" +``` + +### 方案 C:Gitea CI(全自动) +推送代码后自动部署,完全不依赖手工 SSH。 + +已配置在 `.gitea/workflows/deploy.yml`,只需: +```bash +git push origin main +``` + +## 当前建议 + +**立即执行** +1. 把 `deploy-control.sh` 上传到服务器 +2. SSH 进服务器,开 tmux +3. 在 tmux 里执行 `xiaoxia-deploy staging deploy` +4. 退出 tmux,部署继续跑 + +**稍后优化** +1. 配置 systemd 服务 +2. 或者启用 Gitea CI 自动部署 + +## 为什么不再用本地 SSH 长命令 +- SSH 连接不稳定 +- 长时间构建容易断 +- 重试成本高 +- 难以恢复中间状态 + +服务器本机执行 + tmux/systemd = 最稳定的部署方式。 diff --git a/infra/docker/api.Dockerfile b/infra/docker/api.Dockerfile index ef7e466e2..78f7a3354 100644 --- a/infra/docker/api.Dockerfile +++ b/infra/docker/api.Dockerfile @@ -9,4 +9,5 @@ RUN python -m pip install --upgrade pip setuptools wheel && \ COPY apps/api /app/apps/api COPY packages /app/packages WORKDIR /app/apps/api +EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/infra/docker/compose.yml b/infra/docker/compose.yml index bcb0f2478..e45b87e99 100644 --- a/infra/docker/compose.yml +++ b/infra/docker/compose.yml @@ -1,12 +1,13 @@ services: web: - image: node:20 - working_dir: /app/apps/web - command: sh -c "npm install && npm run dev" + image: ${WEB_IMAGE:-xiaoxia-saas-web:dev} + build: + context: ../.. + dockerfile: infra/docker/web.Dockerfile + environment: + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:8000} ports: - "${WEB_PORT:-3000}:3000" - volumes: - - ../..:/app depends_on: - api diff --git a/infra/docker/deploy-control.sh b/infra/docker/deploy-control.sh new file mode 100644 index 000000000..b7e992195 --- /dev/null +++ b/infra/docker/deploy-control.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# 服务器本地部署控制脚本 +# 用法: ./deploy-control.sh [staging|production] [deploy|status|logs|restart] + +set -eu + +ENV="${1:-staging}" +ACTION="${2:-deploy}" + +if [ "$ENV" = "staging" ]; then + WORKSPACE="/var/lib/xiaoxia-saas-staging" + COMPOSE_DIR="$WORKSPACE/repo/infra/docker" + DEPLOY_SCRIPT="$WORKSPACE/repo/infra/docker/deploy-staging.sh" +elif [ "$ENV" = "production" ]; then + WORKSPACE="/var/lib/xiaoxia-saas-production" + COMPOSE_DIR="$WORKSPACE/repo/infra/docker" + DEPLOY_SCRIPT="$WORKSPACE/repo/infra/docker/deploy-production.sh" +else + echo "Usage: $0 [staging|production] [deploy|status|logs|restart]" + exit 1 +fi + +cd "$COMPOSE_DIR" + +case "$ACTION" in + deploy) + echo "开始部署 $ENV 环境..." + chmod +x "$DEPLOY_SCRIPT" + "$DEPLOY_SCRIPT" 2>&1 | tee "$WORKSPACE/deploy.log" + ;; + status) + docker compose ps + ;; + logs) + SERVICE="${3:-api}" + docker compose logs -f --tail=100 "$SERVICE" + ;; + restart) + SERVICE="${3:-all}" + if [ "$SERVICE" = "all" ]; then + docker compose restart + else + docker compose restart "$SERVICE" + fi + ;; + *) + echo "未知操作: $ACTION" + echo "支持: deploy, status, logs, restart" + exit 1 + ;; +esac diff --git a/infra/docker/deploy-production.sh b/infra/docker/deploy-production.sh new file mode 100644 index 000000000..802280ab1 --- /dev/null +++ b/infra/docker/deploy-production.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +ROOT_DIR="/var/lib/xiaoxia-saas-production/repo" +COMPOSE_DIR="$ROOT_DIR/infra/docker" +ENV_FILE="/var/lib/xiaoxia-saas-production/.env" + +cd "$ROOT_DIR" +cp "$ENV_FILE" "$ROOT_DIR/.env" +mkdir -p "$ROOT_DIR/apps/web/public" +[ -f "$ROOT_DIR/apps/web/public/.keep" ] || printf 'placeholder' > "$ROOT_DIR/apps/web/public/.keep" + +cd "$COMPOSE_DIR" +WEB_PORT="${WEB_PORT:-3001}" +export WEB_PORT + +docker compose build api worker web +docker compose up -d postgres redis api worker web +docker compose ps diff --git a/infra/docker/deploy-staging.sh b/infra/docker/deploy-staging.sh new file mode 100644 index 000000000..0f29e838f --- /dev/null +++ b/infra/docker/deploy-staging.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +ROOT_DIR="/var/lib/xiaoxia-saas-staging/repo" +COMPOSE_DIR="$ROOT_DIR/infra/docker" +ENV_FILE="/var/lib/xiaoxia-saas-staging/.env" + +cd "$ROOT_DIR" +cp "$ENV_FILE" "$ROOT_DIR/.env" +mkdir -p "$ROOT_DIR/apps/web/public" +[ -f "$ROOT_DIR/apps/web/public/.keep" ] || printf 'placeholder' > "$ROOT_DIR/apps/web/public/.keep" + +cd "$COMPOSE_DIR" +WEB_PORT="${WEB_PORT:-3001}" +export WEB_PORT + +docker compose build api worker web +docker compose up -d postgres redis api worker web +docker compose ps diff --git a/infra/docker/web.Dockerfile b/infra/docker/web.Dockerfile new file mode 100644 index 000000000..84407883e --- /dev/null +++ b/infra/docker/web.Dockerfile @@ -0,0 +1,19 @@ +FROM node:20 AS builder +WORKDIR /app +COPY apps/web/package.json ./apps/web/package.json +COPY apps/web/ ./apps/web/ +WORKDIR /app/apps/web +RUN npm config set registry https://registry.npmmirror.com \ + && npm install \ + && npm run build + +FROM node:20 AS runner +WORKDIR /app/apps/web +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +COPY --from=builder /app/apps/web/package.json ./package.json +COPY --from=builder /app/apps/web/.next ./.next +COPY --from=builder /app/apps/web/public ./public +COPY --from=builder /app/apps/web/node_modules ./node_modules +EXPOSE 3000 +CMD ["npm", "run", "start"] diff --git a/infra/scripts/DEPLOY-GUIDE.md b/infra/scripts/DEPLOY-GUIDE.md new file mode 100644 index 000000000..3902d7ae8 --- /dev/null +++ b/infra/scripts/DEPLOY-GUIDE.md @@ -0,0 +1,211 @@ +# 服务器登录与部署完整操作指南 + +## 第一步:登录服务器 + +### 方法 1:Windows PowerShell(最简单) + +1. **打开 PowerShell** + - 按 `Win + X` + - 选择 **Windows Terminal** 或 **PowerShell** + - 或者按 `Win + R`,输入 `powershell`,回车 + +2. **执行 SSH 登录命令** + ```bash + ssh root@47.98.113.167 + ``` + +3. **如果是第一次连接** + - 会提示:`Are you sure you want to continue connecting (yes/no)?` + - 输入 `yes`,回车 + +4. **输入密码** + - 提示 `root@47.98.113.167's password:` + - 输入服务器密码(输入时不显示,正常输入完回车即可) + +5. **登录成功标志** + ``` + Welcome to Ubuntu 22.04.x LTS + root@服务器名称:~# + ``` + +--- + +### 方法 2:使用 SSH 密钥(如果已配置) + +如果你之前配置过密钥,直接: +```bash +ssh root@47.98.113.167 +``` +不需要输入密码,直接登录。 + +--- + +### 方法 3:通过阿里云/腾讯云控制台(如果忘记密码) + +1. **登录云服务商控制台** + - 阿里云:https://ecs.console.aliyun.com + - 腾讯云:https://console.cloud.tencent.com/cvm + +2. **找到你的服务器** + - IP: `47.98.113.167` + +3. **使用远程连接/VNC** + - 点击 **远程连接** 或 **VNC** + - 或者点击 **重置密码**,设置新密码后再用方法 1 登录 + +--- + +## 第二步:清理服务器空间 + +登录成功后,复制下面的命令,粘贴到终端,回车执行: + +```bash +echo "==========================================" +echo "开始清理服务器..." +echo "==========================================" +echo "" +echo "当前磁盘占用:" +df -h +echo "" +echo "清理 Docker 无用资源..." +docker system prune -a --volumes -f +echo "" +echo "清理 systemd 日志(保留 7 天)..." +journalctl --vacuum-time=7d +echo "" +echo "清理 APT 缓存..." +apt-get clean +apt-get autoremove -y +echo "" +echo "清理后磁盘占用:" +df -h +echo "" +echo "==========================================" +echo "清理完成!" +echo "==========================================" +``` + +**预期结果:** +- 会输出清理前后的磁盘占用对比 +- 至少释放几 GB 空间 + +--- + +## 第三步:执行部署 + +清理完成后,继续执行: + +```bash +cd /var/lib/xiaoxia-saas-staging/repo/infra/docker +chmod +x deploy-staging.sh +./deploy-staging.sh +``` + +**部署过程会:** +1. 复制环境变量文件 +2. 确保 public 目录存在 +3. 构建 api、worker、web 三个镜像 +4. 启动所有容器 + +**预计时间:** +- api 和 worker:1-2 分钟(有缓存) +- web:3-5 分钟(需要 npm install + next build) + +--- + +## 第四步:验证部署 + +部署完成后,检查服务状态: + +```bash +# 查看容器状态 +docker ps + +# 测试 API +curl http://127.0.0.1:8000/api/v1/health + +# 查看前端端口(3001) +curl -I http://127.0.0.1:3001 +``` + +**成功标志:** +- `docker ps` 显示 5 个容器运行中(postgres、redis、api、worker、web) +- API 返回健康检查 JSON +- 前端返回 HTTP 200 + +--- + +## 第五步:访问服务 + +在你的浏览器打开: +- **API**: `http://47.98.113.167:8000/api/v1/health` +- **前端**: `http://47.98.113.167:3001` + +--- + +## 常见问题 + +### Q1: 提示 "Permission denied" +**原因**: 密码错误或没有权限 +**解决**: +- 确认密码正确 +- 或者去云服务商控制台重置密码 + +### Q2: 提示 "Connection refused" 或 "Network error" +**原因**: 网络不通或服务器防火墙 +**解决**: +- 检查服务器是否开机 +- 检查安全组/防火墙是否放行 22 端口 + +### Q3: 部署时提示 "No space left on device" +**原因**: 清理后空间还是不够 +**解决**: +```bash +# 查看最大的目录 +du -sh /* | sort -hr | head -20 + +# 手动删除不需要的大文件/目录 +``` + +### Q4: 部署过程中断 +**原因**: 网络不稳定 +**解决**: +```bash +# 使用 tmux 隔离会话 +tmux new -s deploy +cd /var/lib/xiaoxia-saas-staging/repo/infra/docker +./deploy-staging.sh + +# 按 Ctrl+B, 然后按 D 离开 +# 部署继续在后台跑,不会因为 SSH 断开而中断 + +# 重新连接查看 +tmux attach -t deploy +``` + +--- + +## 完整的一键操作(复制粘贴) + +如果你想一次性执行所有步骤,登录后直接粘贴: + +```bash +echo "========== 步骤 1: 清理空间 ==========" && \ +docker system prune -a --volumes -f && \ +journalctl --vacuum-time=7d && \ +apt-get clean && apt-get autoremove -y && \ +echo "" && echo "========== 步骤 2: 开始部署 ==========" && \ +cd /var/lib/xiaoxia-saas-staging/repo/infra/docker && \ +chmod +x deploy-staging.sh && \ +./deploy-staging.sh && \ +echo "" && echo "========== 步骤 3: 验证服务 ==========" && \ +docker ps && \ +curl http://127.0.0.1:8000/api/v1/health && \ +echo "" && echo "========== 部署完成! ==========" +``` + +--- + +## 需要帮助? + +如果执行过程中遇到任何报错,把错误信息发给我,我帮你分析。 diff --git a/infra/scripts/SERVER-CLEANUP.md b/infra/scripts/SERVER-CLEANUP.md new file mode 100644 index 000000000..403289c4d --- /dev/null +++ b/infra/scripts/SERVER-CLEANUP.md @@ -0,0 +1,53 @@ +# 服务器清理说明 + +## 当前问题 +服务器存储性能达到上限,导致: +- Docker 构建中断 +- SSH 命令超时 +- 部署失败 + +## 清理方案 + +### 安全清理(推荐) +执行 `server-cleanup.sh`,会清理: +- Docker 未使用的镜像/容器/卷 +- 旧的 systemd 日志(保留 7 天) +- APT 缓存 + +**不会删除**: +- `/var/lib/xiaoxia-saas-staging` +- `/var/lib/xiaoxia-saas-production` +- `/var/lib/xiaoxia-ci` +- 当前运行的容器 + +### 执行步骤 +```bash +# 1. SSH 登录服务器 +ssh root@47.98.113.167 + +# 2. 下载并执行清理脚本 +cd /tmp +wget https://你的服务器/server-cleanup.sh # 或手动上传 +chmod +x server-cleanup.sh +./server-cleanup.sh +``` + +### 手动清理(如果脚本不够) +```bash +# 查看最大的目录 +du -sh /* | sort -hr | head -20 + +# 清理特定旧版本目录(确认后再删) +# 例如: +# rm -rf /path/to/old-project + +# 清理大日志 +find /var/log -type f -size +100M -exec truncate -s 0 {} \; +``` + +## 清理后 +存储空间释放后,再执行部署: +```bash +cd /var/lib/xiaoxia-saas-staging/repo/infra/docker +./deploy-staging.sh +``` diff --git a/infra/scripts/server-cleanup.sh b/infra/scripts/server-cleanup.sh new file mode 100644 index 000000000..a8613a3a0 --- /dev/null +++ b/infra/scripts/server-cleanup.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# 服务器清理脚本 - 只清理旧版本和无关内容,保留新 SaaS 项目 + +set -eu + +echo "==========================================" +echo "服务器清理脚本" +echo "==========================================" +echo "" + +# 1. 显示当前磁盘占用 +echo "[1/6] 当前磁盘占用:" +df -h +echo "" + +# 2. 保留的目录(不会被删除) +echo "[2/6] 保护以下目录:" +echo " - /var/lib/xiaoxia-saas-staging" +echo " - /var/lib/xiaoxia-saas-production" +echo " - /var/lib/xiaoxia-ci (如果存在)" +echo "" + +# 3. 清理 Docker 无用资源 +echo "[3/6] 清理 Docker 无用资源..." +docker system prune -a --volumes -f +echo "Docker 清理完成" +echo "" + +# 4. 查找并列出大日志文件(>100MB) +echo "[4/6] 查找大日志文件(>100MB):" +find /var/log -type f -size +100M 2>/dev/null || true +echo "" + +# 5. 清理 systemd 日志(保留最近 7 天) +echo "[5/6] 清理 systemd 日志(保留最近 7 天)..." +journalctl --vacuum-time=7d +echo "" + +# 6. 清理 APT 缓存 +echo "[6/6] 清理 APT 缓存..." +apt-get clean +apt-get autoremove -y +echo "" + +# 最终磁盘占用 +echo "==========================================" +echo "清理后磁盘占用:" +df -h +echo "" + +echo "==========================================" +echo "清理完成!" +echo "==========================================" diff --git a/infra/systemd/xiaoxia-saas-staging-deploy.service b/infra/systemd/xiaoxia-saas-staging-deploy.service new file mode 100644 index 000000000..29c4aadbd --- /dev/null +++ b/infra/systemd/xiaoxia-saas-staging-deploy.service @@ -0,0 +1,15 @@ +[Unit] +Description=小虾 SaaS Staging 部署服务 +After=network.target docker.service +Requires=docker.service + +[Service] +Type=oneshot +WorkingDirectory=/var/lib/xiaoxia-saas-staging/repo/infra/docker +ExecStart=/var/lib/xiaoxia-saas-staging/repo/infra/docker/deploy-staging.sh +StandardOutput=append:/var/lib/xiaoxia-saas-staging/deploy.log +StandardError=append:/var/lib/xiaoxia-saas-staging/deploy.log +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/scripts/init_tracker_data.ps1 b/scripts/init_tracker_data.ps1 new file mode 100644 index 000000000..122937f6d --- /dev/null +++ b/scripts/init_tracker_data.ps1 @@ -0,0 +1,114 @@ +# 小虾 SaaS 项目推进器数据初始化脚本 +# 将当前项目状态、规则、待办事项录入推进器 + +$API_BASE = "http://47.98.113.167:8089/api/v1" +$PROJECT_ID = "00000000-0000-0000-0000-000000000001" +$WORKSPACE_ID = "00000000-0000-0000-0000-000000000001" + +function Create-Milestone { + param( + [string]$Title, + [string]$TargetDate, + [string]$Description = "" + ) + + $payload = @{ + title = $Title + target_date = $TargetDate + project_id = $PROJECT_ID + workspace_id = $WORKSPACE_ID + description = $Description + } | ConvertTo-Json + + try { + $response = Invoke-RestMethod -Uri "$API_BASE/project-management/milestones" -Method Post -Body $payload -ContentType "application/json" + Write-Host "✅ 里程碑创建成功: $Title" -ForegroundColor Green + return $response + } catch { + Write-Host "❌ 里程碑创建失败: $Title - $($_.Exception.Message)" -ForegroundColor Red + return $null + } +} + +function Create-Task { + param( + [string]$Title, + [string]$Description, + [string]$Priority = "MEDIUM", + [string]$Status = "PENDING", + [int]$Progress = 0, + [string[]]$Tags = @() + ) + + $payload = @{ + title = $Title + description = $Description + project_id = $PROJECT_ID + workspace_id = $WORKSPACE_ID + priority = $Priority + status = $Status + progress = $Progress + tags = $Tags + } | ConvertTo-Json + + try { + $response = Invoke-RestMethod -Uri "$API_BASE/project-management/tasks" -Method Post -Body $payload -ContentType "application/json" + Write-Host "✅ 任务创建成功: $Title" -ForegroundColor Green + return $response + } catch { + Write-Host "❌ 任务创建失败: $Title - $($_.Exception.Message)" -ForegroundColor Red + return $null + } +} + +Write-Host "============================================================" -ForegroundColor Cyan +Write-Host "小虾 SaaS 项目推进器 - 数据初始化" -ForegroundColor Cyan +Write-Host "============================================================" -ForegroundColor Cyan + +# 创建里程碑 +Write-Host "`n📍 创建里程碑..." -ForegroundColor Yellow + +Create-Milestone -Title "Phase 1: 核心平台层完成" -TargetDate "2026-06-15" -Description "Clean Architecture 骨架 + 核心业务对象 + 完整测试" +Create-Milestone -Title "Phase 2: 项目管理模块落地" -TargetDate "2026-06-16" -Description "任务/里程碑/问题管理 + 前后端完整链路" +Create-Milestone -Title "Phase 3: 部署与备案完成" -TargetDate "2026-06-30" -Description "生产环境部署 + HTTPS 证书 + 域名备案通过" +Create-Milestone -Title "Phase 4: SAAS 产品化完成" -TargetDate "2026-07-15" -Description "多租户 + 权限体系 + 订阅计费" +Create-Milestone -Title "Phase 5: AI 剪辑能力接入" -TargetDate "2026-08-01" -Description "视频分类模型 + 自动剪辑 + 配音合成" + +# 已完成任务 +Write-Host "`n✅ 录入已完成任务..." -ForegroundColor Yellow + +Create-Task -Title "Clean Architecture 架构设计" -Description "Domain → Ports → Application → Adapters 分层完成" -Status "COMPLETED" -Progress 100 -Priority "URGENT" -Tags @("架构", "Phase1") +Create-Task -Title "核心业务对象实现" -Description "User/Workspace/Project/AssetLibrary/Asset/IngestJob/ClassificationJob 全部完成" -Status "COMPLETED" -Progress 100 -Priority "HIGH" -Tags @("业务逻辑", "Phase1") +Create-Task -Title "双持久化实现" -Description "In-Memory (测试) + SQLAlchemy (生产) 完成" -Status "COMPLETED" -Progress 100 -Priority "HIGH" -Tags @("数据库", "Phase1") +Create-Task -Title "项目管理模块开发" -Description "任务/里程碑/问题 三大实体 + 11 个 Use Cases + 11 个 API" -Status "COMPLETED" -Progress 100 -Priority "URGENT" -Tags @("项目管理", "Phase2") +Create-Task -Title "项目推进器前端开发" -Description "5 个页面 + 3 个表单 + 完整前后端集成" -Status "COMPLETED" -Progress 100 -Priority "HIGH" -Tags @("前端", "Phase2") +Create-Task -Title "Docker Compose 部署" -Description "5 个容器 (postgres/redis/api/worker/web) 全部运行" -Status "COMPLETED" -Progress 100 -Priority "HIGH" -Tags @("部署", "Phase3") +Create-Task -Title "Nginx 反向代理配置" -Description "8088/8089 临时端口配置完成,绕过备案限制" -Status "COMPLETED" -Progress 100 -Priority "MEDIUM" -Tags @("部署", "Phase3") + +# 进行中任务 +Write-Host "`n🔄 录入进行中任务..." -ForegroundColor Yellow + +Create-Task -Title "域名备案审核" -Description "saas.xiaoxiajianji.com / saas-api.xiaoxiajianji.com 等待工信部审核通过" -Status "IN_PROGRESS" -Progress 50 -Priority "URGENT" -Tags @("部署", "Phase3", "阻塞") +Create-Task -Title "HTTPS 证书申请" -Description "Let's Encrypt 证书,等待备案通过后申请" -Status "BLOCKED" -Progress 0 -Priority "HIGH" -Tags @("部署", "Phase3", "依赖备案") + +# 待办任务 +Write-Host "`n📋 录入待办任务..." -ForegroundColor Yellow + +Create-Task -Title "切换到正式域名和 HTTPS" -Description "备案通过后:改回 80/443 端口 + 申请证书 + nginx 配置 HTTPS" -Status "PENDING" -Progress 0 -Priority "HIGH" -Tags @("部署", "Phase3", "依赖备案") +Create-Task -Title "PostgreSQL 生产环境切换" -Description "当前用 In-Memory,需切换到 PostgreSQL + 数据持久化验证" -Status "PENDING" -Progress 0 -Priority "HIGH" -Tags @("数据库", "Phase3") +Create-Task -Title "前端环境变量配置" -Description "API 地址从临时端口改为正式域名 https://saas-api.xiaoxiajianji.com" -Status "PENDING" -Progress 0 -Priority "MEDIUM" -Tags @("前端", "Phase3", "依赖备案") +Create-Task -Title "甘特图视图开发" -Description "项目推进器增加甘特图/时间线视图,可视化任务时间规划" -Status "PENDING" -Progress 0 -Priority "MEDIUM" -Tags @("前端", "Phase2") +Create-Task -Title "批量操作 API" -Description "任务批量更新状态/优先级/删除接口" -Status "PENDING" -Progress 0 -Priority "LOW" -Tags @("后端", "Phase2") +Create-Task -Title "数据导出功能" -Description "导出任务列表为 Excel/CSV" -Status "PENDING" -Progress 0 -Priority "LOW" -Tags @("功能", "Phase2") +Create-Task -Title "多租户权限体系" -Description "Workspace 级别权限控制 + 用户角色管理" -Status "PENDING" -Progress 0 -Priority "URGENT" -Tags @("权限", "Phase4") +Create-Task -Title "认证与账号体系" -Description "JWT 登录 + 注册 + 密码重置" -Status "PENDING" -Progress 0 -Priority "URGENT" -Tags @("认证", "Phase4") +Create-Task -Title "订阅与计费体系" -Description "SaaS 订阅套餐 + 支付接入(微信/支付宝)" -Status "PENDING" -Progress 0 -Priority "HIGH" -Tags @("商业化", "Phase4") +Create-Task -Title "视频分类模型接入" -Description "真实 AI 模型替换占位分类逻辑" -Status "PENDING" -Progress 0 -Priority "URGENT" -Tags @("AI", "Phase5") +Create-Task -Title "自动剪辑能力" -Description "视频自动剪辑 + 转场特效 + 字幕生成" -Status "PENDING" -Progress 0 -Priority "HIGH" -Tags @("AI", "Phase5") +Create-Task -Title "配音合成能力" -Description "AI 配音 + 音频混音" -Status "PENDING" -Progress 0 -Priority "MEDIUM" -Tags @("AI", "Phase5") + +Write-Host "`n============================================================" -ForegroundColor Cyan +Write-Host "✅ 数据初始化完成!" -ForegroundColor Green +Write-Host "============================================================" -ForegroundColor Cyan +Write-Host "`n访问推进器: http://47.98.113.167:8088/projects" -ForegroundColor Cyan +Write-Host "访问 API 文档: http://47.98.113.167:8089/docs`n" -ForegroundColor Cyan diff --git a/scripts/init_tracker_data.py b/scripts/init_tracker_data.py new file mode 100644 index 000000000..2acbcd8e5 --- /dev/null +++ b/scripts/init_tracker_data.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +""" +小虾 SaaS 项目推进器数据初始化脚本 +将当前项目状态、规则、待办事项录入推进器 +""" +import requests +import json +from datetime import datetime, timedelta + +API_BASE = "http://47.98.113.167:8089/api/v1" + +# 临时创建项目和工作空间(实际应该从数据库获取) +# 这里用固定的 UUID 模拟 +PROJECT_ID = "00000000-0000-0000-0000-000000000001" +WORKSPACE_ID = "00000000-0000-0000-0000-000000000001" + +def create_milestone(title, target_date, description=""): + """创建里程碑""" + payload = { + "title": title, + "target_date": target_date, + "project_id": PROJECT_ID, + "workspace_id": WORKSPACE_ID, + "description": description + } + resp = requests.post(f"{API_BASE}/project-management/milestones", json=payload) + if resp.status_code == 200: + print(f"✅ 里程碑创建成功: {title}") + return resp.json() + else: + print(f"❌ 里程碑创建失败: {title} - {resp.text}") + return None + +def create_task(title, description, priority="MEDIUM", status="PENDING", progress=0, tags=None): + """创建任务""" + payload = { + "title": title, + "description": description, + "project_id": PROJECT_ID, + "workspace_id": WORKSPACE_ID, + "priority": priority, + "status": status, + "progress": progress, + "tags": tags or [] + } + resp = requests.post(f"{API_BASE}/project-management/tasks", json=payload) + if resp.status_code == 200: + print(f"✅ 任务创建成功: {title}") + return resp.json() + else: + print(f"❌ 任务创建失败: {title} - {resp.text}") + return None + +def main(): + print("=" * 60) + print("小虾 SaaS 项目推进器 - 数据初始化") + print("=" * 60) + + # ========== 里程碑 ========== + print("\n📍 创建里程碑...") + + milestones = [ + { + "title": "Phase 1: 核心平台层完成", + "target_date": "2026-06-15", + "description": "Clean Architecture 骨架 + 核心业务对象 + 完整测试" + }, + { + "title": "Phase 2: 项目管理模块落地", + "target_date": "2026-06-16", + "description": "任务/里程碑/问题管理 + 前后端完整链路" + }, + { + "title": "Phase 3: 部署与备案完成", + "target_date": "2026-06-30", + "description": "生产环境部署 + HTTPS 证书 + 域名备案通过" + }, + { + "title": "Phase 4: SAAS 产品化完成", + "target_date": "2026-07-15", + "description": "多租户 + 权限体系 + 订阅计费" + }, + { + "title": "Phase 5: AI 剪辑能力接入", + "target_date": "2026-08-01", + "description": "视频分类模型 + 自动剪辑 + 配音合成" + } + ] + + for m in milestones: + create_milestone(m["title"], m["target_date"], m["description"]) + + # ========== 已完成任务 ========== + print("\n✅ 录入已完成任务...") + + completed_tasks = [ + { + "title": "Clean Architecture 架构设计", + "description": "Domain → Ports → Application → Adapters 分层完成", + "status": "COMPLETED", + "progress": 100, + "priority": "URGENT", + "tags": ["架构", "Phase1"] + }, + { + "title": "核心业务对象实现", + "description": "User/Workspace/Project/AssetLibrary/Asset/IngestJob/ClassificationJob 全部完成", + "status": "COMPLETED", + "progress": 100, + "priority": "HIGH", + "tags": ["业务逻辑", "Phase1"] + }, + { + "title": "双持久化实现", + "description": "In-Memory (测试) + SQLAlchemy (生产) 完成", + "status": "COMPLETED", + "progress": 100, + "priority": "HIGH", + "tags": ["数据库", "Phase1"] + }, + { + "title": "项目管理模块开发", + "description": "任务/里程碑/问题 三大实体 + 11 个 Use Cases + 11 个 API", + "status": "COMPLETED", + "progress": 100, + "priority": "URGENT", + "tags": ["项目管理", "Phase2"] + }, + { + "title": "项目推进器前端开发", + "description": "5 个页面 + 3 个表单 + 完整前后端集成", + "status": "COMPLETED", + "progress": 100, + "priority": "HIGH", + "tags": ["前端", "Phase2"] + }, + { + "title": "Docker Compose 部署", + "description": "5 个容器 (postgres/redis/api/worker/web) 全部运行", + "status": "COMPLETED", + "progress": 100, + "priority": "HIGH", + "tags": ["部署", "Phase3"] + }, + { + "title": "Nginx 反向代理配置", + "description": "8088/8089 临时端口配置完成,绕过备案限制", + "status": "COMPLETED", + "progress": 100, + "priority": "MEDIUM", + "tags": ["部署", "Phase3"] + } + ] + + for task in completed_tasks: + create_task(**task) + + # ========== 进行中任务 ========== + print("\n🔄 录入进行中任务...") + + in_progress_tasks = [ + { + "title": "域名备案审核", + "description": "saas.xiaoxiajianji.com / saas-api.xiaoxiajianji.com 等待工信部审核通过", + "status": "IN_PROGRESS", + "progress": 50, + "priority": "URGENT", + "tags": ["部署", "Phase3", "阻塞"] + }, + { + "title": "HTTPS 证书申请", + "description": "Let's Encrypt 证书,等待备案通过后申请", + "status": "BLOCKED", + "progress": 0, + "priority": "HIGH", + "tags": ["部署", "Phase3", "依赖备案"] + } + ] + + for task in in_progress_tasks: + create_task(**task) + + # ========== 待办任务 ========== + print("\n📋 录入待办任务...") + + pending_tasks = [ + { + "title": "切换到正式域名和 HTTPS", + "description": "备案通过后:改回 80/443 端口 + 申请证书 + nginx 配置 HTTPS", + "status": "PENDING", + "progress": 0, + "priority": "HIGH", + "tags": ["部署", "Phase3", "依赖备案"] + }, + { + "title": "PostgreSQL 生产环境切换", + "description": "当前用 In-Memory,需切换到 PostgreSQL + 数据持久化验证", + "status": "PENDING", + "progress": 0, + "priority": "HIGH", + "tags": ["数据库", "Phase3"] + }, + { + "title": "前端环境变量配置", + "description": "API 地址从临时端口改为正式域名 https://saas-api.xiaoxiajianji.com", + "status": "PENDING", + "progress": 0, + "priority": "MEDIUM", + "tags": ["前端", "Phase3", "依赖备案"] + }, + { + "title": "甘特图视图开发", + "description": "项目推进器增加甘特图/时间线视图,可视化任务时间规划", + "status": "PENDING", + "progress": 0, + "priority": "MEDIUM", + "tags": ["前端", "Phase2"] + }, + { + "title": "批量操作 API", + "description": "任务批量更新状态/优先级/删除接口", + "status": "PENDING", + "progress": 0, + "priority": "LOW", + "tags": ["后端", "Phase2"] + }, + { + "title": "数据导出功能", + "description": "导出任务列表为 Excel/CSV", + "status": "PENDING", + "progress": 0, + "priority": "LOW", + "tags": ["功能", "Phase2"] + }, + { + "title": "多租户权限体系", + "description": "Workspace 级别权限控制 + 用户角色管理", + "status": "PENDING", + "progress": 0, + "priority": "URGENT", + "tags": ["权限", "Phase4"] + }, + { + "title": "认证与账号体系", + "description": "JWT 登录 + 注册 + 密码重置", + "status": "PENDING", + "progress": 0, + "priority": "URGENT", + "tags": ["认证", "Phase4"] + }, + { + "title": "订阅与计费体系", + "description": "SaaS 订阅套餐 + 支付接入(微信/支付宝)", + "status": "PENDING", + "progress": 0, + "priority": "HIGH", + "tags": ["商业化", "Phase4"] + }, + { + "title": "视频分类模型接入", + "description": "真实 AI 模型替换占位分类逻辑", + "status": "PENDING", + "progress": 0, + "priority": "URGENT", + "tags": ["AI", "Phase5"] + }, + { + "title": "自动剪辑能力", + "description": "视频自动剪辑 + 转场特效 + 字幕生成", + "status": "PENDING", + "progress": 0, + "priority": "HIGH", + "tags": ["AI", "Phase5"] + }, + { + "title": "配音合成能力", + "description": "AI 配音 + 音频混音", + "status": "PENDING", + "progress": 0, + "priority": "MEDIUM", + "tags": ["AI", "Phase5"] + } + ] + + for task in pending_tasks: + create_task(**task) + + print("\n" + "=" * 60) + print("✅ 数据初始化完成!") + print("=" * 60) + print(f"\n访问推进器: http://47.98.113.167:8088/projects") + print(f"访问 API 文档: http://47.98.113.167:8089/docs\n") + +if __name__ == "__main__": + main()