From 739c0ff9c553acfee6d740e13752c2e2bb3c551a Mon Sep 17 00:00:00 2001 From: wucm667 Date: Thu, 30 Jul 2026 16:28:19 +0800 Subject: [PATCH] feat(home): add compact home page preset to avoid abuse classification - Add compact_home_enabled setting to provide a minimal landing page - Preserves custom home_content priority over compact mode - Renders only site identity, navigation, and login/dashboard link - Avoids marketing copy that triggers anti-fraud systems - Includes focused backend/frontend tests and i18n support Fixes #5065 --- .../internal/handler/admin/setting_handler.go | 1 + .../handler/admin/setting_handler_audit.go | 3 + .../setting_handler_platform_quota_test.go | 12 ++ .../handler/admin/setting_handler_update.go | 3 + backend/internal/handler/dto/settings.go | 2 + backend/internal/handler/setting_handler.go | 1 + backend/internal/service/domain_constants.go | 1 + backend/internal/service/setting_parse.go | 1 + backend/internal/service/setting_public.go | 4 + .../service/setting_service_public_test.go | 19 +++ .../service/setting_service_update_test.go | 10 ++ backend/internal/service/setting_update.go | 1 + backend/internal/service/settings_view.go | 2 + frontend/src/api/admin/settings.ts | 2 + .../auth/__tests__/WechatOAuthSection.spec.ts | 1 + .../ProfileIdentityBindingsSection.spec.ts | 1 + .../src/i18n/locales/en/admin/settings.ts | 2 + .../src/i18n/locales/zh/admin/settings.ts | 2 + frontend/src/stores/__tests__/app.spec.ts | 2 + frontend/src/stores/app.ts | 1 + frontend/src/types/index.ts | 1 + frontend/src/views/HomeView.vue | 73 ++++++++++- .../views/__tests__/HomeView.compact.spec.ts | 122 ++++++++++++++++++ frontend/src/views/admin/SettingsView.vue | 15 +++ .../admin/__tests__/SettingsView.spec.ts | 17 +++ 25 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 frontend/src/views/__tests__/HomeView.compact.spec.ts diff --git a/backend/internal/handler/admin/setting_handler.go b/backend/internal/handler/admin/setting_handler.go index 703d297fa2..8ad27e122a 100644 --- a/backend/internal/handler/admin/setting_handler.go +++ b/backend/internal/handler/admin/setting_handler.go @@ -233,6 +233,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) { ContactInfo: settings.ContactInfo, DocURL: settings.DocURL, HomeContent: settings.HomeContent, + CompactHomeEnabled: settings.CompactHomeEnabled, HideCcsImportButton: settings.HideCcsImportButton, PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled, PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL, diff --git a/backend/internal/handler/admin/setting_handler_audit.go b/backend/internal/handler/admin/setting_handler_audit.go index 30c2495af4..ab91ac9f52 100644 --- a/backend/internal/handler/admin/setting_handler_audit.go +++ b/backend/internal/handler/admin/setting_handler_audit.go @@ -299,6 +299,9 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings, if before.HomeContent != after.HomeContent { changed = append(changed, "home_content") } + if before.CompactHomeEnabled != after.CompactHomeEnabled { + changed = append(changed, "compact_home_enabled") + } if before.HideCcsImportButton != after.HideCcsImportButton { changed = append(changed, "hide_ccs_import_button") } diff --git a/backend/internal/handler/admin/setting_handler_platform_quota_test.go b/backend/internal/handler/admin/setting_handler_platform_quota_test.go index 273b34420b..39048394e2 100644 --- a/backend/internal/handler/admin/setting_handler_platform_quota_test.go +++ b/backend/internal/handler/admin/setting_handler_platform_quota_test.go @@ -64,6 +64,18 @@ func TestDiffSettings_NoChangeWhenEqual(t *testing.T) { } } +func TestDiffSettings_DetectsCompactHomeChange(t *testing.T) { + changed := diffSettings( + &service.SystemSettings{}, + &service.SystemSettings{CompactHomeEnabled: true}, + nil, + nil, + UpdateSettingsRequest{}, + ) + + require.Contains(t, changed, service.SettingKeyCompactHomeEnabled) +} + func TestEqualNullableFloat(t *testing.T) { five := 5.0 five2 := 5.0 diff --git a/backend/internal/handler/admin/setting_handler_update.go b/backend/internal/handler/admin/setting_handler_update.go index 65c90a3a6b..54569bc47a 100644 --- a/backend/internal/handler/admin/setting_handler_update.go +++ b/backend/internal/handler/admin/setting_handler_update.go @@ -142,6 +142,7 @@ type UpdateSettingsRequest struct { ContactInfo string `json:"contact_info"` DocURL string `json:"doc_url"` HomeContent string `json:"home_content"` + CompactHomeEnabled bool `json:"compact_home_enabled"` HideCcsImportButton bool `json:"hide_ccs_import_button"` PurchaseSubscriptionEnabled *bool `json:"purchase_subscription_enabled"` PurchaseSubscriptionURL *string `json:"purchase_subscription_url"` @@ -1430,6 +1431,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { ContactInfo: req.ContactInfo, DocURL: req.DocURL, HomeContent: req.HomeContent, + CompactHomeEnabled: req.CompactHomeEnabled, HideCcsImportButton: req.HideCcsImportButton, PurchaseSubscriptionEnabled: purchaseEnabled, PurchaseSubscriptionURL: purchaseURL, @@ -1982,6 +1984,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { ContactInfo: updatedSettings.ContactInfo, DocURL: updatedSettings.DocURL, HomeContent: updatedSettings.HomeContent, + CompactHomeEnabled: updatedSettings.CompactHomeEnabled, HideCcsImportButton: updatedSettings.HideCcsImportButton, PurchaseSubscriptionEnabled: updatedSettings.PurchaseSubscriptionEnabled, PurchaseSubscriptionURL: updatedSettings.PurchaseSubscriptionURL, diff --git a/backend/internal/handler/dto/settings.go b/backend/internal/handler/dto/settings.go index dc34771cad..da4c6abc10 100644 --- a/backend/internal/handler/dto/settings.go +++ b/backend/internal/handler/dto/settings.go @@ -142,6 +142,7 @@ type SystemSettings struct { ContactInfo string `json:"contact_info"` DocURL string `json:"doc_url"` HomeContent string `json:"home_content"` + CompactHomeEnabled bool `json:"compact_home_enabled"` HideCcsImportButton bool `json:"hide_ccs_import_button"` PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"` PurchaseSubscriptionURL string `json:"purchase_subscription_url"` @@ -344,6 +345,7 @@ type PublicSettings struct { ContactInfo string `json:"contact_info"` DocURL string `json:"doc_url"` HomeContent string `json:"home_content"` + CompactHomeEnabled bool `json:"compact_home_enabled"` HideCcsImportButton bool `json:"hide_ccs_import_button"` PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"` PurchaseSubscriptionURL string `json:"purchase_subscription_url"` diff --git a/backend/internal/handler/setting_handler.go b/backend/internal/handler/setting_handler.go index 49f4d5f990..d825d7ef63 100644 --- a/backend/internal/handler/setting_handler.go +++ b/backend/internal/handler/setting_handler.go @@ -67,6 +67,7 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) { ContactInfo: settings.ContactInfo, DocURL: settings.DocURL, HomeContent: settings.HomeContent, + CompactHomeEnabled: settings.CompactHomeEnabled, HideCcsImportButton: settings.HideCcsImportButton, PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled, PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL, diff --git a/backend/internal/service/domain_constants.go b/backend/internal/service/domain_constants.go index 766b7381e3..aa3153d0e8 100644 --- a/backend/internal/service/domain_constants.go +++ b/backend/internal/service/domain_constants.go @@ -271,6 +271,7 @@ const ( SettingKeyContactInfo = "contact_info" // 客服联系方式 SettingKeyDocURL = "doc_url" // 文档链接 SettingKeyHomeContent = "home_content" // 首页内容(支持 Markdown/HTML,或 URL 作为 iframe src) + SettingKeyCompactHomeEnabled = "compact_home_enabled" // 是否启用内置简洁首页 SettingKeyHideCcsImportButton = "hide_ccs_import_button" // 是否隐藏 API Keys 页面的导入 CCS 按钮 SettingKeyPurchaseSubscriptionEnabled = "purchase_subscription_enabled" // 是否展示"购买订阅"页面入口 SettingKeyPurchaseSubscriptionURL = "purchase_subscription_url" // "购买订阅"页面 URL(作为 iframe src) diff --git a/backend/internal/service/setting_parse.go b/backend/internal/service/setting_parse.go index a4d409f9c1..d5075038b1 100644 --- a/backend/internal/service/setting_parse.go +++ b/backend/internal/service/setting_parse.go @@ -330,6 +330,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin ContactInfo: settings[SettingKeyContactInfo], DocURL: settings[SettingKeyDocURL], HomeContent: settings[SettingKeyHomeContent], + CompactHomeEnabled: settings[SettingKeyCompactHomeEnabled] == "true", HideCcsImportButton: settings[SettingKeyHideCcsImportButton] == "true", PurchaseSubscriptionEnabled: settings[SettingKeyPurchaseSubscriptionEnabled] == "true", PurchaseSubscriptionURL: strings.TrimSpace(settings[SettingKeyPurchaseSubscriptionURL]), diff --git a/backend/internal/service/setting_public.go b/backend/internal/service/setting_public.go index 76af91c305..bb2213a118 100644 --- a/backend/internal/service/setting_public.go +++ b/backend/internal/service/setting_public.go @@ -179,6 +179,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings SettingKeyContactInfo, SettingKeyDocURL, SettingKeyHomeContent, + SettingKeyCompactHomeEnabled, SettingKeyHideCcsImportButton, SettingKeyPurchaseSubscriptionEnabled, SettingKeyPurchaseSubscriptionURL, @@ -307,6 +308,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings ContactInfo: settings[SettingKeyContactInfo], DocURL: settings[SettingKeyDocURL], HomeContent: settings[SettingKeyHomeContent], + CompactHomeEnabled: settings[SettingKeyCompactHomeEnabled] == "true", HideCcsImportButton: settings[SettingKeyHideCcsImportButton] == "true", PurchaseSubscriptionEnabled: settings[SettingKeyPurchaseSubscriptionEnabled] == "true", PurchaseSubscriptionURL: strings.TrimSpace(settings[SettingKeyPurchaseSubscriptionURL]), @@ -495,6 +497,7 @@ type PublicSettingsInjectionPayload struct { ContactInfo string `json:"contact_info"` DocURL string `json:"doc_url"` HomeContent string `json:"home_content"` + CompactHomeEnabled bool `json:"compact_home_enabled"` HideCcsImportButton bool `json:"hide_ccs_import_button"` PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"` PurchaseSubscriptionURL string `json:"purchase_subscription_url"` @@ -567,6 +570,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any ContactInfo: settings.ContactInfo, DocURL: settings.DocURL, HomeContent: settings.HomeContent, + CompactHomeEnabled: settings.CompactHomeEnabled, HideCcsImportButton: settings.HideCcsImportButton, PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled, PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL, diff --git a/backend/internal/service/setting_service_public_test.go b/backend/internal/service/setting_service_public_test.go index 621620fd01..54bdda77f7 100644 --- a/backend/internal/service/setting_service_public_test.go +++ b/backend/internal/service/setting_service_public_test.go @@ -78,6 +78,25 @@ func TestSettingService_GetPublicSettings_ExposesTablePreferences(t *testing.T) require.Equal(t, []int{20, 50, 100}, settings.TablePageSizeOptions) } +func TestSettingService_GetPublicSettings_ExposesCompactHomeEnabled(t *testing.T) { + repo := &settingPublicRepoStub{ + values: map[string]string{ + SettingKeyCompactHomeEnabled: "true", + }, + } + svc := NewSettingService(repo, &config.Config{}) + + settings, err := svc.GetPublicSettings(context.Background()) + + require.NoError(t, err) + require.True(t, settings.CompactHomeEnabled) + + missingSettings, err := NewSettingService(&settingPublicRepoStub{values: map[string]string{}}, &config.Config{}). + GetPublicSettings(context.Background()) + require.NoError(t, err) + require.False(t, missingSettings.CompactHomeEnabled) +} + func TestSettingService_GetPublicSettings_ExposesForceEmailOnThirdPartySignup(t *testing.T) { repo := &settingPublicRepoStub{ values: map[string]string{ diff --git a/backend/internal/service/setting_service_update_test.go b/backend/internal/service/setting_service_update_test.go index b689f56781..750f01728b 100644 --- a/backend/internal/service/setting_service_update_test.go +++ b/backend/internal/service/setting_service_update_test.go @@ -228,6 +228,16 @@ func (s *defaultSubGroupReaderStub) GetByID(ctx context.Context, id int64) (*Gro return nil, ErrGroupNotFound } +func TestSettingService_UpdateSettings_PersistsCompactHomeEnabled(t *testing.T) { + repo := &settingUpdateRepoStub{} + svc := NewSettingService(repo, &config.Config{}) + + err := svc.UpdateSettings(context.Background(), &SystemSettings{CompactHomeEnabled: true}) + + require.NoError(t, err) + require.Equal(t, "true", repo.updates[SettingKeyCompactHomeEnabled]) +} + func TestSettingService_UpdateSettings_DefaultSubscriptions_ValidGroup(t *testing.T) { repo := &settingUpdateRepoStub{} groupReader := &defaultSubGroupReaderStub{ diff --git a/backend/internal/service/setting_update.go b/backend/internal/service/setting_update.go index d094f9026f..e242c46d2c 100644 --- a/backend/internal/service/setting_update.go +++ b/backend/internal/service/setting_update.go @@ -320,6 +320,7 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting updates[SettingKeyContactInfo] = settings.ContactInfo updates[SettingKeyDocURL] = settings.DocURL updates[SettingKeyHomeContent] = settings.HomeContent + updates[SettingKeyCompactHomeEnabled] = strconv.FormatBool(settings.CompactHomeEnabled) updates[SettingKeyHideCcsImportButton] = strconv.FormatBool(settings.HideCcsImportButton) updates[SettingKeyPurchaseSubscriptionEnabled] = strconv.FormatBool(settings.PurchaseSubscriptionEnabled) updates[SettingKeyPurchaseSubscriptionURL] = strings.TrimSpace(settings.PurchaseSubscriptionURL) diff --git a/backend/internal/service/settings_view.go b/backend/internal/service/settings_view.go index 4a6d3906f3..a8ddb4908a 100644 --- a/backend/internal/service/settings_view.go +++ b/backend/internal/service/settings_view.go @@ -139,6 +139,7 @@ type SystemSettings struct { ContactInfo string DocURL string HomeContent string + CompactHomeEnabled bool HideCcsImportButton bool PurchaseSubscriptionEnabled bool PurchaseSubscriptionURL string @@ -305,6 +306,7 @@ type PublicSettings struct { ContactInfo string DocURL string HomeContent string + CompactHomeEnabled bool HideCcsImportButton bool PurchaseSubscriptionEnabled bool diff --git a/frontend/src/api/admin/settings.ts b/frontend/src/api/admin/settings.ts index 2fceea7b62..b018b925b4 100644 --- a/frontend/src/api/admin/settings.ts +++ b/frontend/src/api/admin/settings.ts @@ -440,6 +440,7 @@ export interface SystemSettings { contact_info: string; doc_url: string; home_content: string; + compact_home_enabled: boolean; hide_ccs_import_button: boolean; table_default_page_size: number; table_page_size_options: number[]; @@ -752,6 +753,7 @@ export interface UpdateSettingsRequest { contact_info?: string; doc_url?: string; home_content?: string; + compact_home_enabled?: boolean; hide_ccs_import_button?: boolean; table_default_page_size?: number; table_page_size_options?: number[]; diff --git a/frontend/src/components/auth/__tests__/WechatOAuthSection.spec.ts b/frontend/src/components/auth/__tests__/WechatOAuthSection.spec.ts index 2f269e0bc5..5c2673424a 100644 --- a/frontend/src/components/auth/__tests__/WechatOAuthSection.spec.ts +++ b/frontend/src/components/auth/__tests__/WechatOAuthSection.spec.ts @@ -73,6 +73,7 @@ function buildPublicSettings(overrides: Partial = {}): WeC contact_info: '', doc_url: '', home_content: '', + compact_home_enabled: false, hide_ccs_import_button: false, payment_enabled: false, table_default_page_size: 20, diff --git a/frontend/src/components/user/profile/__tests__/ProfileIdentityBindingsSection.spec.ts b/frontend/src/components/user/profile/__tests__/ProfileIdentityBindingsSection.spec.ts index 3ad64861e7..c1121b1f7d 100644 --- a/frontend/src/components/user/profile/__tests__/ProfileIdentityBindingsSection.spec.ts +++ b/frontend/src/components/user/profile/__tests__/ProfileIdentityBindingsSection.spec.ts @@ -250,6 +250,7 @@ describe('ProfileIdentityBindingsSection', () => { contact_info: '', doc_url: '', home_content: '', + compact_home_enabled: false, hide_ccs_import_button: false, payment_enabled: false, table_default_page_size: 20, diff --git a/frontend/src/i18n/locales/en/admin/settings.ts b/frontend/src/i18n/locales/en/admin/settings.ts index 5e54b22214..0ee313ec38 100644 --- a/frontend/src/i18n/locales/en/admin/settings.ts +++ b/frontend/src/i18n/locales/en/admin/settings.ts @@ -548,6 +548,8 @@ export default { homeContentPlaceholder: 'Enter custom content for the home page. Supports Markdown & HTML. If a URL is entered, it will be displayed as an iframe.', homeContentHint: 'Customize the home page content. Supports Markdown/HTML. If you enter a URL (starting with http:// or https://), it will be used as an iframe src to embed an external page. When set, the default status information will no longer be displayed.', homeContentIframeWarning: '⚠️ iframe mode note: Some websites have X-Frame-Options or CSP security policies that prevent embedding in iframes. If the page appears blank or shows an error, please verify the target website allows embedding, or consider using HTML mode to build your own content.', + compactHome: 'Compact Home Page', + compactHomeHint: 'Show a restrained site identity page when no custom home page content is set.', hideCcsImportButton: 'Hide CCS Import Button', hideCcsImportButtonHint: 'When enabled, the "Import to CCS" button will be hidden on the API Keys page' }, diff --git a/frontend/src/i18n/locales/zh/admin/settings.ts b/frontend/src/i18n/locales/zh/admin/settings.ts index 4989305c4d..075a220d47 100644 --- a/frontend/src/i18n/locales/zh/admin/settings.ts +++ b/frontend/src/i18n/locales/zh/admin/settings.ts @@ -543,6 +543,8 @@ export default { '自定义首页内容,支持 Markdown/HTML。如果输入的是链接(以 http:// 或 https:// 开头),则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页。设置后首页的状态信息将不再显示。', homeContentIframeWarning: '⚠️ iframe 模式提示:部分网站设置了 X-Frame-Options 或 CSP 安全策略,禁止被嵌入到 iframe 中。如果页面显示空白或报错,请确认目标网站允许被嵌入,或考虑使用 HTML 模式自行构建页面内容。', + compactHome: '简洁首页', + compactHomeHint: '未设置自定义首页内容时,展示简洁的站点信息页面。', hideCcsImportButton: '隐藏 CCS 导入按钮', hideCcsImportButtonHint: '启用后将在 API Keys 页面隐藏"导入 CCS"按钮' }, diff --git a/frontend/src/stores/__tests__/app.spec.ts b/frontend/src/stores/__tests__/app.spec.ts index d936f1d2ed..d5cee36065 100644 --- a/frontend/src/stores/__tests__/app.spec.ts +++ b/frontend/src/stores/__tests__/app.spec.ts @@ -33,6 +33,7 @@ function createPublicSettings(overrides: Partial = {}): PublicSe contact_info: '', doc_url: '', home_content: '', + compact_home_enabled: false, hide_ccs_import_button: false, payment_enabled: false, risk_control_enabled: false, @@ -452,6 +453,7 @@ describe('useAppStore', () => { contact_info: '', doc_url: '', home_content: '', + compact_home_enabled: false, hide_ccs_import_button: false, purchase_subscription_enabled: false, purchase_subscription_url: '', diff --git a/frontend/src/stores/app.ts b/frontend/src/stores/app.ts index 9839170a29..6080ddf041 100644 --- a/frontend/src/stores/app.ts +++ b/frontend/src/stores/app.ts @@ -342,6 +342,7 @@ export const useAppStore = defineStore('app', () => { contact_info: contactInfo.value, doc_url: docUrl.value, home_content: '', + compact_home_enabled: false, hide_ccs_import_button: false, payment_enabled: false, table_default_page_size: 20, diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 31b4fd8911..fbc10f3b8e 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -210,6 +210,7 @@ export interface PublicSettings { contact_info: string doc_url: string home_content: string + compact_home_enabled: boolean hide_ccs_import_button: boolean payment_enabled: boolean risk_control_enabled: boolean diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index e01aa53a50..ddb3e87e1d 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,6 +1,6 @@