Просмотр исходного кода

chore: Missed a couple of files in the last commit

jamesread 3 недель назад
Родитель
Сommit
c7646166f2

+ 21 - 0
frontend/resources/vue/utils/logsFilterStorage.js

@@ -0,0 +1,21 @@
+export const LOGS_FILTER_STORAGE_KEY = 'olivetin-logs-filter'
+
+export function loadStoredLogsFilter () {
+  try {
+    return sessionStorage.getItem(LOGS_FILTER_STORAGE_KEY) || ''
+  } catch {
+    return ''
+  }
+}
+
+export function storeLogsFilter (value) {
+  try {
+    if (value) {
+      sessionStorage.setItem(LOGS_FILTER_STORAGE_KEY, value)
+    } else {
+      sessionStorage.removeItem(LOGS_FILTER_STORAGE_KEY)
+    }
+  } catch {
+    // Ignore storage failures (private mode, quota, etc.)
+  }
+}

+ 30 - 9
service/internal/api/api.go

@@ -1217,6 +1217,9 @@ func (api *oliveTinAPI) Init(ctx ctx.Context, req *connect.Request[apiv1.InitReq
 		currentVersion = installationinfo.Build.Version
 		availableVersion = installationinfo.Runtime.AvailableVersion
 	}
+
+	rootDashboardEntries := api.buildRootDashboardEntries(user, api.cfg.Dashboards)
+
 	res := &apiv1.InitResponse{
 		ShowFooter:                api.cfg.ShowFooter,
 		ShowNavigation:            api.cfg.ShowNavigation,
@@ -1232,7 +1235,8 @@ func (api *oliveTinAPI) Init(ctx ctx.Context, req *connect.Request[apiv1.InitReq
 		OAuth2Providers:           buildPublicOAuth2ProvidersList(api.cfg),
 		AdditionalLinks:           buildAdditionalLinks(api.cfg.AdditionalNavigationLinks),
 		StyleMods:                 api.cfg.StyleMods,
-		RootDashboards:            api.buildRootDashboards(user, api.cfg.Dashboards),
+		RootDashboards:            rootDashboardTitles(rootDashboardEntries),
+		RootDashboardEntries:      rootDashboardEntries,
 		AuthenticatedUser:         user.Username,
 		AuthenticatedUserProvider: user.Provider,
 		EffectivePolicy:           buildEffectivePolicy(user.EffectivePolicy),
@@ -1300,30 +1304,47 @@ func getValidThemeName(themesDir string, entry os.DirEntry) string {
 }
 
 func (api *oliveTinAPI) buildRootDashboards(user *authpublic.AuthenticatedUser, dashboards []*config.DashboardComponent) []string {
-	var rootDashboards []string
+	return rootDashboardTitles(api.buildRootDashboardEntries(user, dashboards))
+}
+
+func rootDashboardTitles(entries []*apiv1.RootDashboard) []string {
+	titles := make([]string, 0, len(entries))
+
+	for _, entry := range entries {
+		titles = append(titles, entry.Title)
+	}
+
+	return titles
+}
+
+func (api *oliveTinAPI) buildRootDashboardEntries(user *authpublic.AuthenticatedUser, dashboards []*config.DashboardComponent) []*apiv1.RootDashboard {
+	var entries []*apiv1.RootDashboard
 	dashboardRenderRequest := api.createDashboardRenderRequest(user, "", "")
 
-	api.addDefaultDashboardIfNeeded(&rootDashboards, dashboardRenderRequest)
-	api.addCustomDashboards(&rootDashboards, dashboards, dashboardRenderRequest)
+	api.addDefaultDashboardEntryIfNeeded(&entries, dashboardRenderRequest)
+	api.addCustomDashboardEntries(&entries, dashboards, dashboardRenderRequest)
 
-	return rootDashboards
+	return entries
 }
 
-func (api *oliveTinAPI) addDefaultDashboardIfNeeded(rootDashboards *[]string, rr *DashboardRenderRequest) {
+func (api *oliveTinAPI) addDefaultDashboardEntryIfNeeded(entries *[]*apiv1.RootDashboard, rr *DashboardRenderRequest) {
 	defaultDashboard := buildDefaultDashboard(rr)
 	if defaultDashboard != nil && len(defaultDashboard.Contents) > 0 {
 		log.Tracef("defaultDashboard: %+v", defaultDashboard.Contents)
-		*rootDashboards = append(*rootDashboards, "Actions")
+		*entries = append(*entries, &apiv1.RootDashboard{Title: "Actions"})
 	}
 }
 
-func (api *oliveTinAPI) addCustomDashboards(rootDashboards *[]string, dashboards []*config.DashboardComponent, rr *DashboardRenderRequest) {
+func (api *oliveTinAPI) addCustomDashboardEntries(entries *[]*apiv1.RootDashboard, dashboards []*config.DashboardComponent, rr *DashboardRenderRequest) {
 	for _, dashboard := range dashboards {
 		// We have to build the dashboard response instead of just looping over config.dashboards,
 		// because we need to check if the user has access to the dashboard
 		db := renderDashboard(rr, dashboard.Title)
 		if db != nil {
-			*rootDashboards = append(*rootDashboards, dashboard.Title)
+			*entries = append(*entries, &apiv1.RootDashboard{
+				Title:    dashboard.Title,
+				Category: dashboard.Category,
+			})
 		}
 	}
 }

+ 55 - 0
service/internal/api/dashboard_acl_test.go

@@ -68,6 +68,61 @@ func TestDashboardAclsRootNavAndGetDashboard(t *testing.T) {
 	assert.Equal(t, "Services", db.Title)
 }
 
+func TestRootDashboardEntriesIncludeCategory(t *testing.T) {
+	cfg := config.DefaultConfig()
+	cfg.Dashboards = []*config.DashboardComponent{
+		{
+			Title:    "Misc Tools",
+			Contents: []*config.DashboardComponent{{Title: "Hello", Type: "display"}},
+		},
+		{
+			Title:    "My Servers",
+			Category: "Infrastructure",
+			Contents: []*config.DashboardComponent{{Title: "Ping", Type: "display"}},
+		},
+		{
+			Title:    "Status Board",
+			Category: "Monitoring",
+			Contents: []*config.DashboardComponent{{Title: "Uptime", Type: "display"}},
+		},
+		{
+			Title:    "My Containers",
+			Category: "Infrastructure",
+			Contents: []*config.DashboardComponent{{Title: "Restart", Type: "display"}},
+		},
+	}
+
+	ex := executor.DefaultExecutor(cfg)
+	api := newServer(ex)
+	user := &authpublic.AuthenticatedUser{Username: "guest", Provider: "system"}
+	user.BuildUserAcls(cfg)
+
+	entries := api.buildRootDashboardEntries(user, cfg.Dashboards)
+	require.Len(t, entries, 4)
+	assert.Equal(t, []string{"Misc Tools", "My Servers", "Status Board", "My Containers"}, rootDashboardTitles(entries))
+	assert.Equal(t, "", entries[0].Category)
+	assert.Equal(t, "Infrastructure", entries[1].Category)
+	assert.Equal(t, "Monitoring", entries[2].Category)
+	assert.Equal(t, "Infrastructure", entries[3].Category)
+}
+
+func TestRootDashboardEntriesOmitAclHiddenCategories(t *testing.T) {
+	cfg := buildDashboardAclTestConfig()
+	cfg.Dashboards[0].Category = "Public"
+	cfg.Dashboards[1].Category = "Admin only"
+
+	ex := executor.DefaultExecutor(cfg)
+	api := newServer(ex)
+
+	guest := &authpublic.AuthenticatedUser{Username: "guest", Provider: "system"}
+	guest.BuildUserAcls(cfg)
+
+	entries := api.buildRootDashboardEntries(guest, cfg.Dashboards)
+	require.Len(t, entries, 1)
+	assert.Equal(t, "Public tools", entries[0].Title)
+	assert.Equal(t, "Public", entries[0].Category)
+}
+
 func TestDashboardAclsNestedDirectoryDeepLink(t *testing.T) {
 	cfg := buildDashboardAclTestConfig()
 	cfg.Dashboards = []*config.DashboardComponent{

+ 47 - 0
service/internal/config/port_env_test.go

@@ -0,0 +1,47 @@
+package config
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestApplyPortEnvironmentOverride(t *testing.T) {
+	t.Setenv("PORT", "8080")
+
+	cfg := DefaultConfig()
+	cfg.ListenAddressSingleHTTPFrontend = "0.0.0.0:1337"
+	applyPortEnvironmentOverride(cfg)
+
+	assert.Equal(t, "0.0.0.0:8080", cfg.ListenAddressSingleHTTPFrontend)
+}
+
+func TestApplyPortEnvironmentOverridePreservesHost(t *testing.T) {
+	t.Setenv("PORT", "9000")
+
+	cfg := DefaultConfig()
+	cfg.ListenAddressSingleHTTPFrontend = "127.0.0.1:1337"
+	applyPortEnvironmentOverride(cfg)
+
+	assert.Equal(t, "127.0.0.1:9000", cfg.ListenAddressSingleHTTPFrontend)
+}
+
+func TestApplyPortEnvironmentOverrideUnsetLeavesConfig(t *testing.T) {
+	t.Setenv("PORT", "")
+
+	cfg := DefaultConfig()
+	cfg.ListenAddressSingleHTTPFrontend = "0.0.0.0:2337"
+	applyPortEnvironmentOverride(cfg)
+
+	assert.Equal(t, "0.0.0.0:2337", cfg.ListenAddressSingleHTTPFrontend)
+}
+
+func TestApplyPortEnvironmentOverrideIgnoresInvalid(t *testing.T) {
+	t.Setenv("PORT", "not-a-port")
+
+	cfg := DefaultConfig()
+	cfg.ListenAddressSingleHTTPFrontend = "0.0.0.0:1337"
+	applyPortEnvironmentOverride(cfg)
+
+	assert.Equal(t, "0.0.0.0:1337", cfg.ListenAddressSingleHTTPFrontend)
+}