Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
155 changes: 155 additions & 0 deletions docs/MENU_CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -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
17 changes: 16 additions & 1 deletion frontend/src/components/navbar/MenuItems.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -173,37 +180,45 @@
]
},
{
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,
style: 'border:none; height: 46px; border-radius: 4px; padding: 12px 16px;',
}
]

// Filter menu items based on enabled configuration
const rawNavItems = allRawNavItems.filter(item => enabledMenusSet.has(item.id))

const allNavItems = ref(rawNavItems)

const moreItems = ref([])
Expand Down
1 change: 1 addition & 0 deletions frontend/src/views/admin/admin_next.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
const DEFAULT_TAGS = "[]";
const ON_PREMISE = "{{ .onPremise }}";
const ENABLE_HTTPS = "{{ .enableHttps }}";
const ENABLED_MENUS = "{{ .enabledMenus }}";
</script>
</head>
<body class="h-full">
Expand Down
1 change: 1 addition & 0 deletions frontend/src/views/admin/layouts/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
const DEFAULT_TAGS = "[]";
const ON_PREMISE = "{{ .onPremise }}";
const ENABLE_HTTPS = "{{ .enableHttps }}";
const ENABLED_MENUS = "{{ .enabledMenus }}";
</script>
</head>
<body class="h-full">
Expand Down
1 change: 1 addition & 0 deletions frontend/src/views/datapipelines/layouts/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}";
Expand Down
1 change: 1 addition & 0 deletions frontend/src/views/layouts/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
const DEFAULT_TAGS = "[]";
const ON_PREMISE = "{{ .onPremise }}";
const ENABLE_HTTPS = "{{ .enableHttps }}";
const ENABLED_MENUS = "{{ .enabledMenus }}";
</script>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions internal/handlers/render/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions internal/routes/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
1 change: 1 addition & 0 deletions pkg/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ type GlobalConfig struct {
ServerBaseUrl string
OnPremise bool
EnableHttps bool
EnabledMenus string
}