fix: honor table defaults and preserve dispatch mappings
This commit is contained in:
@@ -4,6 +4,7 @@ import "time"
|
|||||||
|
|
||||||
// APIKeyAuthSnapshot API Key 认证缓存快照(仅包含认证所需字段)
|
// APIKeyAuthSnapshot API Key 认证缓存快照(仅包含认证所需字段)
|
||||||
type APIKeyAuthSnapshot struct {
|
type APIKeyAuthSnapshot struct {
|
||||||
|
Version int `json:"version"`
|
||||||
APIKeyID int64 `json:"api_key_id"`
|
APIKeyID int64 `json:"api_key_id"`
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
GroupID *int64 `json:"group_id,omitempty"`
|
GroupID *int64 `json:"group_id,omitempty"`
|
||||||
@@ -63,8 +64,9 @@ type APIKeyAuthGroupSnapshot struct {
|
|||||||
SupportedModelScopes []string `json:"supported_model_scopes,omitempty"`
|
SupportedModelScopes []string `json:"supported_model_scopes,omitempty"`
|
||||||
|
|
||||||
// OpenAI Messages 调度配置(仅 openai 平台使用)
|
// OpenAI Messages 调度配置(仅 openai 平台使用)
|
||||||
AllowMessagesDispatch bool `json:"allow_messages_dispatch"`
|
AllowMessagesDispatch bool `json:"allow_messages_dispatch"`
|
||||||
DefaultMappedModel string `json:"default_mapped_model,omitempty"`
|
DefaultMappedModel string `json:"default_mapped_model,omitempty"`
|
||||||
|
MessagesDispatchModelConfig OpenAIMessagesDispatchModelConfig `json:"messages_dispatch_model_config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIKeyAuthCacheEntry 缓存条目,支持负缓存
|
// APIKeyAuthCacheEntry 缓存条目,支持负缓存
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
"github.com/dgraph-io/ristretto"
|
"github.com/dgraph-io/ristretto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const apiKeyAuthSnapshotVersion = 3
|
||||||
|
|
||||||
type apiKeyAuthCacheConfig struct {
|
type apiKeyAuthCacheConfig struct {
|
||||||
l1Size int
|
l1Size int
|
||||||
l1TTL time.Duration
|
l1TTL time.Duration
|
||||||
@@ -192,6 +194,9 @@ func (s *APIKeyService) applyAuthCacheEntry(key string, entry *APIKeyAuthCacheEn
|
|||||||
if entry.Snapshot == nil {
|
if entry.Snapshot == nil {
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
if entry.Snapshot.Version != apiKeyAuthSnapshotVersion {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
return s.snapshotToAPIKey(key, entry.Snapshot), true, nil
|
return s.snapshotToAPIKey(key, entry.Snapshot), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +205,7 @@ func (s *APIKeyService) snapshotFromAPIKey(apiKey *APIKey) *APIKeyAuthSnapshot {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
snapshot := &APIKeyAuthSnapshot{
|
snapshot := &APIKeyAuthSnapshot{
|
||||||
|
Version: apiKeyAuthSnapshotVersion,
|
||||||
APIKeyID: apiKey.ID,
|
APIKeyID: apiKey.ID,
|
||||||
UserID: apiKey.UserID,
|
UserID: apiKey.UserID,
|
||||||
GroupID: apiKey.GroupID,
|
GroupID: apiKey.GroupID,
|
||||||
@@ -243,6 +249,7 @@ func (s *APIKeyService) snapshotFromAPIKey(apiKey *APIKey) *APIKeyAuthSnapshot {
|
|||||||
SupportedModelScopes: apiKey.Group.SupportedModelScopes,
|
SupportedModelScopes: apiKey.Group.SupportedModelScopes,
|
||||||
AllowMessagesDispatch: apiKey.Group.AllowMessagesDispatch,
|
AllowMessagesDispatch: apiKey.Group.AllowMessagesDispatch,
|
||||||
DefaultMappedModel: apiKey.Group.DefaultMappedModel,
|
DefaultMappedModel: apiKey.Group.DefaultMappedModel,
|
||||||
|
MessagesDispatchModelConfig: apiKey.Group.MessagesDispatchModelConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return snapshot
|
return snapshot
|
||||||
@@ -298,6 +305,7 @@ func (s *APIKeyService) snapshotToAPIKey(key string, snapshot *APIKeyAuthSnapsho
|
|||||||
SupportedModelScopes: snapshot.Group.SupportedModelScopes,
|
SupportedModelScopes: snapshot.Group.SupportedModelScopes,
|
||||||
AllowMessagesDispatch: snapshot.Group.AllowMessagesDispatch,
|
AllowMessagesDispatch: snapshot.Group.AllowMessagesDispatch,
|
||||||
DefaultMappedModel: snapshot.Group.DefaultMappedModel,
|
DefaultMappedModel: snapshot.Group.DefaultMappedModel,
|
||||||
|
MessagesDispatchModelConfig: snapshot.Group.MessagesDispatchModelConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.compileAPIKeyIPRules(apiKey)
|
s.compileAPIKeyIPRules(apiKey)
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ func TestAPIKeyService_GetByKey_UsesL2Cache(t *testing.T) {
|
|||||||
groupID := int64(9)
|
groupID := int64(9)
|
||||||
cacheEntry := &APIKeyAuthCacheEntry{
|
cacheEntry := &APIKeyAuthCacheEntry{
|
||||||
Snapshot: &APIKeyAuthSnapshot{
|
Snapshot: &APIKeyAuthSnapshot{
|
||||||
|
Version: apiKeyAuthSnapshotVersion,
|
||||||
APIKeyID: 1,
|
APIKeyID: 1,
|
||||||
UserID: 2,
|
UserID: 2,
|
||||||
GroupID: &groupID,
|
GroupID: &groupID,
|
||||||
@@ -226,6 +227,129 @@ func TestAPIKeyService_GetByKey_UsesL2Cache(t *testing.T) {
|
|||||||
require.Equal(t, map[string][]int64{"claude-opus-*": {1, 2}}, apiKey.Group.ModelRouting)
|
require.Equal(t, map[string][]int64{"claude-opus-*": {1, 2}}, apiKey.Group.ModelRouting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIKeyService_SnapshotRoundTrip_PreservesMessagesDispatchModelConfig(t *testing.T) {
|
||||||
|
svc := NewAPIKeyService(nil, nil, nil, nil, nil, nil, &config.Config{})
|
||||||
|
groupID := int64(9)
|
||||||
|
apiKey := &APIKey{
|
||||||
|
ID: 1,
|
||||||
|
UserID: 2,
|
||||||
|
GroupID: &groupID,
|
||||||
|
Key: "k-roundtrip",
|
||||||
|
Status: StatusActive,
|
||||||
|
User: &User{
|
||||||
|
ID: 2,
|
||||||
|
Status: StatusActive,
|
||||||
|
Role: RoleUser,
|
||||||
|
Balance: 10,
|
||||||
|
Concurrency: 3,
|
||||||
|
},
|
||||||
|
Group: &Group{
|
||||||
|
ID: groupID,
|
||||||
|
Name: "openai",
|
||||||
|
Platform: PlatformOpenAI,
|
||||||
|
Status: StatusActive,
|
||||||
|
SubscriptionType: SubscriptionTypeStandard,
|
||||||
|
RateMultiplier: 1,
|
||||||
|
AllowMessagesDispatch: true,
|
||||||
|
DefaultMappedModel: "gpt-5.4",
|
||||||
|
MessagesDispatchModelConfig: OpenAIMessagesDispatchModelConfig{
|
||||||
|
OpusMappedModel: "gpt-5.4-nano",
|
||||||
|
SonnetMappedModel: "gpt-5.3-codex",
|
||||||
|
HaikuMappedModel: "gpt-5.4-mini",
|
||||||
|
ExactModelMappings: map[string]string{
|
||||||
|
"claude-sonnet-4.5": "gpt-5.4-nano",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot := svc.snapshotFromAPIKey(apiKey)
|
||||||
|
roundTrip := svc.snapshotToAPIKey(apiKey.Key, snapshot)
|
||||||
|
|
||||||
|
require.NotNil(t, roundTrip)
|
||||||
|
require.NotNil(t, roundTrip.Group)
|
||||||
|
require.Equal(t, apiKey.Group.MessagesDispatchModelConfig, roundTrip.Group.MessagesDispatchModelConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIKeyService_GetByKey_IgnoresLegacyAuthCacheSnapshotWithoutMessagesDispatchConfig(t *testing.T) {
|
||||||
|
cache := &authCacheStub{}
|
||||||
|
var repoCalls int32
|
||||||
|
repo := &authRepoStub{
|
||||||
|
getByKeyForAuth: func(ctx context.Context, key string) (*APIKey, error) {
|
||||||
|
atomic.AddInt32(&repoCalls, 1)
|
||||||
|
groupID := int64(9)
|
||||||
|
return &APIKey{
|
||||||
|
ID: 1,
|
||||||
|
UserID: 2,
|
||||||
|
GroupID: &groupID,
|
||||||
|
Status: StatusActive,
|
||||||
|
User: &User{
|
||||||
|
ID: 2,
|
||||||
|
Status: StatusActive,
|
||||||
|
Role: RoleUser,
|
||||||
|
Balance: 10,
|
||||||
|
Concurrency: 3,
|
||||||
|
},
|
||||||
|
Group: &Group{
|
||||||
|
ID: groupID,
|
||||||
|
Name: "openai",
|
||||||
|
Platform: PlatformOpenAI,
|
||||||
|
Status: StatusActive,
|
||||||
|
Hydrated: true,
|
||||||
|
SubscriptionType: SubscriptionTypeStandard,
|
||||||
|
RateMultiplier: 1,
|
||||||
|
AllowMessagesDispatch: true,
|
||||||
|
DefaultMappedModel: "gpt-5.4",
|
||||||
|
MessagesDispatchModelConfig: OpenAIMessagesDispatchModelConfig{
|
||||||
|
OpusMappedModel: "gpt-5.4-nano",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cfg := &config.Config{
|
||||||
|
APIKeyAuth: config.APIKeyAuthCacheConfig{
|
||||||
|
L2TTLSeconds: 60,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svc := NewAPIKeyService(repo, nil, nil, nil, nil, cache, cfg)
|
||||||
|
|
||||||
|
groupID := int64(9)
|
||||||
|
cache.getAuthCache = func(ctx context.Context, key string) (*APIKeyAuthCacheEntry, error) {
|
||||||
|
return &APIKeyAuthCacheEntry{
|
||||||
|
Snapshot: &APIKeyAuthSnapshot{
|
||||||
|
APIKeyID: 1,
|
||||||
|
UserID: 2,
|
||||||
|
GroupID: &groupID,
|
||||||
|
Status: StatusActive,
|
||||||
|
User: APIKeyAuthUserSnapshot{
|
||||||
|
ID: 2,
|
||||||
|
Status: StatusActive,
|
||||||
|
Role: RoleUser,
|
||||||
|
Balance: 10,
|
||||||
|
Concurrency: 3,
|
||||||
|
},
|
||||||
|
Group: &APIKeyAuthGroupSnapshot{
|
||||||
|
ID: groupID,
|
||||||
|
Name: "openai",
|
||||||
|
Platform: PlatformOpenAI,
|
||||||
|
Status: StatusActive,
|
||||||
|
SubscriptionType: SubscriptionTypeStandard,
|
||||||
|
RateMultiplier: 1,
|
||||||
|
AllowMessagesDispatch: true,
|
||||||
|
DefaultMappedModel: "gpt-5.4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
apiKey, err := svc.GetByKey(context.Background(), "k-legacy")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int32(1), atomic.LoadInt32(&repoCalls))
|
||||||
|
require.NotNil(t, apiKey.Group)
|
||||||
|
require.Equal(t, "gpt-5.4-nano", apiKey.Group.MessagesDispatchModelConfig.OpusMappedModel)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIKeyService_GetByKey_NegativeCache(t *testing.T) {
|
func TestAPIKeyService_GetByKey_NegativeCache(t *testing.T) {
|
||||||
cache := &authCacheStub{}
|
cache := &authCacheStub{}
|
||||||
repo := &authRepoStub{
|
repo := &authRepoStub{
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ import { computed, ref } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import Icon from '@/components/icons/Icon.vue'
|
import Icon from '@/components/icons/Icon.vue'
|
||||||
import Select from './Select.vue'
|
import Select from './Select.vue'
|
||||||
import { setPersistedPageSize } from '@/composables/usePersistedPageSize'
|
|
||||||
import { getConfiguredTablePageSizeOptions, normalizeTablePageSize } from '@/utils/tablePreferences'
|
import { getConfiguredTablePageSizeOptions, normalizeTablePageSize } from '@/utils/tablePreferences'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -225,7 +224,6 @@ const goToPage = (newPage: number) => {
|
|||||||
const handlePageSizeChange = (value: string | number | boolean | null) => {
|
const handlePageSizeChange = (value: string | number | boolean | null) => {
|
||||||
if (value === null || typeof value === 'boolean') return
|
if (value === null || typeof value === 'boolean') return
|
||||||
const newPageSize = normalizeTablePageSize(typeof value === 'string' ? parseInt(value, 10) : value)
|
const newPageSize = normalizeTablePageSize(typeof value === 'string' ? parseInt(value, 10) : value)
|
||||||
setPersistedPageSize(newPageSize)
|
|
||||||
emit('update:pageSize', newPageSize)
|
emit('update:pageSize', newPageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { afterEach, describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import { getPersistedPageSize } from '@/composables/usePersistedPageSize'
|
||||||
|
|
||||||
|
describe('usePersistedPageSize', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
localStorage.clear()
|
||||||
|
delete window.__APP_CONFIG__
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses the system table default instead of stale localStorage state', () => {
|
||||||
|
window.__APP_CONFIG__ = {
|
||||||
|
table_default_page_size: 1000,
|
||||||
|
table_page_size_options: [20, 50, 1000]
|
||||||
|
} as any
|
||||||
|
localStorage.setItem('table-page-size', '50')
|
||||||
|
localStorage.setItem('table-page-size-source', 'user')
|
||||||
|
|
||||||
|
expect(getPersistedPageSize()).toBe(1000)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,49 +1,9 @@
|
|||||||
import { getConfiguredTableDefaultPageSize, normalizeTablePageSize } from '@/utils/tablePreferences'
|
import { getConfiguredTableDefaultPageSize, normalizeTablePageSize } from '@/utils/tablePreferences'
|
||||||
|
|
||||||
const STORAGE_KEY = 'table-page-size'
|
|
||||||
const SOURCE_KEY = 'table-page-size-source'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 localStorage 读取/写入 pageSize
|
* 读取当前系统配置的表格默认每页条数。
|
||||||
* 全局共享一个 key,所有表格统一偏好
|
* 不再使用本地持久化缓存,所有页面统一以通用表格设置为准。
|
||||||
*/
|
*/
|
||||||
export function getPersistedPageSize(fallback = getConfiguredTableDefaultPageSize()): number {
|
export function getPersistedPageSize(fallback = getConfiguredTableDefaultPageSize()): number {
|
||||||
try {
|
return normalizeTablePageSize(getConfiguredTableDefaultPageSize() || fallback)
|
||||||
const stored = localStorage.getItem(STORAGE_KEY)
|
|
||||||
if (stored) {
|
|
||||||
return normalizeTablePageSize(stored)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// localStorage 不可用(隐私模式等)
|
|
||||||
}
|
|
||||||
return normalizeTablePageSize(fallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPersistedPageSize(size: number): void {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(STORAGE_KEY, String(normalizeTablePageSize(size)))
|
|
||||||
localStorage.setItem(SOURCE_KEY, 'user')
|
|
||||||
} catch {
|
|
||||||
// 静默失败
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function syncPersistedPageSizeWithSystemDefault(defaultSize = getConfiguredTableDefaultPageSize()): void {
|
|
||||||
try {
|
|
||||||
const normalizedDefault = normalizeTablePageSize(defaultSize)
|
|
||||||
const stored = localStorage.getItem(STORAGE_KEY)
|
|
||||||
const source = localStorage.getItem(SOURCE_KEY)
|
|
||||||
const normalizedStored = stored ? normalizeTablePageSize(stored) : null
|
|
||||||
|
|
||||||
if ((source === 'user' || (source === null && stored !== null)) && stored) {
|
|
||||||
localStorage.setItem(STORAGE_KEY, String(normalizedStored ?? normalizedDefault))
|
|
||||||
localStorage.setItem(SOURCE_KEY, 'user')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem(STORAGE_KEY, String(normalizedDefault))
|
|
||||||
localStorage.setItem(SOURCE_KEY, 'system')
|
|
||||||
} catch {
|
|
||||||
// 静默失败
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ref, reactive, onUnmounted, toRaw } from 'vue'
|
import { ref, reactive, onUnmounted, toRaw } from 'vue'
|
||||||
import { useDebounceFn } from '@vueuse/core'
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
import type { BasePaginationResponse, FetchOptions } from '@/types'
|
import type { BasePaginationResponse, FetchOptions } from '@/types'
|
||||||
import { getPersistedPageSize, setPersistedPageSize } from './usePersistedPageSize'
|
import { getPersistedPageSize } from './usePersistedPageSize'
|
||||||
|
|
||||||
interface PaginationState {
|
interface PaginationState {
|
||||||
page: number
|
page: number
|
||||||
@@ -88,7 +88,6 @@ export function useTableLoader<T, P extends Record<string, any>>(options: TableL
|
|||||||
const handlePageSizeChange = (size: number) => {
|
const handlePageSizeChange = (size: number) => {
|
||||||
pagination.page_size = size
|
pagination.page_size = size
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
setPersistedPageSize(size)
|
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -329,85 +329,8 @@ describe('useAppStore', () => {
|
|||||||
|
|
||||||
expect((window as any).__APP_CONFIG__.table_default_page_size).toBe(1000)
|
expect((window as any).__APP_CONFIG__.table_default_page_size).toBe(1000)
|
||||||
expect((window as any).__APP_CONFIG__.table_page_size_options).toEqual([20, 100, 1000])
|
expect((window as any).__APP_CONFIG__.table_page_size_options).toEqual([20, 100, 1000])
|
||||||
expect(localStorage.getItem('table-page-size')).toBe('1000')
|
expect(localStorage.getItem('table-page-size')).toBeNull()
|
||||||
expect(localStorage.getItem('table-page-size-source')).toBe('system')
|
expect(localStorage.getItem('table-page-size-source')).toBeNull()
|
||||||
})
|
|
||||||
|
|
||||||
it('fetchPublicSettings(force) 保留用户显式选择的分页大小', async () => {
|
|
||||||
localStorage.setItem('table-page-size', '100')
|
|
||||||
localStorage.setItem('table-page-size-source', 'user')
|
|
||||||
|
|
||||||
vi.mocked(getPublicSettings).mockResolvedValue({
|
|
||||||
registration_enabled: false,
|
|
||||||
email_verify_enabled: false,
|
|
||||||
registration_email_suffix_whitelist: [],
|
|
||||||
promo_code_enabled: true,
|
|
||||||
password_reset_enabled: false,
|
|
||||||
invitation_code_enabled: false,
|
|
||||||
turnstile_enabled: false,
|
|
||||||
turnstile_site_key: '',
|
|
||||||
site_name: 'Updated Site',
|
|
||||||
site_logo: '',
|
|
||||||
site_subtitle: '',
|
|
||||||
api_base_url: '',
|
|
||||||
contact_info: '',
|
|
||||||
doc_url: '',
|
|
||||||
home_content: '',
|
|
||||||
hide_ccs_import_button: false,
|
|
||||||
purchase_subscription_enabled: false,
|
|
||||||
purchase_subscription_url: '',
|
|
||||||
table_default_page_size: 1000,
|
|
||||||
table_page_size_options: [20, 50, 1000],
|
|
||||||
custom_menu_items: [],
|
|
||||||
custom_endpoints: [],
|
|
||||||
linuxdo_oauth_enabled: false,
|
|
||||||
backend_mode_enabled: false,
|
|
||||||
version: '1.0.0'
|
|
||||||
})
|
|
||||||
|
|
||||||
const store = useAppStore()
|
|
||||||
await store.fetchPublicSettings(true)
|
|
||||||
|
|
||||||
expect(localStorage.getItem('table-page-size')).toBe('1000')
|
|
||||||
expect(localStorage.getItem('table-page-size-source')).toBe('user')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('fetchPublicSettings(force) 保留旧版本未标记来源的分页偏好', async () => {
|
|
||||||
localStorage.setItem('table-page-size', '50')
|
|
||||||
|
|
||||||
vi.mocked(getPublicSettings).mockResolvedValue({
|
|
||||||
registration_enabled: false,
|
|
||||||
email_verify_enabled: false,
|
|
||||||
registration_email_suffix_whitelist: [],
|
|
||||||
promo_code_enabled: true,
|
|
||||||
password_reset_enabled: false,
|
|
||||||
invitation_code_enabled: false,
|
|
||||||
turnstile_enabled: false,
|
|
||||||
turnstile_site_key: '',
|
|
||||||
site_name: 'Updated Site',
|
|
||||||
site_logo: '',
|
|
||||||
site_subtitle: '',
|
|
||||||
api_base_url: '',
|
|
||||||
contact_info: '',
|
|
||||||
doc_url: '',
|
|
||||||
home_content: '',
|
|
||||||
hide_ccs_import_button: false,
|
|
||||||
purchase_subscription_enabled: false,
|
|
||||||
purchase_subscription_url: '',
|
|
||||||
table_default_page_size: 1000,
|
|
||||||
table_page_size_options: [20, 50, 1000],
|
|
||||||
custom_menu_items: [],
|
|
||||||
custom_endpoints: [],
|
|
||||||
linuxdo_oauth_enabled: false,
|
|
||||||
backend_mode_enabled: false,
|
|
||||||
version: '1.0.0'
|
|
||||||
})
|
|
||||||
|
|
||||||
const store = useAppStore()
|
|
||||||
await store.fetchPublicSettings(true)
|
|
||||||
|
|
||||||
expect(localStorage.getItem('table-page-size')).toBe('50')
|
|
||||||
expect(localStorage.getItem('table-page-size-source')).toBe('user')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
type ReleaseInfo
|
type ReleaseInfo
|
||||||
} from '@/api/admin/system'
|
} from '@/api/admin/system'
|
||||||
import { getPublicSettings as fetchPublicSettingsAPI } from '@/api/auth'
|
import { getPublicSettings as fetchPublicSettingsAPI } from '@/api/auth'
|
||||||
import { syncPersistedPageSizeWithSystemDefault } from '@/composables/usePersistedPageSize'
|
|
||||||
|
|
||||||
export const useAppStore = defineStore('app', () => {
|
export const useAppStore = defineStore('app', () => {
|
||||||
// ==================== State ====================
|
// ==================== State ====================
|
||||||
@@ -288,7 +287,6 @@ export const useAppStore = defineStore('app', () => {
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.__APP_CONFIG__ = { ...config }
|
window.__APP_CONFIG__ = { ...config }
|
||||||
}
|
}
|
||||||
syncPersistedPageSizeWithSystemDefault(config.table_default_page_size)
|
|
||||||
cachedPublicSettings.value = config
|
cachedPublicSettings.value = config
|
||||||
siteName.value = config.site_name || 'Sub2API'
|
siteName.value = config.site_name || 'Sub2API'
|
||||||
siteLogo.value = config.site_logo || ''
|
siteLogo.value = config.site_logo || ''
|
||||||
|
|||||||
Reference in New Issue
Block a user