* feat(heicode): 客户端错误遥测上报端点(默认关闭)(#24) 按客户端 winos#23 契约 + 权威 schema 实现,结合 HM 入库逻辑: - POST /api/heicode/telemetry/events,挂 UserOrV2DeviceAuth(设备配对鉴权)。 - 接收顶层 JSON 数组(非包裹),批量 1-20、<=256KB;校验 body client_id 等于已验签 设备(X-Heicode-Device-Id),不一致 403;无设备身份拒绝。 - 真实 4xx/5xx 码(400 非数组、413 超限、403 设备、410 关闭),让客户端"4xx 丢弃" 语义生效;2xx 返回 {accepted:n}。 - 独立表 telemetry_events,与计费完全隔离:不写 consume log、不碰 quota。 - 宽松入库(最大化采集):未知枚举 / 哨兵 app_version(0.0.0-heicode-local)/ 缺字段 原样入库;schema_version 缺省 1;stack_top/context 存 TEXT(JSON);记 user_id 作 device 到 account 关联 + 服务端 received_at。 - 默认 HEICODE_TELEMETRY_ENABLED=false 时返回 410(kill switch);隐私政策更新 + 端点下发形态确认前不开启外发。 测试用客户端仿真夹具:parseTelemetryBatch / toModel 映射与默认 / 拒绝非数组,全过。 Refs #24(上线门槛:隐私政策 §2 如实披露 + 下发形态 + 去重;见工单评论) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(heicode): GET /api/heicode/config 下发 telemetry 配置/kill-switch (#24) 按客户端 #24 拍板:选独立 config 端点(而非塞登录响应),便于 kill switch 在 会话内传导、不依赖重登录。返回 telemetry 块 {enabled, endpoint, max_batch, flush_interval_sec};enabled 取 HEICODE_TELEMETRY_ENABLED(默认 false)。 未鉴权全局只读(同 capabilities 姿态)。 测试 heicode_config_test.go:enabled 反映 env、endpoint 与摄入路由一致、缺省 false。 Refs #24 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(telemetry): 服务端二次脱敏 stack_top/context + 明确生产门槛 (#24 review) 按 Fasthei 复审意见补隐私门槛: - 服务端纵深防御脱敏:新增导出 model.RedactText(包装已上线的 redactAuditSecrets, #11),在 telemetry 入库前对 stack_top / context 再脱敏一遍(sk-/Bearer/URL token/JSON 密钥字段),即便客户端漏脱敏也不会把明文密钥落库。 - 测试 TestTelemetryToModel_RedactsSecrets:stack_top 里的 sk-、context 里的 Bearer token 被打码,非密钥内容保留。 - 端点默认 HEICODE_TELEMETRY_ENABLED=false,关时 410;隐私政策披露完成前生产 不得开启外发(见 #24 评论记录产品/法务状态)。 go build / vet 干净;controller 测试通过。 Refs #24 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: chenchen <chenchen@xinghanlab.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
38 lines
1.2 KiB
Go
38 lines
1.2 KiB
Go
package controller
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// /api/heicode/config carries the telemetry kill-switch block (#24). enabled
|
|
// must reflect HEICODE_TELEMETRY_ENABLED and the endpoint must match the ingest
|
|
// route so the client polls the right place.
|
|
func TestHeicodeConfig_TelemetryBlock(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
t.Setenv("HEICODE_TELEMETRY_ENABLED", "true")
|
|
rec := httptest.NewRecorder()
|
|
ctx, _ := gin.CreateTestContext(rec)
|
|
ctx.Request = httptest.NewRequest(http.MethodGet, "/api/heicode/config", nil)
|
|
HeicodeConfig(ctx)
|
|
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
|
body := rec.Body.String()
|
|
require.Contains(t, body, `"telemetry"`)
|
|
require.Contains(t, body, `"endpoint":"/api/heicode/telemetry/events"`)
|
|
require.Contains(t, body, `"enabled":true`)
|
|
|
|
// default OFF when the flag is unset
|
|
t.Setenv("HEICODE_TELEMETRY_ENABLED", "")
|
|
rec2 := httptest.NewRecorder()
|
|
ctx2, _ := gin.CreateTestContext(rec2)
|
|
ctx2.Request = httptest.NewRequest(http.MethodGet, "/api/heicode/config", nil)
|
|
HeicodeConfig(ctx2)
|
|
require.Contains(t, rec2.Body.String(), `"enabled":false`)
|
|
}
|