feat: harden loose-offline sync for user JWT, schema, and console ops

Enable Binding-scoped agent push/pull, empty-table schema ensure, SyncPage inspect/drop-table, default module import, and agent-bound publish docs from the 宇恒联调意见.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-08-05 09:47:35 +08:00
parent 76cdcd760e
commit b04b180d30
59 changed files with 4762 additions and 308 deletions

View File

@@ -19,7 +19,7 @@ func Open(driver Driver, dsn string) (*sql.DB, error) {
if dsn == "" {
return nil, fmt.Errorf("sqlite dsn required")
}
// modernc.org/sqlite 注册名为 sqlite
dsn = normalizeSQLiteDSN(dsn)
case DriverMySQL:
name = "mysql"
case DriverPostgres:
@@ -31,8 +31,14 @@ func Open(driver Driver, dsn string) (*sql.DB, error) {
if err != nil {
return nil, err
}
db.SetMaxOpenConns(5)
db.SetMaxIdleConns(2)
if driver == DriverSQLite {
// SQLite 文件锁:单连接更稳,避免 Windows 上并发 open 卡死/EOF
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
} else {
db.SetMaxOpenConns(5)
db.SetMaxIdleConns(2)
}
if err := db.Ping(); err != nil {
_ = db.Close()
return nil, err
@@ -40,6 +46,33 @@ func Open(driver Driver, dsn string) (*sql.DB, error) {
return db, nil
}
// normalizeSQLiteDSNWindows 反斜杠 → 正斜杠;裸盘符路径补 file:;补 busy_timeout。
func normalizeSQLiteDSN(dsn string) string {
dsn = strings.TrimSpace(dsn)
lower := strings.ToLower(dsn)
if strings.HasPrefix(lower, "file:") {
rest := dsn[5:]
path, query, hasQ := strings.Cut(rest, "?")
path = strings.ReplaceAll(path, `\`, `/`)
if hasQ {
dsn = "file:" + path + "?" + query
} else {
dsn = "file:" + path
}
} else if strings.Contains(dsn, `\`) || (len(dsn) >= 2 && dsn[1] == ':') {
path := strings.ReplaceAll(dsn, `\`, `/`)
dsn = "file:" + path
}
if !strings.Contains(strings.ToLower(dsn), "busy_timeout") {
if strings.Contains(dsn, "?") {
dsn += "&_pragma=busy_timeout(5000)"
} else {
dsn += "?_pragma=busy_timeout(5000)"
}
}
return dsn
}
func quoteIdent(driver Driver, name string) string {
name = strings.ReplaceAll(name, "`", "")
name = strings.ReplaceAll(name, `"`, "")