# 精油配方计算器 - 部署文档 ## 架构 - **前端**: 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 # 用户管�� │ └── 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 | 服务器版本 |