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

@@ -16,16 +16,19 @@ func TestEnsureBindingUpsert(t *testing.T) {
LocalDatabaseID: "local-a",
OnlineDBID: "online-1",
ChannelID: "ch1",
DatabaseName: "本地演示库",
DisplayName: "AI建站智能体API",
})
if err != nil || b.ID == "" {
if err != nil || b.ID == "" || b.DisplayName != "AI建站智能体API" {
t.Fatalf("ensure: %+v err=%v", b, err)
}
b2, err := st.EnsureBinding(Binding{
TenantID: 1,
LocalDatabaseID: "local-a",
OnlineDBID: "online-2",
DisplayName: "线上库B",
})
if err != nil || b2.OnlineDBID != "online-2" || b2.ID != b.ID {
if err != nil || b2.OnlineDBID != "online-2" || b2.ID != b.ID || b2.DisplayName != "线上库B" {
t.Fatalf("upsert: %+v err=%v", b2, err)
}
list, err := st.ListBindings(1, "local-a")
@@ -33,3 +36,42 @@ func TestEnsureBindingUpsert(t *testing.T) {
t.Fatalf("list=%d err=%v", len(list), err)
}
}
func TestUserBindingScope(t *testing.T) {
dir := t.TempDir()
st, err := NewFileStore(filepath.Join(dir, "dbsync"))
if err != nil {
t.Fatal(err)
}
_, err = st.EnsureBinding(Binding{
TenantID: 1, UserID: 10,
LocalDatabaseID: "loc-a", OnlineDBID: "on-a", ChannelID: "ch1",
})
if err != nil {
t.Fatal(err)
}
_, err = st.EnsureBinding(Binding{
TenantID: 1, UserID: 20,
LocalDatabaseID: "loc-b", OnlineDBID: "on-b", ChannelID: "ch1",
})
if err != nil {
t.Fatal(err)
}
mine, err := st.ListBindingsFiltered(1, 10, "")
if err != nil || len(mine) != 1 || mine[0].OnlineDBID != "on-a" {
t.Fatalf("filtered: %+v err=%v", mine, err)
}
if !st.UserOwnsOnlineDB(1, 10, "ch1", "on-a") {
t.Fatal("should own on-a")
}
if st.UserOwnsOnlineDB(1, 10, "ch1", "on-b") {
t.Fatal("must not own other's online_db_id")
}
if !st.UserCanAccessChannel(1, 10, "ch1") {
t.Fatal("should access ch1")
}
if st.UserCanAccessChannel(1, 10, "ch-other") {
t.Fatal("must not access unbound channel")
}
}