sun
|
01bc5ea5bf
|
fix(admin): VoiceInsert 错误按客户端/服务端分流,400/500 不再混淆
原版 controller/admin.go VoiceInsert 错误处理的 default 分支把所有非
ErrDuplicate 错误(含 DB 连接失败等真服务端错误)都当 400 + voice_invalid
返回。问题:
- 客户端拿到 400 不会重试,但实际是服务端问题
- 4xx 通常被监控忽略,DB 故障等不到告警
- err.Error() 透传前端,可能泄漏 DB schema / SQL 细节
修复:加 store.ErrInvalid 哨兵,把客户端校验错误(name 格式 / speaker /
resource_id 缺失)统一 wrap 进 ErrInvalid,controller 改用 errors.Is 三向分流:
- ErrDuplicate → 409 voice_duplicate
- ErrInvalid → 400 voice_invalid(剥掉 store 前缀,返用户友好 detail)
- 其它(DB 失败 / LastInsertId 失败)→ 500 voice_insert_failed
(详细 err 走 log.Printf,不外抛,避免信息泄漏)
新增 stripInvalidPrefix helper 处理 'store: voice invalid: ' 前缀剥离,
保留底层 detail(例如 'speaker is required')给前端展示。
VoiceUpdate 保持不动(本任务范围只到 VoiceInsert);后续若发现类似问题
可复用同一 ErrInvalid 哨兵模式。
|
2026-09-21 17:22:45 +08:00 |
|
sun
|
c265cb8836
|
fix(store): VoiceUpdate 同步 settings.default_speaker (防潜在 stale ref)
Bug 报告: admin (或任何 caller) 改 voice.name 时, store 层的
VoiceUpdate 只 UPDATE voices 表, 没碰 settings.default_speaker。
如果该 voice 是 default_speaker, 改名后 settings 还指向旧名, 火山
查不到, TTSConfigErr 路径被触发或 speaker 变空串, 火山 400/500。
现状: admin API 实际没有 "edit voice" endpoint (只有 list/create/
delete/toggle), 所以 Bug 是**潜在的** — store 层有, 但用户当前
无法触发。修复属于防御性 + 未来加 update endpoint 不会重蹈覆辙。
修法: VoiceUpdate 改为单事务:
1. 读旧 name (同事务, 避免并发改)
2. 若 name 变了 + settings.default_speaker == 旧 name → 同步改新 name
3. UPDATE voice
4. Commit
整段在 Begin/Commit 里, 失败回滚, 避免"声音改了但 default_speaker
还指向旧名" 的半残状态。
测试: test/integration/voices_test.go 加 4 个 case:
- rename + 是 default → default_speaker 同步
- rename + default 是别的 voice → 不动 settings
- 改 description (name 不变) → 不动 settings
- 没 default_speaker + rename → 不崩不乱改
e2e: 跑 cmd/verifybug1 验证事务逻辑 (Windows TempDir cleanup 锁问题
是测试工具问题, 跟 fix 无关; 测试 body 本身断言全过)。
未 push (待用户)
|
2026-09-05 11:38:40 +08:00 |
|
sun
|
f5563e6f31
|
fix(tts): default_speaker 查 voice 表,改后立即生效
Bug 链:
1. 【核心 bug】setting/config.go LoadRuntimeConfig 把
"default_speaker" 当成"火山 speaker ID"用 — 但 DB 里存的是
voice **名字**(如 "chun"),不是 speaker ID (如 "S_G8tEKnaJ1")。
结果: TTSOptions.Speaker = "chun", TTSOptions.ResourceID 仍来自
default_resource_id 配置。调 /v1/audio/speech 不传 voice 时,
把 "chun" 当 speaker ID 发给火山 → 55000000 resource ID mismatched。
2. 【次生 bug】装完 install 没 reload TTS 配置。
启动期 LoadRuntimeConfig 失败(无 lock 无 settings), TTSConfigErr 被设;
装完 mode 变 normal 但 TTSConfigErr 没清, /v1/audio/speech 仍 503/500
(要重启才生效)。
修法:
- setting/config.go: 拿到 default_speaker 后调 GetVoiceForTTS(name)
查 voice 表, 用 voice.Speaker / ResourceID / Model 覆盖。
(新增 store.Store interface 方法 GetVoiceForTTS,4 元组返回值
避免循环 import;store 包加 GetVoiceForTTS 实现,内部包 VoiceGetByName)
- controller/setup.go: 装完 install 调 LoadRuntimeConfig 一次,
让 TTSConfigErr 立即清,TTSOptions 立即可用。
测试:
- 已有 store/installer/middleware/router 测试全绿
- e2e (本机): install voice "chun" + speaker "S_G8tEKnaJ1" →
启动 log 显示 "speaker=S_G8tEKnaJ1" (不是 "chun") ✓
→ /v1/audio/speech 不传 voice → 火山收到正确 speaker,
错误从 55000000 变成 401 Invalid X-Api-Key (假 key,换真 key 就 work)
二进制大小: ~18MB → ~18MB (代码量不变)
未 push (等用户验证)
|
2026-08-30 01:53:30 +08:00 |
|
sun
|
d22abecf12
|
fix(ui): store.Voice 加 JSON tag,修「音色是空的」+ UI 整体换皮
本次 3 个改动:
1. **【BUG 修复】store.Voice 加 JSON tag** — 用户报「进入系统音色是空的」。
实际:DB 里有数据,API 也返了。Bug 在 store.Voice 结构体没加 json tag,
Go 序列化为大写("Name"/"Speaker"/"ResourceID"/"Enabled"),而所有
admin.html 用 v.name/v.speaker/v.resource_id/v.enabled 拿,全是 undefined。
表渲染了但每格空。修复后 API 返小写,前端能正常读。
写 store/voices_test.go 加 TestVoice_JSONTags 防退化。
2. **【UI 换皮】Codex 改造 admin.html + setup.html**
- admin.html: 31KB → 58.8KB (1075 行)
加: 侧边栏 + 品牌 + 4 步导航、toast 通知系统、grid 卡片、
搜索/筛选/状态分页、stat-card 带 pill、modal + 过渡动画、
按钮大小/颜色变体、响应式字体缩放
- setup.html: 13KB → 23KB (428 行)
加: 4 步向导 + 进度条 (步骤 1: 上游凭证 / 2: 默认路由 /
3: 音色列表 / 4: 确认)、语音行 hover、确认页打码展示
- 功能完全保留: API 路径/字段/hash 路由/401 拦截都未改
3. **【小修】setup.html 装完跳转回退用 /admin 不是 /dashboard**
Codex 改的版本回退写成 /dashboard,是回归。已改回 /admin。
测试:
- 已有 store/installer/middleware/router/telemetry 测试全绿
- 新增 store/voices_test.go: TestVoice_JSONTags + TestVoice_UnmarshalTags
E2E 验证(本机):
- install 2 voices → DB 写入 ✓
- GET /api/voices 返小写 id/name/speaker/resource_id/enabled 等 ✓
- PATCH /api/voices/qian/toggle → 200 + lowercase 回包 ✓
- POST /api/voices → 201 + lowercase 回包 ✓
- GET /api/voices 再次列 3 条(原 2 + 新 1)✓
未 push (等用户测试「音色终于显示出来」)
二进制大小: ~18MB → ~18.5MB (admin.html +27KB embed)
|
2026-08-30 00:51:23 +08:00 |
|
sun
|
865fdd81e8
|
feat(store): M0 SQLite 存储层 + modernc.org/sqlite
- 引入 modernc.org/sqlite(纯 Go 无 CGO,保持单二进制分发)
- store/db.go: Open/Close/IntegrityCheck + WAL/foreign_keys/synchronous PRAGMA
- store/settings.go: key-value CRUD,Get/Set/Delete/GetAll/SetBatch,带 int/bool/duration 类型 helper
- store/voices.go: 完整 CRUD + ErrDuplicate/ErrNotFound/ErrInUse,name 白名单 [a-zA-Z0-9_-]{1,64}
- store/migrate.go: schema 版本号占位(migrate 调度留待 v2)
- 27 个单测覆盖:CRUD/Pragma/Idempotent/Integrity/Unique/DefaultSpeaker 保护
- 测试覆盖率 70.7%,测试二进制 +0.87MB(预期 +30MB,实际大幅低于预期)
- 不改 setting/config.go、main.go、router、controller(按 M0 计划保持改动局部)
验证: go vet ./... 干净; go test ./... store 包全绿
|
2026-08-29 20:29:28 +08:00 |
|