mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-04 21:07:45 +08:00
- 新增脱敏审计存储、筛选、保留策略、完整性校验与 JSON/CSV 导出 - 覆盖查询编辑器、事务、导入同步、对象操作、AI/MCP 与 Web 运行时入口 - 完善事务完整语句日志、Windows 快捷键映射及 SQL 分析布局 - 补充多语言、健康状态、数据目录迁移和回归测试
141 lines
4.0 KiB
Go
141 lines
4.0 KiB
Go
package app
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"GoNavi-Wails/internal/connection"
|
|
)
|
|
|
|
type sqlAuditUserActionOptions struct {
|
|
StatementCount *int
|
|
SafeError *string
|
|
}
|
|
|
|
// beginSQLAuditUserAction returns a deferred recorder for exported user-facing
|
|
// write helpers that already own execution, validation, and dialect handling.
|
|
// The supplied text is an operation descriptor or generated statement and must
|
|
// never contain row values or message payloads.
|
|
func (a *App) beginSQLAuditUserAction(
|
|
config connection.ConnectionConfig,
|
|
dbName string,
|
|
source string,
|
|
sqlText *string,
|
|
result *connection.QueryResult,
|
|
statementCount ...*int,
|
|
) func() {
|
|
options := sqlAuditUserActionOptions{}
|
|
if len(statementCount) > 0 {
|
|
options.StatementCount = statementCount[0]
|
|
}
|
|
return a.beginSQLAuditUserActionWithOptions(config, dbName, source, sqlText, result, options)
|
|
}
|
|
|
|
func (a *App) beginSQLAuditUserActionWithOptions(
|
|
config connection.ConnectionConfig,
|
|
dbName string,
|
|
source string,
|
|
sqlText *string,
|
|
result *connection.QueryResult,
|
|
options sqlAuditUserActionOptions,
|
|
) func() {
|
|
startedAt := time.Now()
|
|
queryID := generateQueryID()
|
|
runConfig := normalizeRunConfig(config, dbName)
|
|
return func() {
|
|
if result == nil {
|
|
return
|
|
}
|
|
statement := ""
|
|
if sqlText != nil {
|
|
statement = strings.TrimSpace(*sqlText)
|
|
}
|
|
resolvedStatementCount := 0
|
|
if options.StatementCount != nil {
|
|
resolvedStatementCount = *options.StatementCount
|
|
}
|
|
auditResult := *result
|
|
if !auditResult.Success && options.SafeError != nil {
|
|
auditResult.Message = strings.TrimSpace(*options.SafeError)
|
|
}
|
|
a.recordSQLAuditQuery(sqlAuditQueryInput{
|
|
Config: runConfig,
|
|
Database: dbName,
|
|
DBType: resolveDDLDBType(runConfig),
|
|
QueryID: queryID,
|
|
SQL: statement,
|
|
Source: source,
|
|
CommitMode: "auto",
|
|
Duration: time.Since(startedAt),
|
|
StatementCount: resolvedStatementCount,
|
|
Result: auditResult,
|
|
})
|
|
}
|
|
}
|
|
|
|
// DBQueryAudited executes one explicit application-level user action and writes
|
|
// exactly one SQL audit event without adding it to slow-query history.
|
|
func (a *App) DBQueryAudited(
|
|
config connection.ConnectionConfig,
|
|
dbName string,
|
|
query string,
|
|
source string,
|
|
) connection.QueryResult {
|
|
queryID := generateQueryID()
|
|
return a.dbQueryWithCancel(config, dbName, query, queryID, dbQueryAuditOptions{
|
|
auditAll: true,
|
|
auditWrites: true,
|
|
source: normalizeSQLAuditUserActionSource(source),
|
|
})
|
|
}
|
|
|
|
// DBQueryAI is the dedicated entry point used by GoNavi's built-in AI tool
|
|
// runtime. The audit source describes this called entry point; it is not an
|
|
// unforgeable actor identity or security provenance claim.
|
|
func (a *App) DBQueryAI(
|
|
config connection.ConnectionConfig,
|
|
dbName string,
|
|
query string,
|
|
) connection.QueryResult {
|
|
return a.dbQueryWithCancel(config, dbName, query, "", dbQueryAuditOptions{
|
|
auditAll: true,
|
|
auditWrites: true,
|
|
source: "ai_action",
|
|
})
|
|
}
|
|
|
|
// MCPQueryExecutor is a narrow adapter for the MCP server. Keeping it separate
|
|
// from App's Wails-bound method set makes the mcp source a backend-owned fact
|
|
// rather than a source string accepted from a browser or desktop caller.
|
|
type MCPQueryExecutor struct {
|
|
app *App
|
|
}
|
|
|
|
func NewMCPQueryExecutor(app *App) *MCPQueryExecutor {
|
|
return &MCPQueryExecutor{app: app}
|
|
}
|
|
|
|
func (executor *MCPQueryExecutor) DBQueryMulti(
|
|
config connection.ConnectionConfig,
|
|
dbName string,
|
|
query string,
|
|
) connection.QueryResult {
|
|
if executor == nil || executor.app == nil {
|
|
return connection.QueryResult{Success: false, Message: "MCP query executor is unavailable"}
|
|
}
|
|
return executor.app.dbQueryMulti(config, dbName, query, "", dbQueryMultiAuditOptions{
|
|
auditAll: true,
|
|
auditWrites: true,
|
|
source: "mcp",
|
|
})
|
|
}
|
|
|
|
func normalizeSQLAuditUserActionSource(source string) string {
|
|
switch normalized := strings.ToLower(strings.TrimSpace(source)); normalized {
|
|
case "data_editor", "table_designer", "object_editor", "message_publish":
|
|
return normalized
|
|
default:
|
|
return "application_api"
|
|
}
|
|
}
|