
数据库客户端后端前端CLI桌面应用数据可视化AI 应用【免费下载链接】whodbWhere data access meets operational intelligence项目地址https://gitcode.com/gh_mirrors/wh/whodb点击查看免费下载本篇技术指南围绕开源仓库 whodb核心定位Where data access meets operational intelligence的 Go 后端开发规范.agents/rules/go-backend.md展开覆盖变更后验证流程、SQL 注入防护、插件扩展模式与代码风格四大部分。读者将掌握 whodb 后端core/与cli/两个 Go 模块的实际开发约定如何使用plugins.WithConnection管理数据库连接生命周期、如何通过sqlident安全渲染表名与列名、如何基于GormPlugin扩展新数据库类型以及env/envconfig包的分工边界并能在源码与测试用例中得到逐条印证。规范适用范围与验证流程规范文件 .agents/rules/go-backend.md 通过 frontmatter 中的paths字段将约束范围限定为paths: - core/**/*.go - cli/**/*.go即所有改动发生在core服务端引擎、插件、BAML 客户端或cli命令行工具、TUI两个 Go 模块内时均需遵循本文所述规则。变更后的强制验证命令规范要求每次改动后执行cd core ./lint.sh go build ./cmd/whodb这条命令串联了两层检查./lint.sh这是 core/lint.sh 提供的 golangci-lint 包装脚本。它使用golangci-lint custom构建一个名为whodb-custom-gcl的自定义二进制缓存于$TMPDIR默认/tmp首次运行会自动构建之后复用无参数时默认执行run --config .golangci.yml ./...也可用./lint.sh run ./...、./lint.sh fmt ./...等透传形式指定子命令。它内置了 gounslop 模块插件用于约束 Go 代码中不必要的interface{}等写法与下文any优先的规范直接呼应。go build ./cmd/whodb编译 core/cmd/whodb/main.go 生成服务端可执行文件确保新增插件包、blank import 等改动没有破坏编译。规范同时要求测试后删除构建二进制Delete build binaries after testing避免把编译产物遗留进工作区。SQL 安全三条不可妥协的底线whodb 后端需要同时对接 PostgreSQL、MySQL、SQLite、ClickHouse、MongoDB、Redis 等十余种数据源SQL 构造路径是安全审查的重点。规范给出三条底线。1. 禁止字符串拼接用户输入一律参数化规范原文Neverfmt.Sprintfwith user input — usedb.Raw(... WHERE x ?, val)or GORM builder在 core/src/plugins/gorm/plugin.go 的 WHERE 条件处理中可以看到这一原则的完整落地applyAtomicCondition先通过GetSupportedOperators()校验操作符白名单再对列名调用QuoteIdentifier而用户提供的值全部经ConvertStringValue类型转换后以?占位符传入例如query.Where(col operator ?, value) // 单值比较 query.Where(col IN ?, vals) // IN 列表 query.Where(col BETWEEN ? AND ?, v1, v2) // 区间而原始 SQL 执行入口 ExecuteRawSQL 同样使用db.Raw(query, params...).Rows()传参并保留codeql[go/sql-injection]注释说明该入口本身是为查询编辑器/导入流程提供的用户自写 SQL 通道。2. 标识符必须经sqlident渲染而不是字符串拼接这是最容易踩坑的一类注入表名、列名无法作为绑定参数传入只能作为 SQL 文本。规范明确For identifiers, configureGormPlugin.ConfigureIdentifierQuoting(...)and render raw schema/table/column components throughcore/src/sqlident; never concatenate an identifier or rely onStorageUnitExistsas the SQL safety boundaryStorageUnitExists只做存在性探测见 postgres.go 中的SELECT to_regclass($1 || . || $2) IS NOT NULL它返回布尔值不是后续语句的安全防线真正防注入的是标识符渲染本身。sqlident/name.go 是这一机制的实现核心QuoteStyle枚举了五种方言分隔符DoubleQuote双引号、Backtick反引号、Bracket方括号以及对应的DoubleQuoteStrict/BacktickStrict严格模式拒绝内含分隔符的标识符Unknown表示未配置New(namespace, object)在构造阶段即拒绝空 object 名与包含 NUL 字符的输入返回ErrInvalidIdentifierSQL(style)将命名空间与对象名拆开、各自独立加引号并对闭合分隔符做加倍转义——例如双引号风格下ab被渲染为ab见 quote 函数IsSimple用于判断标识符是否仅含字母、数字、_、$从而决定能否直接走元数据 API否则需要连接器原生查询见测试 name_test.go。单元测试 TestNameSQLQuotesComponentsIndependently 给出了最直观的安全语义New(tenant.with.dot, orders; DROP TABLE secrets;--)经SQL(DoubleQuote)渲染为tenant.with.dot.orders; DROP TABLE secrets;--恶意内容被整体关进引号成为表名的一部分而非可执行 SQL。SQL builder 的 QualifiedTableName 正是通过sqlident.Newname.SQL(style)生成带引号的完整表名再以db.Table(?, clause.Expr{SQL: fullTableName})交给 GORM实现引用安全、解析安全。3. 所有*sql.Rows一律defer rows.Close()扫描游标是资源泄漏高发点。规范要求Always close*sql.Rowswithdefer rows.Close()这在GormPlugin的各处实现中都有体现例如 GetStorageUnitsrows, err : db.Raw(p.GetTableInfoQuery(), schema).Rows() if err ! nil { ... } defer func() { _ rows.Close() }()同时在rows.Next()循环结束后还检查rows.Err()确保迭代过程本身没有出错。ExecuteRawSQL、executeReadOnly、QueryForeignKeyRelationships等所有游标路径均遵循同一模式。插件开发模式基于 GormPlugin 的分层扩展whodb 的数据库能力全部以插件形式存在规范定义了一套统一的开发范式。插件注册表在 core/src/engine/registry.go// RegisterPlugin adds a plugin to the global registry. // Called from init() in each plugin package. func RegisterPlugin(p *Plugin) { globalPlugins append(globalPlugins, p) }统一入口plugins.WithConnection规范要求所有数据库操作都经由Useplugins.WithConnection(config, p.DB, func(db *gorm.DB) (...) { ... })for all DB operationsconnection_pool.go 中的WithConnection[T any]是连接生命周期管理的中枢其决策逻辑为若config.Transaction已携带事务型*gorm.DB则直接在该事务上下文上执行operation(tx.WithContext(...))并拒绝 ReadOnly 请求加入既有事务若config.MultiStatement多语句脚本或config.ReadOnly受保护读则创建独占连接、用完即关defer closeGormDB(db)避免脏会话状态污染连接池其余常规操作走getOrCreateConnection缓存复用防止连接耗尽。配套的 ConfigureConnectionPool 给出默认池参数MaxOpenConns10、MaxIdleConns5、ConnMaxLifetime30m、ConnMaxIdleTime5m注释中解释了与 MySQLwait_timeout约 8h等服务端空闲超时的匹配关系。扩展 SQL 数据库继承GormPlugin只覆盖差异规范原文ExtendGormPluginfor SQL databases — override only what differsGormPlugin 结构体组合了engine.Plugin与GormPluginFunctions接口后者将实现拆成三类职责由 GormPlugin 提供通用实现的连接解析、值类型转换、ConvertRawToRows、SQL builder 创建、排序、外键/计算列查询等由具体数据库覆盖的DB(config)打开连接的 Dialector、GetPlaceholder(index)占位符PostgreSQL 为$nMySQL 为?、各系统目录查询语句GetTableInfoQuery、GetStorageUnitExistsQuery、GetAllSchemasQuery、FormTableName、支持的操作符集合等可选的方言钩子IsArrayTypePostgres 数组_int4、IsGeometryType、HandleCustomDataType、GetColumnCodec、GetMaxBulkInsertParameters默认 65535SQLite 覆盖为 999等。以 PostgreSQL 插件 postgres.go 为例注册与差异覆盖一目了然func init() { engine.RegisterPlugin(NewPostgresPlugin()) } func NewPostgresPlugin() *engine.Plugin { plugin : PostgresPlugin{} plugin.ConfigureIdentifierQuoting(sqlident.DoubleQuote) // PG 用双引号 plugin.Type engine.DatabaseType_Postgres plugin.PluginFunctions plugin plugin.GormPluginFunctions plugin return plugin.Plugin }GetSupportedOperators直接引用sourcecatalogspecs.PostgreSQLSupportedOperatorsGetLastInsertID使用SELECT lastval()GetPlaceholder返回$%d——即只覆盖 PostgreSQL 与通用实现不同的部分。MySQL 插件则体现引用风格随方言切换MySQL/MariaDB/TiDB 三个插件在构造时均调用ConfigureIdentifierQuoting(sqlident.Backtick)见 mysql.goSQLite 与 PostgreSQL 一样使用双引号见 sqlite3.go。请求作用域上下文绝不使用context.Background()规范原文Useconfig.OperationContext()orconfig.OperationContextWithTimeout(...)for request-scoped work — nevercontext.Background()for user requestsengine/plugin.go 提供了两个方法func (c *PluginConfig) OperationContext() context.Context { if c ! nil c.Context ! nil { return c.Context } return context.Background() } func (c *PluginConfig) OperationContextWithTimeout(timeout time.Duration) (context.Context, context.CancelFunc) { return context.WithTimeout(c.OperationContext(), timeout) }OperationContext()会优先采用请求传入的PluginConfig.Context例如 HTTP 请求上下文便于取消与超时传播仅在未配置时退化为context.Background()OperationContextWithTimeout则在此基础上叠加超时。在WithConnection内部所有 GORM 操作都通过db.WithContext(config.OperationContext())挂载上下文。规范中的never针对的是用户请求路径——显式使用context.Background()意味着取消信号、超时与链路追踪全部失效。init() 自注册 入口点 blank import规范原文Self-register ininit()viaengine.RegisterPlugin(...)— add blank import in entry point每个插件包在init()中调用engine.RegisterPlugin完成自注册postgres、mysql、sqlite3等均如此而服务端入口 core/cmd/whodb/main.go 通过一组 blank import 精确控制哪些插件被编译进来// CE plugins — each registers itself via init() _ github.com/clidey/whodb/core/src/plugins/clickhouse _ github.com/clidey/whodb/core/src/plugins/elasticsearch _ github.com/clidey/whodb/core/src/plugins/memcached _ github.com/clidey/whodb/core/src/plugins/mongodb _ github.com/clidey/whodb/core/src/plugins/mysql _ github.com/clidey/whodb/core/src/plugins/postgres _ github.com/clidey/whodb/core/src/plugins/redis _ github.com/clidey/whodb/core/src/plugins/sqlite3这一设计使引擎层engine.go保持纯接口思维Choose(databaseType)通过resolvePluginType先查RegisterPluginTypeAlias注册的别名映射如 ElastiCache→Redis、DocumentDB→MongoDB再按类型匹配返回插件EE 版可以注册新数据库类型而不必改动 CE 代码。需要说明的是非 SQL 数据源MongoDB、Redis、ElasticSearch 等并不继承GormPlugin而是嵌入 BasePlugin编译期断言其满足PluginFunctions并只覆盖自己支持的操作不支持的接口默认返回errors.ErrUnsupported。代码风格约束any而非interface{}规范要求统一使用any。这一点在 BasePlugin.FormatValuefunc (b *BasePlugin) FormatValue(val any) string、PluginConfig.Transaction any、DBOperation泛型等新代码中已全面落地同时lint.sh内置的 gounslop 插件会在 CI/本地 lint 阶段拦截遗留的interface{}写法。env与envconfig的包边界规范原文envpackage: pure declarations only (nologimport). Parsing error reporting →envconfig这是为了打破包循环依赖而刻意设计的边界。core/src/envconfig/envconfig.go的包注释明确写道Package envconfig provides configuration-loading functions that depend on both the env and log packages. It exists to break the circular dependency that would occur if env imported log directly.即 core/src/env 下的env.go、aws.go、azure.go、gcp.go只承载纯声明与默认值如各OpenAIAPIKey、AWSProviderEnvConfig等不 importlog而 JSON 解析、缺省值填充、错误上报等行为全部收敛到envconfig例如GetDefaultDatabaseCredentials读取WHODB_TYPEJSON 数组并回退到编号变量WHODB_TYPE_1、WHODB_TYPE_2……ParseGenericProviders解析WHODB_AI_GENERIC_ID_*系列变量并在缺BASE_URL/MODELS时记录告警。写新配置项时声明放env解析与报错放envconfig。日志脱敏规范最后一条Never log passwords, API keys, tokens, or connection strings数据库凭据engine.Credentials中的Password、AccessToken以及各 AI Provider 的APIKey都属敏感信息不得写入日志。这也是WithConnection失败日志只输出conn_id与错误信息、而不打印完整连接串的原因实践中应使用log.WithError/log.WithFields携带结构化字段并确保字段值本身已脱敏。小结.agents/rules/go-backend.md用不足 40 行浓缩了 whodb Go 后端开发的工程底线安全上坚持参数绑定 sqlident渲染 游标关闭三件套架构上坚持WithConnection统一连接管理、GormPlugin分层扩展、OperationContext传递请求作用域、init自注册 入口 blank import四步范式风格上坚持any优先、env/envconfig分层、日志脱敏。新接入的数据库类型、CRUD 接口或配置项都可以对照这份规范在 core/src/plugins、core/src/sqlident 与 core/src/engine 中找到现成范本与测试用例作为参照。赞分享数据库客户端后端前端CLI桌面应用数据可视化AI 应用【免费下载链接】whodbWhere data access meets operational intelligence项目地址https://gitcode.com/gh_mirrors/wh/whodb点击查看免费下载相关推荐Arcane 开发指南全解Go 后端、SvelteKit 前端与多 Agent 模式的架构规范与工程实践Arcane 开发指南全解Go 后端、SvelteKit 前端与多 Agent 模式的架构规范与工程实践 这篇技术指南以仓库根目录的 AGENTS.md ht云原生运维容器运行时DeerFlow实战跑通一个数据任务看懂SuperAgent的内部走位DeerFlow实战跑通一个数据任务看懂SuperAgent的内部走位 把一份泰坦尼克号乘客 CSV 交给 DeerFlow二十分钟后它交回 5 张统计图人工智能大模型AI Agent自主智能体工具调用MCP 服务Agent 沙箱AI 技能后端前端Databasus 后端开发规范详解Go Gin GORM PostgreSQL 的工程实践Databasus 后端开发规范详解Go Gin GORM PostgreSQL 的工程实践 本文系统讲解 Databasus 后端Go G数据库灾备上一篇Farrow-React集成指南构建同构应用的最佳实践下一篇MCP协议实战教程使用gh_mirrors/server208/server构建实时AI交互应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考