--- name: tool-website-stack description: 指定该技能时加载。 disable-model-invocation: true --- # 工具网站开发技术栈 ## 评审与前提论证(必须先做) 在给出架构结论、改栈或写 `.drone.yml`/Caddy 配置前,必须完成一轮**前提显式化 + 自我反驳**,再进入实现;不得默认「显然成立」的隐含假设。 - **列出前提**:把当前方案依赖的假设写成可检验的条目(流量与并发写、是否多机、runner 上是否已有 Go/Node、是否需要持久会话、是否必须容器隔离、合规/密钥归属等)。 - **反驳与边界**:对每条前提追问「若假则怎样」——例如 SQLite 单写瓶颈、exec runner 与宿主机污染、纯静态托管 vs 需要 BFF、Caddy 与 SPA 路由冲突;至少给出一条会推翻当前选型的反例场景。 - **结论带条件**:最终推荐写成「在 A、B、C 成立时采用 X;若 D 则改为 Y」,避免无条件绝对表述。 - **变更评审**:涉及技术栈、部署路径或 CI 形态的改动,用简短对照表说明「改前假设 / 改后假设 / 风险 / 回滚」后再落代码或配置。 ## 技术栈 - **后端**:golang, sqlite - **前端**:vite + react - **部署**:drone-ci(type: exec,执行器在服务器环境),caddy;**应用进程用 `nohup` 后台启动,禁止使用 `systemctl` / systemd 单元管理本服务** ## 后端(Go + SQLite) - 优先保持单二进制、少依赖:SQLite 适合单机工具站与中小流量;迁移用 `embed` 或显式 SQL 文件,避免隐式魔法。 - API 与静态资源分工清晰:由 Go 提供 API/SSR(若需要)或由 Go 仅作 BFF,前端静态由 Caddy 或 Go `embed` 二选一,勿混用两套来源导致缓存与路由冲突。 - 连接 SQLite 时设置合理 `busy_timeout`、单写者多读者模型;并发写多时再讨论迁移,默认不引入额外数据库。 ## 前端(Vite + React) - 使用 Vite 约定:`npm run build` 产出 `dist`,环境变量以 `VITE_` 前缀暴露给客户端;敏感配置不放前端。 - 与 Go 联调:开发期用 Vite dev server 代理 API 到本地 Go 端口;生产构建产物路径与 Caddy/Go 静态路由一致。 ## CI(Drone,type: exec) - **exec 执行器在服务器本机运行**:流水线步骤直接在该机 shell 环境执行,不是 Kubernetes/docker pipeline 的隔离容器。 - `.drone.yml` 中需 `type: exec`;脚本依赖服务器已安装的工具链(Go、Node、sqlite3 等),改 CI 时注明需在 runner 主机预装的依赖。 - 密钥用 Drone 仓库/组织密钥注入环境变量,勿写入仓库明文。 ## 部署(Caddy) - 典型模式:Caddy 终止 TLS,反代到 Go 监听端口;或对静态 `root` 指向 Vite `dist`,`/api` 等路径 `reverse_proxy` 到后端。 - 优先 Caddyfile 声明式配置;注意 `file_server` 与 SPA `try_files` 类行为与 React Router 模式一致。 ## 进程管理(Go 应用) - **禁止**使用 `systemctl`、systemd unit、`daemon-reload` 等方式管理本仓库的 Go 二进制。 - **使用 `nohup`**:部署脚本在替换二进制后,先结束旧进程(例如 `pkill -x <二进制名>`,或读 PID 文件 `kill`),再执行 `nohup /path/to/binary >> /path/to/app.log 2>&1 &`;必要时 `echo $!` 写入 `.pid` 便于下次优雅停止。 - 环境变量在启动该行的 shell 中已 `export`,或由 Drone 步骤注入后再执行 `nohup`,勿依赖 systemd 的 `Environment=`。 ## 开发与部署文档(功能完成后) 功能或里程碑合并前,应把「如何在本机跑通、如何打出生产包、流水线与服务器上发生什么」写清楚,便于本人或他人接手与排障。下列条目按本栈默认约定组织;若某仓库的 `Makefile`、`.drone.yml`、nohup 启动命令与日志路径不同,以仓库内实际文件为准。 ### 1. 本地开发与联调 - **后端**:通常 `make dev-backend` 或等价命令(例如 `go run -tags dev .`);确认 `ADDR`、`DB_PATH`、`JWT_SECRET` 等环境变量在开发期的取值与文档一致。 - **前端**:通常 `make dev-frontend` 或 `cd frontend && npm run dev`;通过 Vite 代理将 API 指到本地 Go 端口,避免生产路径硬编码。 - **数据库与文件**:说明 SQLite 文件路径、上传目录等是否在仓库外或需初始化脚本;新增迁移或种子数据时写明执行顺序。 ### 2. 合并前的本地验证(与 CI 对齐) - 在仓库根目录执行与 CI 相同的构建序列(例如 `Makefile` 的 `build`:`frontend` 下 `npm ci && npm run build`,再 `go build …`),确保无仅本地才有的依赖或未提交的 `frontend/dist` 误用。 - 若有 `go test`、`npm test`、lint,在说明中列出应执行的命令;无测试时至少注明「已手工验证的场景」(关键 API、页面、鉴权路径)。 ### 3. 生产构建与二进制 - **产物**:单一 Go 二进制(可内嵌前端 `dist`)或「二进制 + 静态目录」二选一,与 Caddy/路由设计一致;写明生产构建使用的 Go build tags(例如 `dev` 与默认/生产 embed 的差异)。 - **优化**:如使用 `-ldflags="-s -w"` 等,在文档中一笔带过即可,避免与 CI 中的 `go build` 参数不一致。 ### 4. CI(Drone exec)在服务器上做什么 - **触发条件**:例如仅 `main` 分支;说明是否「每次 push 都构建、仅 main 部署」。 - **构建步骤**:与 `.drone.yml` 逐步对应(进入 `frontend`、`npm ci`、`npm run build`、`go build` 输出路径)。 - **部署步骤**:例如将二进制 `cp` 到固定路径后,结束旧进程再以 `nohup /path/to/binary >> /path/to.log 2>&1 &` 拉起新进程(**不得**使用 `systemctl`);注明需要 **exec runner 所在机器** 已预装 Go/Node/sqlite3 及版本下限,与 `.drone.yml` 注释一致。 - **密钥**:列出需在 Drone 中配置的密钥名(不落仓库);若新增环境变量,说明在 Drone 步骤 `environment` 中注入,或在部署 shell 里 `export` 后再执行 `nohup`。 ### 5. 运行时与 Caddy - **进程**:监听地址与端口(如 `:8882`)、工作目录、读写路径(数据库、上传目录)。 - **Caddy**:站点块域名、`reverse_proxy` 或 `file_server` 与 SPA 路由的关系;TLS 由 Caddy 管理时,无需在应用内重复监听 443。 - **健康检查**:若有 `/health` 或等价端点,写明供负载均衡或运维探活使用。 ### 6. 交付物形式(收尾时建议产出) - **PR / 合并说明**中简述:行为变更、配置项变更、是否需要一次手动迁移数据,以及部署侧是否需调整 `pkill`/日志路径/`nohup` 行。 - **可选**:若仓库维护 `README` 或独立 `docs/deploy.md`,将本节要点同步到该文件,避免知识只存在于对话中;**未要求时不要新建大段无关文档**,仅补充与本次部署相关的增量。 ### 7. 回滚与风险 - 部署失败时:恢复上一版二进制(或从上一成功构建的制品恢复),结束当前进程后再次用 `nohup` 启动旧二进制;若涉及数据库 schema,说明是否可安全回滚或需备份先行。 - 对 SQLite 文件、上传目录在部署脚本中的覆盖风险点一句提醒。 ## 默认偏好 - 遵守上文「评审与前提论证」:重大决策前先写完前提与反驳,再动手。 - 改动范围限于实现需求:不无故更换栈内技术或引入与 sqlite/exec runner 冲突的部署假设。 - 新建子项目时目录与命名与现有仓库一致;无现成约定时采用常见布局:`cmd/`、`internal/`、`web/` 或 `frontend/`。