Files
LokiStack/CLAUDE.md
T
Logan.LiandClaude 327e282356 refactor: 前后端目录结构模块化 + v2 优化移植与缺陷修复
前端:
- src/srv/api → src/api,按域分文件夹(auth/cluster/host/storage/deployment/collector)
- 新增 http.ts 引擎(http.get/post/put/delete),域模块改用语义化调用
- index.html 增加 favicon(匹配应用 logo 的 emerald + Activity 脉冲)

后端分层增强(保留包名):
- 新增 repository/:从 models 拆出 JsonDB 持久化层(InitDB/GetDB/Shutdown/RequestSave)
- 新增 dto/:从 controllers 抽出各域请求契约(带 binding 校验)
- 新增 pkg/crypto、pkg/jwt:从 utils 拆出安全原语(外部 jwt 别名 jwtv5 避免冲突)
- models 只留领域实体 + sanitizer;utils 只留 gin/应用相关(response/errors)

v2 优化移植与缺陷修复:
- 移植密钥分离/SSH TOFU/并发写安全/Loki 生产级配置/Docker 加固
- 修复 deployment.go 并发丢更新、Helm values 重复键、EncryptField 哨兵、loki-gateway 端点等审计缺陷
- 实现真正的优雅关停(main.go http.Server + signal + repository.Shutdown)
- 修复登录契约:后端返回 accessToken,前端 auth.ts/Login.tsx 对齐
- 补 crypto/jwt/repository 单元测试(含并发写回归)

验证: go build/vet/test 全绿;npm run build 通过;登录→鉴权→优雅关停冒烟通过

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-30 10:46:26 +08:00

7.1 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

项目概览

LokiDeploy 是一个基于 Gin (Go) + React/TypeScript + JSON 文件存储 的平台,用于把 Grafana Loki 日志聚合系统部署到 Kubernetes 集群(通过 kubectl/helm)或裸金属 Linux 主机(通过 SSH)。 Go 二进制同时提供 REST API 服务和已构建的前端(SPA)静态资源。

默认登录账号: admin / admin123(首次启动时由 backend/seed/seed.go 写入数据库)。

常用命令

启动后端前的必备环境变量

后端在缺少以下变量时会 拒绝启动(由 backend/config/config.go 通过 log.Fatal 强制)。 ./run.sh 会自动注入开发用默认值;直接 go run 或 Docker 则不会。

export JWT_SECRET=$(openssl rand -hex 32)        # >= 32 字符
export ENCRYPTION_KEY=$(openssl rand -hex 32)    # >= 32 字节(不是 16,详见「安全」一节)

开发模式(两个终端,热重载)

# 终端 1 — 后端(端口 8080)
cd backend && go run .

# 终端 2 — 前端(端口 3000,会把 /api 代理到 :8080,见 vite.config.ts)
cd frontend && npm install && npm run dev

也可以用 ./run.sh,它会同时拉起前后端并注入开发用默认密钥。

构建单体二进制(前端 + 后端)

bash build.sh          # 产物:backend/bin/lokideploy,并把 frontend/dist 复制到 backend/dist
cd backend && go build -o bin/lokideploy .   # 仅构建后端
cd frontend && npm run build                 # 仅构建前端(tsc -b && vite build)

测试与 lint

cd backend && go test ./...                       # 所有 Go 测试(utils、repository)
cd backend && go test ./utils/ -run TestName -v   # 运行单个测试
cd frontend && npm run lint                       # ESLint

注意:models/ 目录目前 没有 测试,测试集中在 utils/ 和 repository/。

Docker

export JWT_SECRET=... ENCRYPTION_KEY=...
docker compose up -d                       # 开发用 compose(docker-compose.yml)
make build start stop logs status clean    # Makefile 快捷命令
docker compose -f docker-compose.prod.yml up -d   # 生产环境(读取 ./secrets/*.txt;先运行 `make secrets`)

架构

后端分层(backend/,模块路径 lokideploy,Go 1.24)

