Files
LokiStack/CLAUDE.md
T
logan 2d73661dd1 feat: complete Loki query, insights, settings and deployment workflows
Add the Loki query UI, access policies, ingress insights, alert and registry settings, and their backend APIs. Include the related Kubernetes deployment and collector updates required by the integrated application.

Validated with go test ./..., npm run build, and npm run lint.
2026-10-25 21:28:32 +08:00

9.4 KiB

CLAUDE.md

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

项目概览

opsx-loki 是一个基于 Gin (Go) + React/TypeScript + JSON 文件存储 的平台,用于把 Grafana Loki 日志聚合系统部署到 Kubernetes 集群(通过 client-go 直连 Kubernetes API Server)或裸金属 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/,模块路径 opsxloki,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-yaml / host-binary / host-docker)分发,Kubernetes 操作由 client-go 完成,主机操作通过 SSH。 其它 service:K8sClient、ssh(+ ssh_errors)、storage、cluster_info, 以及较新的 collector(日志采集器) 模块(collector*.go — 部署客户端日志采集器)。

  • middleware/jwt.go — JWTAuth()(解析后对照 JsonDB 校验 TokenVersion,并以数据库最新 role 为准)、AdminAuth();ratelimit.go — 登录接口 IP 滑动窗口限流(20 次/分钟)。

  • 用户管理(简单两级,无 RBAC):GET/POST /users、DELETE /users/:id、 POST /users/:id/reset-password,全部仅 admin。改密/重置密码会递增用户 TokenVersion, 旧 access+refresh token 立即失效(会话吊销)。

  • utils/ — response.go(响应封装助手)、crypto.go(Argon2 + AES-GCM)、jwt.go。

  • seed/ — 数据库为空时写入默认 admin 用户。

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

持久化是 单个 JSON 文件,位于 $DATA_DIR/opsxloki.json(兼容加载旧 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)

前端开发标准:docs/web-ui-guide/(9 章,含分章 AI 检查清单)。编码、Review、重构 时以该标准为基准;与标准冲突的既有代码即为待改进项。

  • 技术栈:Tailwind、shadcn/ui(Radix 基础组件在 src/components/ui/,只保留实际 使用的,需要新组件 npx shadcn add <name>)、react-router 7、react-hook-form + zod、 sonner(通知)、next-themes(暗色)。
  • 页面按域组织(src/pages/<domain>/):index.tsx 编排(≤400 行)+ components/(表单弹窗/卡片/详情)+ hooks/(私有数据逻辑)。跨页复用第三次才晋升 到 src/components/。
  • src/api/ — API 客户端,一域一模块。http.ts 提供:结构化 ApiError(status/code)、 30s 超时、silent 请求选项、401 单飞刷新 + 队列重放(refreshToken 走裸 fetch, 登录/轮询接口静默)、buildQuery 编码 query。组件禁止绕过 api 层直接 fetch。
  • 凭证存取统一走 src/lib/auth-storage.ts,key 带命名空间(lokideploy.auth.*); redirect 回跳校验在 src/lib/redirect.ts(防开放重定向)。
  • 路由单源:src/router/routes.tsx(meta: title/icon/order)驱动菜单、顶栏标题与 document.title;守卫 RequireAuth 带来源路径回跳;404 走 pages/not-found。
  • 部署向导 /wizard(preflight 状态推导 + Grafana 同环境部署 + 统一 log 命名空间)。
  • LDAP 接入(/settings 配置+/users 批量导入+LDAP 登录)与日志查询(/logs,数据源自动注册+kube proxy)。
  • 用户管理页(/users,meta adminOnly: true — 菜单按角色过滤,后端 AdminAuth 兜底); 部署表单的 Loki 版本下拉来自 GET /deployments/versions(失败回退兜底列表)。
  • 列表数据一律 useListQuery(AbortController 取消 + 版本号防错序 + 分页);反馈分级: 成功静默(禁 toast.success)、失败 toast.error、危险操作 ConfirmDialog、加载失败渲染 ErrorState + 重试(与空态严格区分)。
  • 列表数据一律 useListQuery(AbortController 取消 + 版本号防错序 + 分页);反馈分级: 成功静默(禁 toast.success)、失败 toast.error、危险操作 ConfirmDialog、加载失败渲染 ErrorState + 重试(与空态严格区分)。
  • 主题:语义 Token 单源(src/index.css,品牌色 = --primary,状态色 = success/warning/destructive/info);暗色 .dark class 由 next-themes 驱动 (key lokideploy.theme.mode),禁止硬编码调色板类(如 bg-emerald-600)。
  • 开发服务器跑在 :3000(strictPort),把 /api 代理到 :8080;生产环境下由 Go 二进制直接提供 dist/ 静态服务。构建指纹(__APP_VERSION__/__COMMIT_HASH__) 在 Settings「关于」展示。

Loki 部署相关细节

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