# ============================== # Stage 1: Build Rust backend # ============================== FROM rust:1-bookworm AS rust-builder WORKDIR /app # 先复制依赖文件以利用 Docker 缓存 COPY Cargo.toml Cargo.lock ./ COPY crates/erp-core/Cargo.toml crates/erp-core/Cargo.toml COPY crates/erp-auth/Cargo.toml crates/erp-auth/Cargo.toml COPY crates/erp-config/Cargo.toml crates/erp-config/Cargo.toml COPY crates/erp-workflow/Cargo.toml crates/erp-workflow/Cargo.toml COPY crates/erp-message/Cargo.toml crates/erp-message/Cargo.toml COPY crates/erp-plugin/Cargo.toml crates/erp-plugin/Cargo.toml COPY crates/erp-health/Cargo.toml crates/erp-health/Cargo.toml COPY crates/erp-ai/Cargo.toml crates/erp-ai/Cargo.toml COPY crates/erp-dialysis/Cargo.toml crates/erp-dialysis/Cargo.toml COPY crates/erp-server/Cargo.toml crates/erp-server/Cargo.toml COPY crates/erp-server/migration/Cargo.toml crates/erp-server/migration/Cargo.toml COPY crates/erp-plugin-prototype/Cargo.toml crates/erp-plugin-prototype/Cargo.toml COPY crates/erp-plugin-test-sample/Cargo.toml crates/erp-plugin-test-sample/Cargo.toml COPY crates/erp-plugin-assessment/Cargo.toml crates/erp-plugin-assessment/Cargo.toml COPY crates/erp-plugin-crm/Cargo.toml crates/erp-plugin-crm/Cargo.toml COPY crates/erp-plugin-freelance/Cargo.toml crates/erp-plugin-freelance/Cargo.toml COPY crates/erp-plugin-inventory/Cargo.toml crates/erp-plugin-inventory/Cargo.toml COPY crates/erp-plugin-itops/Cargo.toml crates/erp-plugin-itops/Cargo.toml # 创建空的 lib.rs/main.rs 占位以缓存依赖 RUN mkdir -p crates/erp-core/src && echo "" > crates/erp-core/src/lib.rs \ && mkdir -p crates/erp-auth/src && echo "" > crates/erp-auth/src/lib.rs \ && mkdir -p crates/erp-config/src && echo "" > crates/erp-config/src/lib.rs \ && mkdir -p crates/erp-workflow/src && echo "" > crates/erp-workflow/src/lib.rs \ && mkdir -p crates/erp-message/src && echo "" > crates/erp-message/src/lib.rs \ && mkdir -p crates/erp-plugin/src && echo "" > crates/erp-plugin/src/lib.rs \ && mkdir -p crates/erp-health/src && echo "" > crates/erp-health/src/lib.rs \ && mkdir -p crates/erp-ai/src && echo "" > crates/erp-ai/src/lib.rs \ && mkdir -p crates/erp-dialysis/src && echo "" > crates/erp-dialysis/src/lib.rs \ && mkdir -p crates/erp-server/src && echo "fn main(){}" > crates/erp-server/src/main.rs \ && mkdir -p crates/erp-server/migration/src && echo "" > crates/erp-server/migration/src/lib.rs \ && for crate in erp-plugin-prototype erp-plugin-test-sample erp-plugin-assessment erp-plugin-crm erp-plugin-freelance erp-plugin-inventory erp-plugin-itops; do \ mkdir -p crates/$crate/src && echo "" > crates/$crate/src/lib.rs; \ done # 构建依赖(仅当 Cargo.toml/Cargo.lock 变化时重新编译) RUN cargo build --release -p erp-server 2>/dev/null || true # 复制实际源码 COPY crates/ crates/ # 重新构建(增量编译,只编译业务代码) RUN cargo build --release -p erp-server # ============================== # Stage 2: Build frontend # ============================== FROM node:20-alpine AS frontend-builder WORKDIR /app RUN corepack enable && corepack prepare pnpm@latest --activate COPY apps/web/package.json apps/web/pnpm-lock.yaml ./apps/web/ RUN cd apps/web && pnpm install --frozen-lockfile COPY apps/web/ ./apps/web/ RUN cd apps/web && pnpm build # ============================== # Stage 3: Production runtime # ============================== FROM debian:bookworm-slim AS runtime RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # 复制 Rust 二进制 COPY --from=rust-builder /app/target/release/erp-server /app/erp-server # 复制配置文件 COPY config/ /app/config/ # 复制前端构建产物(可通过 volume 暴露给 OpenResty) COPY --from=frontend-builder /app/apps/web/dist/ /app/static/ # 创建上传目录 RUN mkdir -p /app/uploads # 非特权用户运行 RUN useradd -r -s /bin/false appuser \ && chown -R appuser:appuser /app USER appuser # 环境变量(运行时通过 docker-compose / .env 覆盖) ENV ERP__SERVER__HOST=0.0.0.0 ENV ERP__SERVER__PORT=3000 ENV ERP__SERVER__METRICS_PORT=9090 ENV ERP__STORAGE__UPLOAD_DIR=/app/uploads EXPOSE 3000 9090 VOLUME ["/app/uploads", "/app/static"] HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ CMD curl -f http://localhost:3000/api/v1/health || exit 1 ENTRYPOINT ["/app/erp-server"]