- Replace single-file 8441-line HTML with Vue 3 SPA - Pinia stores: auth, oils, recipes, diary, ui - Composables: useApi, useDialog, useSmartPaste, useOilTranslation - 6 shared components: RecipeCard, RecipeDetailOverlay, TagPicker, etc. - 9 page views: RecipeSearch, RecipeManager, Inventory, OilReference, etc. - 14 Cypress E2E test specs (113 tests), all passing - Multi-stage Dockerfile (Node build + Python runtime) - Demo video generation scripts (TTS + subtitles + screen recording) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
195 lines
6.8 KiB
Markdown
195 lines
6.8 KiB
Markdown
# 精油配方计算器 - 部署文档
|
||
|
||
## 架构
|
||
|
||
- **前端**: Vue 3 + Vite + Pinia + Vue Router(构建为静态文件,由 FastAPI serve)
|
||
- **后端**: FastAPI + SQLite,端口 8000
|
||
- **部署**: Kubernetes (k3s) on `oci.euphon.net`
|
||
- **域名**: https://oil.oci.euphon.net
|
||
- **TLS**: Traefik + Let's Encrypt (自动)
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
├── backend/
|
||
│ ├── main.py # FastAPI 应用
|
||
│ ├── database.py # SQLite 数据库操作
|
||
│ ├── defaults.json # 默认精油和配方数据(首次启动时 seed)
|
||
│ └── requirements.txt
|
||
├── frontend/ # Vue 3 + Vite 项目
|
||
│ ├── package.json
|
||
│ ├── vite.config.js
|
||
│ ├── index.html
|
||
│ ├── public/ # 静态资源(favicon、PWA icons)
|
||
│ └── src/
|
||
│ ├── main.js # 入口文件
|
||
│ ├── App.vue # 根组件
|
||
│ ├── router/ # Vue Router 路由配置
|
||
│ ├── stores/ # Pinia 状态管理
|
||
│ │ ├── auth.js # 认证/用户
|
||
│ │ ├── oils.js # 精油价格
|
||
│ │ ├── recipes.js # 配方/标签/收藏
|
||
│ │ ├── diary.js # 个人配方日记
|
||
│ │ └── ui.js # UI 状态
|
||
│ ├── composables/ # 组合式函数
|
||
│ │ ├── useApi.js # API 请求封装
|
||
│ │ ├── useDialog.js # 自定义对话框
|
||
│ │ ├── useSmartPaste.js # 智能粘贴解析
|
||
│ │ └── useOilTranslation.js # 精油中英翻译
|
||
│ ├── components/ # 共享组件
|
||
│ │ ├── RecipeCard.vue
|
||
│ │ ├── RecipeDetailOverlay.vue
|
||
│ │ ├── TagPicker.vue
|
||
│ │ ├── LoginModal.vue
|
||
│ │ ├── UserMenu.vue
|
||
│ │ └── CustomDialog.vue
|
||
│ ├── views/ # 页面组件
|
||
│ │ ├── RecipeSearch.vue # 配方查询
|
||
│ │ ├── RecipeManager.vue # 管理配方
|
||
│ │ ├── Inventory.vue # 个人库存
|
||
│ │ ├── OilReference.vue # 精油价目
|
||
│ │ ├── Projects.vue # 商业核算
|
||
│ │ ├── MyDiary.vue # 我的(日记/品牌/账号)
|
||
│ │ ├── AuditLog.vue # 操作日志
|
||
│ │ ├── BugTracker.vue # Bug 追踪
|
||
│ │ └── UserManagement.vue # 用户管<E688B7><E7AEA1>
|
||
│ └── assets/
|
||
│ └── styles.css # 全局样式
|
||
├── deploy/
|
||
│ ├── namespace.yaml
|
||
│ ├── deployment.yaml
|
||
│ ├── service.yaml
|
||
│ ├── pvc.yaml # 1Gi 持久卷,存放 SQLite 数据库
|
||
│ ├── ingress.yaml
|
||
│ ├── setup-kubeconfig.sh
|
||
│ └── kubeconfig
|
||
├── Dockerfile # 多阶段构建(Node → Python)
|
||
└── doc/deploy.md
|
||
```
|
||
|
||
## 本地开发
|
||
|
||
```bash
|
||
# 前端开发(热更新)
|
||
cd frontend
|
||
npm install
|
||
npm run dev # 默认 http://localhost:5173,自动代理 /api 到 :8000
|
||
|
||
# 后端开发
|
||
cd backend
|
||
pip install -r requirements.txt
|
||
uvicorn backend.main:app --reload --port 8000
|
||
```
|
||
|
||
## 构建
|
||
|
||
```bash
|
||
# 前端构建
|
||
cd frontend
|
||
npm run build # 输出到 frontend/dist/
|
||
|
||
# Docker 构建(多阶段:先构建前端,再打包后端)
|
||
docker build -t oil-calculator .
|
||
```
|
||
|
||
## 首次部署
|
||
|
||
已完成,以下为记录。
|
||
|
||
```bash
|
||
# 1. SSH 到服务器
|
||
ssh fam@oci.euphon.net
|
||
|
||
# 2. 上传项目
|
||
scp oil-calc.tar.gz fam@oci.euphon.net:/tmp/
|
||
ssh fam@oci.euphon.net "mkdir -p ~/oil-calculator && cd ~/oil-calculator && tar xzf /tmp/oil-calc.tar.gz"
|
||
|
||
# 3. 构建并推送镜像
|
||
cd ~/oil-calculator
|
||
docker build -t registry.oci.euphon.net/oil-calculator:latest .
|
||
docker push registry.oci.euphon.net/oil-calculator:latest
|
||
|
||
# 4. 部署 k8s 资源
|
||
cd deploy
|
||
kubectl apply -f namespace.yaml
|
||
kubectl apply -f pvc.yaml
|
||
kubectl apply -f deployment.yaml
|
||
kubectl apply -f service.yaml
|
||
kubectl apply -f ingress.yaml
|
||
|
||
# 5. 需要 regcred 才能拉取私有镜像(从已有 ns 复制)
|
||
kubectl get secret regcred -n guitar -o yaml | sed 's/namespace: guitar/namespace: oil-calculator/' | kubectl apply -f -
|
||
```
|
||
|
||
## 更新部署
|
||
|
||
```bash
|
||
# 在本地打包上传
|
||
cd /path/to/oil
|
||
tar czf /tmp/oil-calc.tar.gz Dockerfile backend/ frontend/ deploy/ --exclude='frontend/node_modules' --exclude='frontend/dist'
|
||
scp /tmp/oil-calc.tar.gz fam@oci.euphon.net:/tmp/
|
||
|
||
# SSH 到服务器构建并重启
|
||
ssh fam@oci.euphon.net
|
||
cd ~/oil-calculator && tar xzf /tmp/oil-calc.tar.gz
|
||
docker build -t registry.oci.euphon.net/oil-calculator:latest .
|
||
docker push registry.oci.euphon.net/oil-calculator:latest
|
||
kubectl rollout restart deploy/oil-calculator -n oil-calculator
|
||
```
|
||
|
||
或者使用受限 kubeconfig(仅重启,不含构建):
|
||
|
||
```bash
|
||
KUBECONFIG=deploy/kubeconfig kubectl rollout restart deploy/oil-calculator
|
||
```
|
||
|
||
## 受限 Kubeconfig
|
||
|
||
文件位于 `deploy/kubeconfig`,权限范围:
|
||
- **Namespace**: `oil-calculator` only
|
||
- **ServiceAccount**: `oil-calculator-deployer`
|
||
- **权限**: pods, services, deployments, replicasets, ingresses, PVC, configmaps, secrets, pods/log 的完整 CRUD
|
||
- **无法**访问其他 namespace 或集群级资源
|
||
|
||
重新生成:`bash deploy/setup-kubeconfig.sh`
|
||
|
||
## K8s 配置要点
|
||
|
||
- **Ingress class**: `traefik`
|
||
- **TLS annotation**: `traefik.ingress.kubernetes.io/router.tls.certresolver: le`
|
||
- **镜像仓库**: `registry.oci.euphon.net`(需要 `regcred` secret)
|
||
- **数据持久化**: PVC 挂载到 `/data`,SQLite 数据库在 `/data/oil_calculator.db`
|
||
|
||
## API 端点
|
||
|
||
| Method | Path | 说明 |
|
||
|--------|------|------|
|
||
| GET | /api/oils | 所有精油列表 |
|
||
| POST | /api/oils | 添加/更新精油 |
|
||
| DELETE | /api/oils/{name} | 删除精油 |
|
||
| GET | /api/recipes | 所有配方列表 |
|
||
| POST | /api/recipes | 新增配方 |
|
||
| PUT | /api/recipes/{id} | 更新配方 |
|
||
| DELETE | /api/recipes/{id} | 删除配方 |
|
||
| GET | /api/tags | 所有标签 |
|
||
| POST | /api/tags | 新增标签 |
|
||
| DELETE | /api/tags/{name} | 删除标签 |
|
||
| GET | /api/me | 当前用户信息 |
|
||
| POST | /api/login | 登录 |
|
||
| POST | /api/register | 注册 |
|
||
| GET | /api/diary | 个人配方列表 |
|
||
| POST | /api/diary | 创建个人配方 |
|
||
| PUT | /api/diary/{id} | 更新个人配方 |
|
||
| DELETE | /api/diary/{id} | 删除个人配方 |
|
||
| GET | /api/favorites | 收藏列表 |
|
||
| POST | /api/favorites/{id} | 添加收藏 |
|
||
| DELETE | /api/favorites/{id} | 取消收藏 |
|
||
| GET | /api/inventory | 个人库存 |
|
||
| POST | /api/inventory | 更新库存 |
|
||
| GET | /api/projects | 商业项目列表 |
|
||
| GET | /api/audit-log | 操作日志 |
|
||
| GET | /api/users | 用户列表(管理员)|
|
||
| GET | /api/bug-reports | Bug 列表 |
|
||
| GET | /api/notifications | 通知列表 |
|
||
| GET | /api/version | 服务器版本 |
|