chore: 初始化 app-cli,本地开发环境脚本

- dev/dev.ps1: 一键启动后端(编译后运行)+前端(4321),校验 CORS 与 astro dev 单实例锁
- dev/stop.ps1: 按 PID 登记停止,-Force 清残留
- dev/status.ps1: 端口/PID/健康检查一览
- dev/doctor.ps1: 工具链/路径/依赖/数据库/端口/CORS 体检
- vision/vision.ps1: CLIP 向量服务起停
- lib/common.ps1: 路径探测、端口、进程登记、HTTP 探测
- config/local.env.example: 路径与端口覆盖模板
This commit is contained in:
toom1996
2026-10-22 10:37:59 +08:00
commit e366ee7498
10 changed files with 1269 additions and 0 deletions

140
README.md Normal file
View File

@ -0,0 +1,140 @@
# app-cli
运维脚本仓库。**不放业务代码**,只放「怎么跑起来、怎么检查、怎么上线」的操作入口。
三个业务仓各自独立,本仓库通过路径引用它们:
```
<project>/
├─ app-cli/ ← 本仓库:脚本与部署编排
├─ backend/ ← Go 后端(8090 公开 / 8091 SSG / 8092 后台)
├─ test/test/ ← Astro 前端
└─ fashion-siglip/ ← CLIP 向量服务(可选,仅后台「疑似同款」用)
```
## 为什么单独一个仓库
| 理由 | 具体好处 |
|---|---|
| 脚本不跟业务版本绑在一起 | 改部署流程不用动业务仓,回滚业务代码不会把脚本一起回滚掉 |
| 一处放全部「怎么运行」的知识 | 换机器只 clone 这个仓库 + 配一份 `config/local.env` |
| 服务器只需拉这一个仓库 | 蓝绿部署时槽位切换、备份、健康检查都在这里 |
代价是**目录必须同级**(脚本要能按 `../backend` 找到业务仓)。路径不一致时在 `config/local.env` 里显式写死。
---
## 目录结构(当前进度)
```
app-cli/
├─ dev.cmd # 双击启动(转发给 dev/dev.ps1)
├─ config/
│ ├─ local.env.example # 路径/端口覆盖模板(复制成 local.env)
│ └─ local.env # 本机实际值,不入库
├─ lib/
│ └─ common.ps1 # 公共函数:路径探测/端口/进程/HTTP(已在用)
├─ dev/ # ★ 本地环境(第一版已完成)
│ ├─ dev.ps1 # 一键启动:后端 + 前端(可选 CLIP)
│ ├─ stop.ps1 # 按 PID 登记停止,支持 -Force 清残留
│ ├─ status.ps1 # 端口 / PID / 健康检查一览
│ └─ doctor.ps1 # 体检:版本/路径/依赖/数据库/端口/CORS
└─ vision/
└─ vision.ps1 # CLIP 服务 start/stop/restart/status/logs
# 下面是规划中的目录,本轮还没建:
# ├─ check/ # 提交前检查:go build+vet+test、astro check
# ├─ deploy/ # 生产部署:docker-compose、nginx、deploy.sh、init-db.sh
# └─ docs/ # 排障手册
```
## 脚本分四类
| 类别 | 目录 | 运行在哪 | 现在 |
|---|---|---|---|
| 本地环境 | `dev/` | 开发机 Windows | ✅ 已完成 |
| 视觉服务 | `vision/` | 开发机 / 服务器 | ✅ 已完成 |
| 提交前检查 | `check/` | 开发机 | 待建 |
| 生产部署 | `deploy/` | Linux 服务器 | 待迁入(现在散在前端仓) |
分界的意义:`dev/` 里的东西**永远不在生产跑**,`deploy/` 里的东西**不在开发机跑**。混在一起就会出现「为了本地方便给生产脚本加了个开关」这类事故。
---
## 本地开发怎么用
首次:
```powershell
cd <project>/app-cli
copy config\local.env.example config\local.env # 可选,不填也能跑(自动探测同级目录)
.\dev\doctor.ps1 # 先体检
```
日常:
```powershell
.\dev\dev.ps1 # 后端 + 前端
.\dev\dev.ps1 -Vision -Open # 连 CLIP 一起起,并打开浏览器
.\dev\dev.ps1 frontend # 只起前端(后端已在跑)
.\dev\dev.ps1 -Force # 端口被占时先杀掉占用者
.\dev\status.ps1 # 看状态
.\dev\stop.ps1 # 停掉
```
也可以双击 `dev.cmd`(自动带 `-ExecutionPolicy Bypass`,绕开脚本执行策略限制)。
### 端口约定
| 端口 | 服务 | 谁能访问 |
|---|---|---|
| 8090 | 后端公开 API | 前端浏览器直连(dev 模式下 `baseApi` 写死这个地址) |
| 8091 | 后端 SSG 内部接口 | 仅构建期取数用 |
| 8092 | 管理后台 `/admin` | 本机 |
| 4321 | Astro dev server | 浏览器 |
| 8900 | CLIP 向量服务 | 仅 127.0.0.1 |
**4321 不是随便选的**:后端 `cors.allow_origins` 里放行的就是 `http://localhost:4321`。
而前端 `astro.config.mjs` 的 `server.port` 是 **3000**(那是给容器内 node 常驻用的),
所以直接 `npm run dev` 会起在 3000 —— 浏览器请求会被 CORS 拦掉,页面表现为「没数据」。
`dev.ps1` 用 `--port 4321` 显式覆盖,并在启动后**自检 CORS 回显**,不一致直接告警。
### 为什么后端是「先编译再运行」而不是 `go run`
`go run` 会派生一个 `main.exe` 子进程,PID 杀掉 `go run` 父进程后子进程可能还在,端口继续被占——正是「明明关了服务 8090 还能通」的成因。
`dev.ps1` 用 `go build -o .bin/fashionapi-dev.exe` 再直接运行,进程树干净,`stop.ps1` 能准确回收。
### 日志与状态
```
.logs/backend.log .logs/backend.err.log
.logs/frontend.log .logs/frontend.err.log
.logs/vision.log .logs/vision.err.log
.run/backend.pid .run/frontend.pid .run/vision.pid
```
`stop.ps1` 优先按 PID 停;端口上还有本脚本管不到的进程(比如手动 `go run` 留下的)时只报告不硬杀,加 `-Force` 才清。
---
## 设计取舍
**1. 不用 pm2 / 不用 supervisor(本地)**
进程管理交给脚本 + PID 登记。引入 pm2 只是多一层要维护的状态,而失败场景(谁占着端口、为什么没起来)反而变难查。
**2. 只做 PowerShell,不做 .sh 双份**
开发机是 Windows。生产是 Linux,那边用 `deploy/` 的 bash 脚本,本来就该分开写——同一套逻辑写两遍,最后一定是两份都只测过一份。
**3. 路径自动探测 + 显式覆盖**
默认按同级目录找,用标记文件(`cmd\server\main.go` / `astro.config.mjs` / `app.py`)校验。写错路径会在启动前报错,不会静默启动到空目录。
**4. 体检和启动分离**
`doctor.ps1` 只读不写,可以随时跑;`dev.ps1` 只管启动。合并成一个会出现「想查问题时它顺手改了环境」。
---
## 下一步
1. `check/check.ps1`:`go build` + `go vet` + `go test ./internal/...` + `astro check` 一条命令
2. `deploy/`:把现在散落在前端仓的 `docker-compose.yml`、`nginx/`、`deploy.sh`、`init-db.sh` 迁进来,改成 `build: context: ../backend` / `../test/test`
3. `docs/`:把「上线后 5 处点检」和常见故障对照表写成文档