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.
10 KiB
AGENTS.md
本文件为各类 AI 编码代理(Cursor、Aider、Claude Code、Copilot 等)在本仓库中工作时的通用指引。
内容与 CLAUDE.md 保持同步;两份文件描述的是同一套约定,修改其一请同步另一份。
项目概览
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()会通过一个 单一的、串行化的saveWorkergoroutine 调度一次 防抖(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 端点聚合各资源状态),四步:环境与 目标 → 存储 → Loki→ 采集;k8s 资源统一默认log命名空间(可改)。 Grafana 为可选(默认关闭):Grafana 自动部署已移除,平台内置日志查询。 - LDAP 接入(参照 opsx-web-lite):设置页配置(SystemSetting 表 JSON,Bind 密码 AES 加密、
对外只回
hasPassword);启用后 ①登录支持 LDAP 用户验证并自动建号(authSource=ldap)②用户页 出现「从 LDAP 导入」(已导入置灰;本地同名转 LDAP);/settings/ldap/{test,users}端点。 - 日志查询(
/logs):数据源LogSources(cluster-svc=经 client-go 认证连接 API Server Service Proxy 查集群内 Loki,不依赖 ClusterIP/DNS;url=直连;部署成功自动注册);/logs/{query,labels,label/*/values,histogram}代理端点(cursor 翻页);前端 LogQL 高亮补全 + 直方图 + 标签点击过滤 + Live Tail(3s 轮询)。 - 用户管理页(
/users,metaadminOnly: true— 菜单按角色过滤,后端 AdminAuth 兜底); 部署表单的 Loki 版本下拉来自GET /deployments/versions(失败回退兜底列表)。 - 列表数据一律
useListQuery(AbortController 取消 + 版本号防错序 + 分页);反馈分级: 成功静默(禁 toast.success)、失败 toast.error、危险操作 ConfirmDialog、加载失败渲染 ErrorState + 重试(与空态严格区分)。 - 主题:语义 Token 单源(
src/index.css,品牌色 =--primary,状态色 = success/warning/destructive/info);暗色.darkclass 由 next-themes 驱动 (keylokideploy.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。