From 4b4300a78272fe99aa8dd2aacf687be45b47cb10 Mon Sep 17 00:00:00 2001 From: frankfika Date: Fri, 24 Jul 2026 23:44:05 +0800 Subject: [PATCH] feat: add configuration for enabling/disabling top-level menu items - Add CSGHUB_PORTAL_ENABLED_MENUS env var to control menu visibility - Support comma-separated list of menu IDs (models,datasets,mcp,skills,spaces,codes,collections,prompts) - Default to all menus enabled if not configured - Update backend config and templates to pass enabled menus to frontend - Update MenuItems.vue to filter menus based on configuration - Add comprehensive documentation in docs/MENU_CONFIGURATION.md Fixes OpenCSGs/csghub#1525 --- .env.example | 4 + config/config.go | 1 + docs/MENU_CONFIGURATION.md | 155 ++++++++++++++++++ frontend/src/components/navbar/MenuItems.vue | 17 +- frontend/src/views/admin/admin_next.html | 1 + frontend/src/views/admin/layouts/base.html | 1 + .../src/views/datapipelines/layouts/base.html | 1 + frontend/src/views/layouts/base.html | 1 + internal/handlers/render/base.go | 1 + internal/routes/router.go | 1 + pkg/types/constants.go | 1 + 11 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 docs/MENU_CONFIGURATION.md diff --git a/.env.example b/.env.example index 2106f7d84..ede2618f3 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,10 @@ CSGHUB_PORTAL_ENABLE_HTTPS=true CSGHUB_PORTAL_SERVER_PORT=8090 # A comma-separated list of superuser usernames. CSGHUB_PORTAL_SUPER_USERS= +# A comma-separated list of enabled top-level menu items. +# Available options: models, datasets, mcp, skills, spaces, codes, collections, prompts +# Default: all menus are enabled +CSGHUB_PORTAL_ENABLED_MENUS=models,datasets,mcp,skills,spaces,codes,collections,prompts # csghub-server API Configuration # The URL of the csghub-server service. diff --git a/config/config.go b/config/config.go index 83592a8ff..13520a9bc 100644 --- a/config/config.go +++ b/config/config.go @@ -20,6 +20,7 @@ type Config struct { EnableHttps bool `envconfig:"CSGHUB_PORTAL_ENABLE_HTTPS" default:"true"` SignupURL string `envconfig:"CSGHUB_PORTAL_SIGNUP_URL" default:""` LoginURL string `envconfig:"CSGHUB_PORTAL_LOGIN_URL" default:"http://60.205.228.141:8000/login/oauth/authorize?client_id=4ba65e712c5dabca3e48&response_type=code&redirect_uri=http://localhost:8080/api/v1/callback/casdoor&scope=read&state=casdoor"` + EnabledMenus string `envconfig:"CSGHUB_PORTAL_ENABLED_MENUS" default:"models,datasets,mcp,skills,spaces,codes,collections,prompts"` StarhubServer struct { BaseURL string `envconfig:"CSGHUB_PORTAL_STARHUB_BASE_URL" default:"http://localhost:8080"` diff --git a/docs/MENU_CONFIGURATION.md b/docs/MENU_CONFIGURATION.md new file mode 100644 index 000000000..344ce75da --- /dev/null +++ b/docs/MENU_CONFIGURATION.md @@ -0,0 +1,155 @@ +# Menu Configuration + +## Feature: Enable/Disable Top-Level Menu Items + +This feature allows administrators to control which top-level menu items appear in the CSGHub portal navigation bar through environment variable configuration. + +## Configuration + +Add the following environment variable to your `.env` file: + +```bash +CSGHUB_PORTAL_ENABLED_MENUS=models,datasets,mcp,skills,spaces,codes,collections,prompts +``` + +### Available Menu Items + +The following menu items can be enabled or disabled: + +- `models` - Models repository +- `datasets` - Datasets repository +- `mcp` - MCP Servers (with sub-items: MCP Servers and MCP Tools) +- `skills` - Skills repository +- `spaces` - Application Spaces +- `codes` - Code repository +- `collections` - Collections +- `prompts` - Prompt Library + +### Default Behavior + +If `CSGHUB_PORTAL_ENABLED_MENUS` is not set, all menu items are enabled by default. + +## Examples + +### Enable Only Models and Datasets + +```bash +CSGHUB_PORTAL_ENABLED_MENUS=models,datasets +``` + +This will show only the "Models" and "Datasets" menu items in the navigation bar. + +### Enable Core Features Only + +```bash +CSGHUB_PORTAL_ENABLED_MENUS=models,datasets,spaces,codes +``` + +This will hide MCP, Skills, Collections, and Prompts from the menu. + +### SaaS vs On-Premise + +For SaaS deployments, you might want to limit certain features: + +```bash +CSGHUB_PORTAL_ON_PREMISE=false +CSGHUB_PORTAL_ENABLED_MENUS=models,datasets,spaces +``` + +For on-premise deployments, you might enable all features: + +```bash +CSGHUB_PORTAL_ON_PREMISE=true +CSGHUB_PORTAL_ENABLED_MENUS=models,datasets,mcp,skills,spaces,codes,collections,prompts +``` + +## Implementation Details + +### Backend Changes + +1. **config/config.go**: Added `EnabledMenus` field to Config struct +2. **pkg/types/constants.go**: Added `EnabledMenus` field to GlobalConfig +3. **internal/routes/router.go**: Pass EnabledMenus to GlobalConfig +4. **internal/handlers/render/base.go**: Include enabledMenus in template data + +### Frontend Changes + +1. **Template Files**: Added `ENABLED_MENUS` constant to all base.html templates + - frontend/src/views/layouts/base.html + - frontend/src/views/admin/layouts/base.html + - frontend/src/views/admin/admin_next.html + - frontend/src/views/datapipelines/layouts/base.html + +2. **MenuItems.vue**: Updated to filter menu items based on configuration + - Added unique `id` field to each menu item + - Parse `ENABLED_MENUS` from global configuration + - Filter menu items using Set for O(1) lookup + +## Testing + +### Manual Testing + +1. Set the environment variable: + ```bash + export CSGHUB_PORTAL_ENABLED_MENUS=models,datasets + ``` + +2. Start the application: + ```bash + make run + ``` + +3. Open the portal in a browser and verify that only Models and Datasets menus appear + +4. Try different combinations: + ```bash + # Test with single menu + export CSGHUB_PORTAL_ENABLED_MENUS=models + + # Test with all menus (default) + export CSGHUB_PORTAL_ENABLED_MENUS=models,datasets,mcp,skills,spaces,codes,collections,prompts + + # Test with empty (should show all) + unset CSGHUB_PORTAL_ENABLED_MENUS + ``` + +### Expected Behavior + +- Menu items not in the enabled list should not appear in the navigation bar +- The responsive "More" menu should still work correctly for smaller screens +- Submenu items (like MCP sub-items) should appear when their parent is enabled +- Direct URL access to disabled menu pages is still possible (this is navigation-only filtering) + +## Notes + +- This feature only affects the visibility of menu items in the navigation bar +- Users can still access pages directly via URL even if the menu item is hidden +- For complete feature disabling, additional backend access control is required +- The menu filtering is done on the frontend for better performance +- No database changes are required for this feature + +## Troubleshooting + +### Menus not appearing after configuration change + +1. Restart the backend service to reload environment variables +2. Clear browser cache +3. Verify the environment variable is correctly set: `env | grep ENABLED_MENUS` + +### All menus disappear + +Check if the environment variable has correct syntax: +- Values should be comma-separated +- No spaces around commas +- Use exact menu IDs (case-sensitive) +- If in doubt, unset the variable to restore defaults + +## Future Enhancements + +Potential improvements for this feature: + +1. Add UI configuration panel for administrators +2. Per-user or per-role menu customization +3. Database-backed configuration instead of environment variables +4. Menu item ordering configuration +5. Backend route access control to enforce menu visibility at API level diff --git a/frontend/src/components/navbar/MenuItems.vue b/frontend/src/components/navbar/MenuItems.vue index 67456d55c..2cb38b250 100644 --- a/frontend/src/components/navbar/MenuItems.vue +++ b/frontend/src/components/navbar/MenuItems.vue @@ -132,20 +132,27 @@ hasEmail: Boolean }) - const rawNavItems = [ + // Get enabled menus from global config + const enabledMenusStr = typeof ENABLED_MENUS !== 'undefined' ? ENABLED_MENUS : 'models,datasets,mcp,skills,spaces,codes,collections,prompts' + const enabledMenusSet = new Set(enabledMenusStr.split(',').map(item => item.trim()).filter(Boolean)) + + const allRawNavItems = [ { + id: 'models', title: t('navbar.models'), index: '/models', class: menuItemClass, style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;', }, { + id: 'datasets', title: t('navbar.datasets'), index: '/datasets', class: menuItemClass, style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;', }, { + id: 'mcp', title: t('navbar.mcp'), index: '/mcp/servers', class: menuItemClass, @@ -173,30 +180,35 @@ ] }, { + id: 'skills', title: t('navbar.skills'), index: '/skills', class: menuItemClass, style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;', }, { + id: 'spaces', title: t('navbar.spaces'), index: '/spaces', class: menuItemClass, style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;', }, { + id: 'codes', title: t('navbar.codes'), index: '/codes', class: menuItemClass, style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;', }, { + id: 'collections', title: t('collections.collection'), index: '/collections', class: menuItemClass, style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;', }, { + id: 'prompts', title: t('prompts.promptLibrary'), index: '/prompts/library', class: menuItemClass, @@ -204,6 +216,9 @@ } ] + // Filter menu items based on enabled configuration + const rawNavItems = allRawNavItems.filter(item => enabledMenusSet.has(item.id)) + const allNavItems = ref(rawNavItems) const moreItems = ref([]) diff --git a/frontend/src/views/admin/admin_next.html b/frontend/src/views/admin/admin_next.html index 5e2d68cfe..4fba03344 100644 --- a/frontend/src/views/admin/admin_next.html +++ b/frontend/src/views/admin/admin_next.html @@ -12,6 +12,7 @@ const DEFAULT_TAGS = "[]"; const ON_PREMISE = "{{ .onPremise }}"; const ENABLE_HTTPS = "{{ .enableHttps }}"; + const ENABLED_MENUS = "{{ .enabledMenus }}"; diff --git a/frontend/src/views/admin/layouts/base.html b/frontend/src/views/admin/layouts/base.html index daa56b485..8420a04f1 100644 --- a/frontend/src/views/admin/layouts/base.html +++ b/frontend/src/views/admin/layouts/base.html @@ -12,6 +12,7 @@ const DEFAULT_TAGS = "[]"; const ON_PREMISE = "{{ .onPremise }}"; const ENABLE_HTTPS = "{{ .enableHttps }}"; + const ENABLED_MENUS = "{{ .enabledMenus }}"; diff --git a/frontend/src/views/datapipelines/layouts/base.html b/frontend/src/views/datapipelines/layouts/base.html index 1b0d0c714..20eeba430 100644 --- a/frontend/src/views/datapipelines/layouts/base.html +++ b/frontend/src/views/datapipelines/layouts/base.html @@ -28,6 +28,7 @@ const CSGHUB_SERVER = '{{ .csghubServer }}' const ON_PREMISE = '{{ .onPremise }}' const ENABLE_HTTPS = '{{ .enableHttps }}' + const ENABLED_MENUS = '{{ .enabledMenus }}' const THEME = '{{ .theme }}' const BUILD_EDITION = "{{.buildEdition }}"; const REGION = "{{.region }}"; diff --git a/frontend/src/views/layouts/base.html b/frontend/src/views/layouts/base.html index 4bbf733c6..8e4aaf351 100644 --- a/frontend/src/views/layouts/base.html +++ b/frontend/src/views/layouts/base.html @@ -13,6 +13,7 @@ const DEFAULT_TAGS = "[]"; const ON_PREMISE = "{{ .onPremise }}"; const ENABLE_HTTPS = "{{ .enableHttps }}"; + const ENABLED_MENUS = "{{ .enabledMenus }}"; diff --git a/internal/handlers/render/base.go b/internal/handlers/render/base.go index cbb32a568..66e4dcd85 100644 --- a/internal/handlers/render/base.go +++ b/internal/handlers/render/base.go @@ -43,6 +43,7 @@ func createTemplateData(ctx *gin.Context, extraData map[string]interface{}) gin. "csghubServer": config.ServerBaseUrl, "onPremise": config.OnPremise, "enableHttps": config.EnableHttps, + "enabledMenus": config.EnabledMenus, "currentUser": currentUser, "isLoggedIn": isLoggedIn, "metaTitle": DEFAULT_META_TITLE, diff --git a/internal/routes/router.go b/internal/routes/router.go index 93a56aaa7..9fe810a7a 100644 --- a/internal/routes/router.go +++ b/internal/routes/router.go @@ -244,6 +244,7 @@ func setupViewsRouter(engine *gin.Engine, handlersRegistry *HandlersRegistry) { ServerBaseUrl: handlersRegistry.Config.StarhubServer.BaseURL, OnPremise: handlersRegistry.Config.OnPremise, EnableHttps: handlersRegistry.Config.EnableHttps, + EnabledMenus: handlersRegistry.Config.EnabledMenus, } engine.Use(injectConfig(globalConfig)) diff --git a/pkg/types/constants.go b/pkg/types/constants.go index 15a41327b..07fe5e9d9 100644 --- a/pkg/types/constants.go +++ b/pkg/types/constants.go @@ -4,4 +4,5 @@ type GlobalConfig struct { ServerBaseUrl string OnPremise bool EnableHttps bool + EnabledMenus string }