fix(backend): 模型目录 - 空目录不再丢失集合字段导致刷新掉登录 (#502)
This commit is contained in:
committed by
GitHub
parent
73ec4a33b2
commit
de6a642f6c
@@ -21,10 +21,11 @@ const (
|
||||
|
||||
// ModelCatalogResponse 是创作端模型选择的统一读模型。
|
||||
// Source=frontend 时读取 Models;Source=system 时读取 Channels。
|
||||
// 两个集合都始终序列化为数组:空目录必须发 [],缺字段会被前端判成畸形响应。
|
||||
type ModelCatalogResponse struct {
|
||||
Source ModelCatalogSource `json:"source"`
|
||||
Models []PublicLogicalModel `json:"models,omitempty"`
|
||||
Channels []PublicChannelCatalog `json:"channels,omitempty"`
|
||||
Models []PublicLogicalModel `json:"models"`
|
||||
Channels []PublicChannelCatalog `json:"channels"`
|
||||
}
|
||||
|
||||
// PublicChannelCatalog 公开的渠道目录信息(脱敏)
|
||||
@@ -74,25 +75,25 @@ func (s *Service) ModelCatalog(intent *ModelRequestIntent) (*ModelCatalogRespons
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 两个集合都初始化成非 nil 空切片:空目录要发 [],不能因为“没有模型”而丢掉字段。
|
||||
response := &ModelCatalogResponse{Models: []PublicLogicalModel{}, Channels: []PublicChannelCatalog{}}
|
||||
if frontendEnabled {
|
||||
models, err := s.PublicLogicalModels(intent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ModelCatalogResponse{
|
||||
Source: ModelCatalogSourceFrontend,
|
||||
Models: models,
|
||||
}, nil
|
||||
response.Source = ModelCatalogSourceFrontend
|
||||
response.Models = append(response.Models, models...)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
channels, err := s.publicSystemChannelCatalog(intent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ModelCatalogResponse{
|
||||
Source: ModelCatalogSourceSystem,
|
||||
Channels: channels,
|
||||
}, nil
|
||||
response.Source = ModelCatalogSourceSystem
|
||||
response.Channels = append(response.Channels, channels...)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// publicSystemChannelCatalog 组装普通用户可见的系统渠道读模型,不暴露密钥、Base URL 等执行凭证。
|
||||
|
||||
73
backend/internal/app/model_catalog_test.go
Normal file
73
backend/internal/app/model_catalog_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
"infinite-canvas/backend/internal/repository"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 前台模型目录为空时也必须发 models: [],不能因为 omitempty 丢掉字段:
|
||||
// 前端在 source=frontend 时强制校验 models 是数组,缺字段会被判成畸形响应;
|
||||
// 而 auth-session-hydrator 会把该异常当成未登录并清掉会话,表现为刷新后跳回登录页。
|
||||
func TestModelCatalogAlwaysSerializesCollectionsAsArrays(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
frontendEnabled bool
|
||||
wantSource string
|
||||
}{
|
||||
{name: "空的前台模型目录", frontendEnabled: true, wantSource: "frontend"},
|
||||
{name: "空的系统渠道目录", frontendEnabled: false, wantSource: "system"},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
svc, db := newModelCatalogTestService(t)
|
||||
if test.frontendEnabled {
|
||||
if err := db.Create(&model.SystemSetting{Key: featureAvailabilitySettingKey, ValueJSON: `{"frontendModelsEnabled":true}`}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
catalog, err := svc.ModelCatalog(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(catalog.Source) != test.wantSource {
|
||||
t.Fatalf("source = %q, want %q", catalog.Source, test.wantSource)
|
||||
}
|
||||
|
||||
encoded, err := json.Marshal(catalog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var payload map[string]json.RawMessage
|
||||
if err := json.Unmarshal(encoded, &payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, key := range []string{"models", "channels"} {
|
||||
raw, present := payload[key]
|
||||
if !present || string(raw) == "null" {
|
||||
t.Fatalf("%s 集合没有序列化成数组:%s", key, encoded)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newModelCatalogTestService(t *testing.T) (*Service, *gorm.DB) {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sqlDB, err := db.DB(); err == nil {
|
||||
sqlDB.SetMaxOpenConns(1)
|
||||
}
|
||||
if err := db.AutoMigrate(&model.SystemSetting{}, &model.AdminAuditEvent{}, &model.ModelChannel{}, &model.ChannelModel{}, &model.LogicalModel{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return New(repository.New(db), t.TempDir()), db
|
||||
}
|
||||
Reference in New Issue
Block a user