xueyiming 2 недель назад
Родитель
Сommit
cc509d0810
4 измененных файлов с 153 добавлено и 0 удалено
  1. 26 0
      .dockerignore
  2. 39 0
      Dockerfile
  3. 8 0
      api/app.py
  4. 80 0
      scripts/docker-deploy.sh

+ 26 - 0
.dockerignore

@@ -0,0 +1,26 @@
+.git
+.gitignore
+.venv
+__pycache__
+*.py[cod]
+*.egg-info
+.env
+.env.*
+!.env.example
+.idea
+.vscode
+.pytest_cache
+.ruff_cache
+*.log
+logs/
+tests/
+examples/
+skills/
+node_modules/
+web/node_modules/
+web/dist/
+visualization/.superpowers/
+README_myself.md
+agents/README.md
+*.md
+!README.md

+ 39 - 0
Dockerfile

@@ -0,0 +1,39 @@
+# Stage 1: build frontend
+FROM node:20-alpine AS web-builder
+
+WORKDIR /app/web
+
+COPY web/package.json web/package-lock.json ./
+RUN npm ci
+
+COPY web/ ./
+RUN npm run build
+
+# Stage 2: Python runtime
+FROM python:3.11-slim
+
+WORKDIR /app
+
+ENV PYTHONUNBUFFERED=1 \
+    PYTHONDONTWRITEBYTECODE=1 \
+    PIP_NO_CACHE_DIR=1 \
+    PIP_DISABLE_PIP_VERSION_CHECK=1
+
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends gcc \
+    && rm -rf /var/lib/apt/lists/*
+
+COPY pyproject.toml requirements.txt README.md ./
+COPY supply_agent/ supply_agent/
+COPY supply_infra/ supply_infra/
+COPY agents/ agents/
+COPY api/ api/
+COPY jobs/ jobs/
+
+RUN pip install ".[odps]"
+
+COPY --from=web-builder /app/web/dist ./web/dist
+
+EXPOSE 8080
+
+CMD ["python", "-m", "api"]

+ 8 - 0
api/app.py

@@ -1,8 +1,11 @@
 """FastAPI application — category tree API on port 8080."""
 from __future__ import annotations
 
+from pathlib import Path
+
 from fastapi import FastAPI, HTTPException, Query
 from fastapi.middleware.cors import CORSMiddleware
+from fastapi.staticfiles import StaticFiles
 
 from api.services.category_tree import build_category_tree
 from api.services.demand_belong_category import list_demand_belong_categories
@@ -62,3 +65,8 @@ def demand_belong_oss_logs() -> dict:
     """Return demand_belong_category_agent oss_logs ordered by create_time desc."""
     items = list_demand_belong_oss_logs()
     return {"items": items}
+
+
+_web_dist = Path(__file__).resolve().parent.parent / "web" / "dist"
+if _web_dist.is_dir():
+    app.mount("/", StaticFiles(directory=_web_dist, html=True), name="web")

+ 80 - 0
scripts/docker-deploy.sh

@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+# Build and push Docker image from project source.
+#
+# Usage:
+#   ./scripts/docker-deploy.sh
+#
+# Custom build directory / registry / tag:
+#   BUILD_DIR=/your/build/path \
+#   IMAGE_REGISTRY=registry-vpc.cn-hangzhou.aliyuncs.com/stuuudy/external_demand \
+#   BUILD_TIMESTAMP=20260717143000 \
+#   ./scripts/docker-deploy.sh
+#
+# Environment variables:
+#   BUILD_DIR        Source directory containing Dockerfile (default: repo root)
+#   IMAGE_REGISTRY   Docker image repository
+#   BUILD_TIMESTAMP  Image tag (default: YYYYMMDDHHMMSS)
+#   PUSH             Push after build (default: true)
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+
+BUILD_DIR="${BUILD_DIR:-$PROJECT_ROOT}"
+IMAGE_REGISTRY="${IMAGE_REGISTRY:-registry-vpc.cn-hangzhou.aliyuncs.com/stuuudy/external_demand}"
+BUILD_TIMESTAMP="${BUILD_TIMESTAMP:-$(date +%Y%m%d%H%M%S)}"
+PUSH="${PUSH:-true}"
+
+IMAGE_TAG="${IMAGE_REGISTRY}:${BUILD_TIMESTAMP}"
+LATEST_TAG="${IMAGE_REGISTRY}:latest"
+
+usage() {
+  cat <<EOF
+Usage: $(basename "$0") [options]
+
+Options:
+  --no-push    Build only, do not push to registry
+  -h, --help   Show this help
+
+Environment:
+  BUILD_DIR        ${BUILD_DIR}
+  IMAGE_REGISTRY   ${IMAGE_REGISTRY}
+  BUILD_TIMESTAMP  ${BUILD_TIMESTAMP}
+EOF
+}
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    --no-push)
+      PUSH=false
+      shift
+      ;;
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    *)
+      echo "Unknown option: $1" >&2
+      usage
+      exit 1
+      ;;
+  esac
+done
+
+echo "==> Building from: ${BUILD_DIR}"
+echo "==> Image tag: ${IMAGE_TAG}"
+
+cd "$BUILD_DIR"
+docker build -t "$IMAGE_TAG" -t "$LATEST_TAG" .
+
+if [[ "$PUSH" == "true" ]]; then
+  echo "==> Pushing: ${IMAGE_TAG}"
+  docker push "$IMAGE_TAG"
+  docker push "$LATEST_TAG"
+else
+  echo "==> Skipping push"
+fi
+
+echo "==> Done"
+echo "    ${IMAGE_TAG}"
+echo "    ${LATEST_TAG}"