请求流转:router → middleware(JWT)→ controller → service → repository(JsonDB)。

  • config/ — GetConfig() 单例。读取环境变量;密钥缺失或长度不足时直接 log.Fatal。
  • routers/router.go — 所有路由注册在 /api/v1 下;注册 SPA 静态资源服务,并用 NoRoute 回退到 index.html 以支持前端路由。findDistPath() 负责相对于可执行文件定位 dist/ (兼容 go run 的临时目录和打包后的二进制两种情况)。
  • controllers/ — 薄薄的 Gin handler,每个资源一个文件。职责:绑定/校验入参、调用 service、 用 models.SanitizedJSON(...) 包装返回结果。
  • services/ — 业务逻辑层。部署流水线就在这里: DeploymentService.Deploy() → LokiDeployer.Deploy(),按部署模式 (k8s-helm / k8s-yaml / host-binary)分发,实际通过 shell 调用 kubectl/helm 或 SSH。 其它 service:K8sClient、ssh(+ ssh_errors)、storage、cluster_info, 以及较新的 collector(日志采集器) 模块(collector*.go — 部署客户端日志采集器)。
  • middleware/jwt.go — JWTAuth()(/authorized 路由组必用)、AdminAuth()。
  • utils/ — response.go(响应封装助手)、crypto.go(Argon2 + AES-GCM)、jwt.go。
  • seed/ — 数据库为空时写入默认 admin 用户。

数据层 — JSON 文件数据库(唯一数据源)

持久化是 单个 JSON 文件,位于 $DATA_DIR/lokideploy.json(默认 ./data/)。 没有 SQL 数据库。repository.JsonDB 是一个与文件对应的内存结构:

  • 并发模型(重要):写操作由 Lock()/RLock() 保护,随后 RequestSave() 会通过一个 单一的、串行化的 saveWorker goroutine 调度一次 防抖(500ms)原子写 (写临时文件 → fsync → rename)。worker 可能仍在运行时 绝对不要 直接调用 Save(); 常规写入请用 RequestSave()。
  • 优雅关停的顺序很关键(main.go + repository.Shutdown):停防抖定时器 → 关闭 worker 并 等待退出 → 最后一次 doSave。顺序颠倒会导致更新丢失。
  • 这意味着应用 只能单实例运行 — 不可以水平扩展。
  • NextID(table) 为每个集合分配单调递增的整数 ID。

存在两份 JsonDB 实现。 真正在用的是 repository/db.go,被 main.go、seed 以及所有 service 通过 repository.GetDB() 调用。models/db.go 是一份 正在迁移、尚未清理的旧副本 — 它仍然会被编译(属于 package models),但已经没有任何代码调用 models.GetDB/InitDB/Shutdown。新增数据库代码请写到 repository,不要写到 models。 在旧副本被删除前,修改其中一份时要保证另一份也能通过编译。

响应封装与脱敏

  • 所有 API 响应都是 { code, message, data }。code == 0 表示成功;前端 (frontend/src/api/http.ts)在 code !== 0 时抛错。utils.Success/Error/... 负责构造。
  • 永远不要直接返回原始 model。 必须用 models.SanitizedJSON(...) 包装。各 model 实现 Sanitized() 方法(models/sanitizer.go),用于剔除敏感字段(密码、kubeconfig、私钥、token)。 新增一个 model = 新增一个 Sanitized() 方法 + 在 SanitizedJSON 中补一个 case(单对象 + 切片各一个)。

凭证安全(utils/crypto.go)

  • 密码用 Argon2 哈希。
  • 凭证(kubeconfig、SSH 密码/密钥、存储密钥)用 AES-GCM 加密;AES 密钥由 ENCRYPTION_KEY 经 HKDF 派生 得到(原始密钥永远不会被直接使用)。
  • ENCRYPTION_KEY 必须 ≥ 32 字节。(较早的文档/脚本写的是 16 — 现在这样会导致启动失败。)

前端(frontend/,React 19 + Vite 7 + TypeScript)

  • 技术栈:Tailwind、shadcn/ui(Radix 基础组件在 src/components/ui/)、react-router 7、 react-hook-form + zod、recharts、sonner(通知)。
  • src/api/ 是 当前在用 的 API 客户端(取代了已删除的 src/srv/api/)。http.ts 是 fetch 封装:注入 Authorization: Bearer、30s 超时、401 → 清除 token 并跳转 /login、可选重试。 每个资源一个模块(auth、cluster ……)。
  • src/App.tsx — 路由通过 React.lazy 懒加载,RequireAuth 守卫在 localStorage 无 token 时重定向到 /login。@ → src/ 别名(tsconfig + vite)。
  • 开发服务器跑在 :3000,把 /api 代理到 :8080;生产环境下由 Go 二进制直接提供 dist/ 静态服务。

Loki 部署相关细节

  • 合法的部署模式:k8s-helm、k8s-yaml、host-binary。K8s 模式需要一个 Cluster(kubeconfig); host-binary 需要一个 SSH Host,会安装 Loki + systemd。
  • Loki 版本会对照 services.LokiVersionList 校验(最低支持主版本:2) — 见 services/deployment.go 中的 ValidateLokiVersion。
  • kubectl/helm 在 Docker 镜像中安装时做了 SHA256 校验(见 Dockerfile)。