diff --git a/DELIVERY_PACKAGING_NOTES.md b/DELIVERY_PACKAGING_NOTES.md new file mode 100644 index 0000000..6033bd8 --- /dev/null +++ b/DELIVERY_PACKAGING_NOTES.md @@ -0,0 +1,102 @@ +# tjwater-server Customer Image Packaging Notes + +This repository is the customer-delivery backend package. Build delivery images from this repository root. + +## Image Build + +Build the customer backend image: + +```bash +docker build -t tjwater-server:latest . +``` + +The `Dockerfile` already performs source encapsulation in the builder stage: + +```bash +python scripts/compile.py +python scripts/compile.py --delete-source +``` + +The compiled and source-deleted `app/` directory is then copied into the final runner stage. The source deletion happens inside the Docker builder layer only; it does not delete local workspace files. + +## Encapsulation Scope + +`scripts/compile.py` defaults to compiling these sensitive areas: + +- `app/services` +- `app/native/wndb` +- `app/algorithms` +- `app/infra/epanet/epanet.py` + +These areas should not contain uncompiled `.py` source files in the delivered image. Public entry points, API route wiring, schemas, configuration, and operational files may remain readable when required for runtime. + +## Verification + +After building, verify the image tag: + +```bash +docker image ls tjwater-server +``` + +Verify that core source files were removed and compiled extensions exist: + +```bash +docker run --rm --entrypoint sh tjwater-server:latest -c "\ +printf 'core_py_count='; \ +find /app/app/services /app/app/native/wndb /app/app/algorithms /app/app/infra/epanet/epanet.py -name '*.py' 2>/dev/null | wc -l; \ +printf 'core_so_count='; \ +find /app/app/services /app/app/native/wndb /app/app/algorithms /app/app/infra/epanet -name '*.so' 2>/dev/null | wc -l; \ +python -c 'import app.main; print(\"import_app_main=ok\")'" +``` + +Expected delivery result: + +```text +core_py_count=0 +core_so_count= +import_app_main=ok +``` + +Warnings from third-party packages during import are not necessarily build failures. Treat non-zero exit codes, failed imports, or leftover core `.py` files as blockers. + +## Export For Windows Delivery + +Export the image tarball to the Windows desktop from WSL: + +```bash +docker save -o /mnt/c/Users/admin/Desktop/tjwater-server-latest.tar tjwater-server:latest +``` + +Adjust the Windows username if needed. To find available desktop paths: + +```bash +find /mnt/c/Users -maxdepth 2 -type d \( -name Desktop -o -name 桌面 \) 2>/dev/null +``` + +On the target machine, import the image with: + +```bash +docker load -i tjwater-server-latest.tar +``` + +## Deployment Caution + +The development `infra/docker/docker-compose.yml` bind-mounts local source: + +```yaml +volumes: + - ../../app:/app/app + - ../../resources:/app/resources +``` + +Do not use that source mount for customer delivery of the encapsulated image. Mounting `../../app` over `/app/app` replaces the compiled code inside the image with local source files and defeats the encapsulation. For delivery compose files, use the built image directly and mount only required runtime data/config paths. + +## Local Safety + +Do not run this destructive command in the normal working tree: + +```bash +python scripts/compile.py --delete-source +``` + +Only use `--delete-source` in Docker builder stages or in a disposable delivery copy. The normal delivery image build already handles this safely. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4cfba1b --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# TJWaterServerCustomer 客户版后端 + +`TJWaterServerCustomer` 是 TJWater 客户交付版 Python 后端。该仓库应被视为可部署交付包,只保留客户运行、配置、部署、诊断和交付说明所需内容。 + +## 技术栈 + +- Python 3.12 +- FastAPI / Uvicorn +- Pydantic / SQLAlchemy / psycopg +- Redis、PostgreSQL、PostGIS、TimescaleDB +- WNTR、EPANET、Cython、科学计算与空间分析依赖 +- pytest + +## 目录结构 + +```text +app/main.py FastAPI 入口 +app/api/ HTTP API 路由 +app/auth/ 认证和权限上下文 +app/core/ 配置、日志和基础设施初始化 +app/domain/ 领域模型和 Pydantic schema +app/infra/ 数据库、缓存、EPANET 和外部集成 +app/services/ 核心业务服务,交付镜像中必须封装 +app/algorithms/ 核心算法,交付镜像中必须封装 +app/native/ 本地管网数据读写与转换,交付镜像中必须封装 +scripts/compile.py Cython 封装脚本 +infra/docker/ Docker Compose 编排 +tests/ 后端测试 +``` + +## 本地开发 + +推荐使用已有 conda 环境: + +```bash +conda run -n server python -m pytest tests -q +conda run -n server python scripts/run_server.py +``` + +本地调试不要在正常工作树执行源码删除命令。 + +## 客户版镜像打包 + +交付镜像标签通常为: + +```bash +docker build -t tjwater-server:latest . +``` + +`Dockerfile` 会在 builder 阶段执行: + +```bash +python scripts/compile.py +python scripts/compile.py --delete-source +``` + +源码删除只发生在 Docker 构建层内,不会删除本地工作树源码。详细封装、验证和 Windows 导出说明见: + +```text +DELIVERY_PACKAGING_NOTES.md +``` + +## 封装范围 + +`scripts/compile.py` 默认封装: + +- `app/services` +- `app/native/wndb` +- `app/algorithms` +- `app/infra/epanet/epanet.py` + +交付镜像中这些核心目录不应残留未编译的 `.py` 源码,应以 `.so` 扩展模块运行。 + +## 验证命令 + +构建后检查镜像: + +```bash +docker image ls tjwater-server +``` + +检查核心源码是否已删除、FastAPI 入口是否可导入: + +```bash +docker run --rm --entrypoint sh tjwater-server:latest -c "\ +printf 'core_py_count='; \ +find /app/app/services /app/app/native/wndb /app/app/algorithms /app/app/infra/epanet/epanet.py -name '*.py' 2>/dev/null | wc -l; \ +printf 'core_so_count='; \ +find /app/app/services /app/app/native/wndb /app/app/algorithms /app/app/infra/epanet -name '*.so' 2>/dev/null | wc -l; \ +python -c 'import app.main; print(\"import_app_main=ok\")'" +``` + +期望结果: + +```text +core_py_count=0 +core_so_count=<非零> +import_app_main=ok +``` + +## 部署注意 + +客户交付时不要把本地 `../../app` 挂载到容器 `/app/app`,否则会覆盖镜像内已封装代码。交付 compose 文件应使用构建好的镜像,只挂载必要的运行数据和配置。 + +## 安全规则 + +不要提交 `.env`、生产凭据、客户数据、数据库 dump、日志、生成缓存、临时交付压缩包或本地运行目录。客户版仓库不应加入内部实验、调试工具或非交付源码材料。