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

@@ -0,0 +1,85 @@
package dbsync
import (
"database/sql"
"fmt"
"strings"
"sync"
"time"
)
// remotePool 复用线上 A 连接,避免每次 push 冷开 SQLiteWindows 上常 >3s → 旧默认超时 503
type remotePool struct {
mu sync.Mutex
dbs map[string]*pooledDB
}
type pooledDB struct {
db *sql.DB
driver Driver
lastUsed time.Time
}
var sharedRemotePool = &remotePool{dbs: map[string]*pooledDB{}}
func poolKey(driver Driver, dsn string) string {
if driver == DriverSQLite {
dsn = normalizeSQLiteDSN(dsn)
}
return string(driver) + "|" + dsn
}
// AcquireRemote 获取或新建remote 连接;调用方不要 Close。
func AcquireRemote(driver Driver, dsn string) (*sql.DB, error) {
return sharedRemotePool.acquire(driver, dsn)
}
func (p *remotePool) acquire(driver Driver, dsn string) (*sql.DB, error) {
key := poolKey(driver, dsn)
p.mu.Lock()
defer p.mu.Unlock()
if e, ok := p.dbs[key]; ok && e.db != nil {
if err := e.db.Ping(); err == nil {
e.lastUsed = time.Now()
return e.db, nil
}
_ = e.db.Close()
delete(p.dbs, key)
}
db, err := Open(driver, dsn)
if err != nil {
return nil, err
}
p.dbs[key] = &pooledDB{db: db, driver: driver, lastUsed: time.Now()}
return db, nil
}
// WarmRemote 通道启动时预热,把冷开成本挪出请求路径。
func WarmRemote(driver Driver, dsn string) error {
if strings.TrimSpace(dsn) == "" {
return fmt.Errorf("dsn required")
}
_, err := AcquireRemote(driver, dsn)
return err
}
// InvalidateRemote 通道 DSN 变更或停用时丢掉缓存连接。
func InvalidateRemote(driver Driver, dsn string) {
key := poolKey(driver, dsn)
sharedRemotePool.mu.Lock()
defer sharedRemotePool.mu.Unlock()
if e, ok := sharedRemotePool.dbs[key]; ok {
_ = e.db.Close()
delete(sharedRemotePool.dbs, key)
}
}
// CloseAllRemotes 测试或进程退出时关闭池内连接。
func CloseAllRemotes() {
sharedRemotePool.mu.Lock()
defer sharedRemotePool.mu.Unlock()
for k, e := range sharedRemotePool.dbs {
_ = e.db.Close()
delete(sharedRemotePool.dbs, k)
}
}