重构:拆出 internal/config 与 internal/store 子包

把工程根目录中和"配置加载"、"数据存储"两个领域相关的全部 Go 文件迁到
internal/ 下的子包,按功能分组的第一阶段成果。

internal/config/
- 从 config.go 抽出 Config / MQTTConfig / WebConfig / DatabaseConfig 等
  类型并大写导出,函数改名 Default/Load/Write/Validate/BuildTLS 等。
- 测试随被测代码迁移成 package config 的内部测试。
- 根目录留 config.go 作桥接:用 type alias 让旧的小写名(config /
  mqttConfig / databaseConfig 等)继续可用,避免修改 30+ 处调用方。

internal/store/
- 把 db.go、store_query.go、db_write_queue.go 与 13 个 *_store.go 一并
  迁入;26 个 *Record 类型与 store 同包以避免循环依赖。
- store -> Store;50+ 标识符从小写未导出改为大写导出(包括 record、
  ListOptions、错误变量、bot/llm/runtime 常量、helpers 等)。
- 新增 DB() / Driver() 访问器供 ai 子系统使用,避免直接访问私有字段。
- bot_pki_store.go 独立出来,把 PKI 解密所需的 store 方法集中归类。
- helpers.go 提供 hashPassword / uint32FromRecord / printJSON 等以前在
  其他根目录文件中的辅助;test_helpers_test.go 提供 verifyPassword
  与 publicMapTileSourceDTO 让测试可以本地运行而不依赖 main 包。

根目录新增:
- store_bridge.go:完整 type-alias / 函数包装层,把 internal/store 的
  导出名映射回旧的小写名,让 admin_*_routes.go、web.go、bot_service.go
  等仍未迁出的文件继续编译。后续步骤把它们迁到各自领域包后可逐步删除。
- test_helpers_test.go:根目录测试沿用 openTestStore 的入口。

go build ./... 与 go test ./... 全部通过;测试数量与重构前一致。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-18 13:56:36 +08:00
co-authored by Claude
parent 6f6b06e37d
commit eff4972668
32 changed files with 1759 additions and 1462 deletions
+4 -71
View File
@@ -2,17 +2,12 @@ package main
import (
"encoding/base64"
"encoding/hex"
"errors"
"strings"
"gorm.io/gorm"
)
// pkiKeyResolver mqtpp 在解密 PKI 加密包时回调的接收者私钥/发送者公钥查询函数。
//
// to 是接收者节点号(应该匹配某个本地受管的 bot),from 是发送者节点号(应该已经有 nodeinfo 上报)
// 返回的 ok=false 时调用方会跳过 PKI 路径并回落到 channel PSK 解密。
// newPKIKeyResolver 返回 mqtpp 在解密 PKI 加密包时使用的回调:根据接收者
// 节点号查找受管 bot 的私钥,并根据发送者节点号在 nodeinfo 表中查找其公钥。
// 返回 ok=false 时调用方会跳过 PKI 路径并回落到 channel PSK 解密
func newPKIKeyResolver(s *store) func(toNodeNum, fromNodeNum uint32) ([]byte, []byte, bool) {
if s == nil {
return nil
@@ -20,82 +15,20 @@ func newPKIKeyResolver(s *store) func(toNodeNum, fromNodeNum uint32) ([]byte, []
return func(toNodeNum, fromNodeNum uint32) ([]byte, []byte, bool) {
bot, err := s.GetBotNodeByNodeNum(int64(toNodeNum))
if err != nil {
printJSON(map[string]any{
"event": "pki_resolve_bot_not_found",
"to_num": toNodeNum,
"from_num": fromNodeNum,
})
return nil, nil, false
}
privateKeyB64 := strings.TrimSpace(bot.PrivateKey)
if privateKeyB64 == "" {
printJSON(map[string]any{
"event": "pki_resolve_no_private_key",
"bot_id": bot.NodeID,
"bot_num": bot.NodeNum,
"from_num": fromNodeNum,
})
return nil, nil, false
}
privateKey, err := base64.StdEncoding.DecodeString(privateKeyB64)
if err != nil || len(privateKey) != 32 {
printJSON(map[string]any{
"event": "pki_resolve_invalid_private_key",
"bot_id": bot.NodeID,
"bot_num": bot.NodeNum,
"from_num": fromNodeNum,
"error": err,
})
return nil, nil, false
}
fromPublic, ok := lookupNodeInfoPublicKey(s, fromNodeNum)
fromPublic, ok := s.LookupNodeInfoPublicKey(fromNodeNum)
if !ok {
printJSON(map[string]any{
"event": "pki_resolve_no_sender_public_key",
"bot_id": bot.NodeID,
"bot_num": bot.NodeNum,
"from_num": fromNodeNum,
})
return nil, nil, false
}
return privateKey, fromPublic, true
}
}
// lookupNodeInfoPublicKey 在 nodeinfo 表中按 node_num 查 X25519 公钥,
// 兼容 hex 与 base64 两种历史存储格式。
func lookupNodeInfoPublicKey(s *store, nodeNum uint32) ([]byte, bool) {
var row nodeInfoRecord
if err := s.db.Where("node_num = ?", int64(nodeNum)).Take(&row).Error; err != nil {
return nil, false
}
if row.PublicKey == nil {
return nil, false
}
value := strings.TrimSpace(*row.PublicKey)
if value == "" {
return nil, false
}
if decoded, err := hex.DecodeString(value); err == nil && len(decoded) == 32 {
return decoded, true
}
if decoded, err := base64.StdEncoding.DecodeString(value); err == nil && len(decoded) == 32 {
return decoded, true
}
return nil, false
}
// GetBotNodeByNodeNum 按节点号查找受管 bot 节点;用于 PKI 解密时把 to 字段映射回本地私钥。
func (s *store) GetBotNodeByNodeNum(nodeNum int64) (*botNodeRecord, error) {
if s == nil || s.db == nil {
return nil, errors.New("store not configured")
}
var row botNodeRecord
if err := s.db.Where("node_num = ?", nodeNum).Take(&row).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
return nil, err
}
return &row, nil
}