diff --git a/service/internal/api/api.go b/service/internal/api/api.go index d336235..c7ee481 100644 --- a/service/internal/api/api.go +++ b/service/internal/api/api.go @@ -919,7 +919,6 @@ func discoverAvailableThemes(cfg *config.Config) []string { } themesDir := path.Join(configDir, "custom-webui", "themes") - entries, err := os.ReadDir(themesDir) if err != nil { log.WithFields(log.Fields{ @@ -929,24 +928,38 @@ func discoverAvailableThemes(cfg *config.Config) []string { return []string{} } + themes := collectValidThemes(themesDir, entries) + sort.Strings(themes) + return themes +} + +// collectValidThemes collects theme names from directory entries that have a theme.css file. +func collectValidThemes(themesDir string, entries []os.DirEntry) []string { var themes []string for _, entry := range entries { - if !entry.IsDir() { - continue - } - - themeName := entry.Name() - themeCssPath := path.Join(themesDir, themeName, "theme.css") - - if _, err := os.Stat(themeCssPath); err == nil { + if themeName := getValidThemeName(themesDir, entry); themeName != "" { themes = append(themes, themeName) } } - - sort.Strings(themes) return themes } +// getValidThemeName returns the theme name if the entry is a valid theme directory with theme.css, otherwise returns empty string. +func getValidThemeName(themesDir string, entry os.DirEntry) string { + if !entry.IsDir() { + return "" + } + + themeName := entry.Name() + themeCssPath := path.Join(themesDir, themeName, "theme.css") + + if _, err := os.Stat(themeCssPath); err != nil { + return "" + } + + return themeName +} + func (api *oliveTinAPI) buildRootDashboards(user *authpublic.AuthenticatedUser, dashboards []*config.DashboardComponent) []string { var rootDashboards []string dashboardRenderRequest := api.createDashboardRenderRequest(user, "", "") diff --git a/service/internal/executor/executor.go b/service/internal/executor/executor.go index 2673e7b..96808f3 100644 --- a/service/internal/executor/executor.go +++ b/service/internal/executor/executor.go @@ -239,36 +239,12 @@ func (e *Executor) filterLogsByACL(cfg *config.Config, user *authpublic.Authenti defer e.logmutex.RUnlock() filtered := make([]*InternalLogEntry, 0, len(e.logsTrackingIdsByDate)) - - var filterDate time.Time - var hasDateFilter bool - if dateFilter != "" { - parsedDate, err := time.Parse("2006-01-02", dateFilter) - if err != nil { - log.WithFields(log.Fields{ - "dateFilter": dateFilter, - "error": err, - }).Errorf("Failed to parse date filter, expected format YYYY-MM-DD") - } else { - filterDate = parsedDate - hasDateFilter = true - } - } + filterDate, hasDateFilter := parseDateFilter(dateFilter) for _, trackingId := range e.logsTrackingIdsByDate { entry := e.logs[trackingId] - if !isValidLogEntryForACL(entry) { - continue - } - if isLogEntryAllowedByACL(cfg, user, entry) { - if hasDateFilter { - entryDate := entry.DatetimeStarted.UTC().Truncate(24 * time.Hour) - filterDateUTC := filterDate.UTC().Truncate(24 * time.Hour) - if !entryDate.Equal(filterDateUTC) { - continue - } - } + if shouldIncludeLogEntry(cfg, user, entry, filterDate, hasDateFilter) { filtered = append(filtered, entry) } } @@ -276,6 +252,48 @@ func (e *Executor) filterLogsByACL(cfg *config.Config, user *authpublic.Authenti return filtered } +// parseDateFilter parses the date filter string and returns filter information. +func parseDateFilter(dateFilter string) (filterDate time.Time, hasDateFilter bool) { + if dateFilter == "" { + return time.Time{}, false + } + + parsedDate, err := time.Parse("2006-01-02", dateFilter) + if err != nil { + log.WithFields(log.Fields{ + "dateFilter": dateFilter, + "error": err, + }).Errorf("Failed to parse date filter, expected format YYYY-MM-DD") + return time.Time{}, false + } + + return parsedDate, true +} + +// shouldIncludeLogEntry determines if a log entry should be included based on ACL and date filter. +func shouldIncludeLogEntry(cfg *config.Config, user *authpublic.AuthenticatedUser, entry *InternalLogEntry, filterDate time.Time, hasDateFilter bool) bool { + if !isValidLogEntryForACL(entry) { + return false + } + + if !isLogEntryAllowedByACL(cfg, user, entry) { + return false + } + + return matchesDateFilter(entry, filterDate, hasDateFilter) +} + +// matchesDateFilter checks if the log entry matches the date filter. +func matchesDateFilter(entry *InternalLogEntry, filterDate time.Time, hasDateFilter bool) bool { + if !hasDateFilter { + return true + } + + entryDate := entry.DatetimeStarted.UTC().Truncate(24 * time.Hour) + filterDateUTC := filterDate.UTC().Truncate(24 * time.Hour) + return entryDate.Equal(filterDateUTC) +} + // paginateFilteredLogs applies pagination to a filtered list of logs and returns // the paginated results along with pagination metadata. func paginateFilteredLogs(filtered []*InternalLogEntry, startOffset int64, pageCount int64) ([]*InternalLogEntry, *PagingResult) {