Procházet zdrojové kódy

fix: Massively improve config loading

jamesread před 8 měsíci
rodič
revize
7dc99b1398
2 změnil soubory, kde provedl 166 přidání a 372 odebrání
  1. 147 147
      service/internal/config/config.go
  2. 19 225
      service/internal/config/config_reloader.go

+ 147 - 147
service/internal/config/config.go

@@ -7,212 +7,212 @@ import (
 // Action represents the core functionality of OliveTin - commands that show up
 // as buttons in the UI.
 type Action struct {
-	ID                     string
-	Title                  string
-	Icon                   string
-	Shell                  string
-	Exec                   []string
-	ShellAfterCompleted    string
-	Timeout                int
-	Acls                   []string
-	Entity                 string
-	Hidden                 bool
-	ExecOnStartup          bool
-	ExecOnCron             []string
-	ExecOnFileCreatedInDir []string
-	ExecOnFileChangedInDir []string
-	ExecOnCalendarFile     string
-	Triggers               []string
-	MaxConcurrent          int
-	MaxRate                []RateSpec
-	Arguments              []ActionArgument
-	PopupOnStart           string
-	SaveLogs               SaveLogsConfig
+	ID                     string           `koanf:"id"`
+	Title                  string           `koanf:"title"`
+	Icon                   string           `koanf:"icon"`
+	Shell                  string           `koanf:"shell"`
+	Exec                   []string         `koanf:"exec"`
+	ShellAfterCompleted    string           `koanf:"shellAfterCompleted"`
+	Timeout                int              `koanf:"timeout"`
+	Acls                   []string         `koanf:"acls"`
+	Entity                 string           `koanf:"entity"`
+	Hidden                 bool             `koanf:"hidden"`
+	ExecOnStartup          bool             `koanf:"execOnStartup"`
+	ExecOnCron             []string         `koanf:"execOnCron"`
+	ExecOnFileCreatedInDir []string         `koanf:"execOnFileCreatedInDir"`
+	ExecOnFileChangedInDir []string         `koanf:"execOnFileChangedInDir"`
+	ExecOnCalendarFile     string           `koanf:"execOnCalendarFile"`
+	Triggers               []string         `koanf:"triggers"`
+	MaxConcurrent          int              `koanf:"maxConcurrent"`
+	MaxRate                []RateSpec       `koanf:"maxRate"`
+	Arguments              []ActionArgument `koanf:"arguments"`
+	PopupOnStart           string           `koanf:"popupOnStart"`
+	SaveLogs               SaveLogsConfig   `koanf:"saveLogs"`
 }
 
 // ActionArgument objects appear on Actions.
 type ActionArgument struct {
-	Name        string
-	Title       string
-	Description string
-	Type        string
-	Default     string
-	Choices     []ActionArgumentChoice
-	Entity      string
-	RejectNull  bool
-	Suggestions map[string]string
+	Name        string                 `koanf:"name"`
+	Title       string                 `koanf:"title"`
+	Description string                 `koanf:"description"`
+	Type        string                 `koanf:"type"`
+	Default     string                 `koanf:"default"`
+	Choices     []ActionArgumentChoice `koanf:"choices"`
+	Entity      string                 `koanf:"entity"`
+	RejectNull  bool                   `koanf:"rejectNull"`
+	Suggestions map[string]string      `koanf:"suggestions"`
 }
 
 // ActionArgumentChoice represents a predefined choice for an argument.
 type ActionArgumentChoice struct {
-	Value string
-	Title string
+	Value string `koanf:"value"`
+	Title string `koanf:"title"`
 }
 
 // RateSpec allows you to set a max frequency for an action.
 type RateSpec struct {
-	Limit    int
-	Duration string
+	Limit    int    `koanf:"limit"`
+	Duration string `koanf:"duration"`
 }
 
 // Entity represents a "thing" that can have multiple actions associated with it.
 // for example, a media player with a start and stop action.
 type EntityFile struct {
-	File string
-	Name string
-	Icon string
+	File string `koanf:"file"`
+	Name string `koanf:"name"`
+	Icon string `koanf:"icon"`
 }
 
 // PermissionsList defines what users can do with an action.
 type PermissionsList struct {
-	View bool `mapstructure:"view"`
-	Exec bool `mapstructure:"exec"`
-	Logs bool `mapstructure:"logs"`
-	Kill bool `mapstructure:"kill"`
+	View bool `koanf:"view"`
+	Exec bool `koanf:"exec"`
+	Logs bool `koanf:"logs"`
+	Kill bool `koanf:"kill"`
 }
 
 // AccessControlList defines what permissions apply to a user or user group.
 type AccessControlList struct {
-	Name             string
-	AddToEveryAction bool
-	MatchUsergroups  []string
-	MatchUsernames   []string
-	Permissions      PermissionsList
-	Policy           ConfigurationPolicy
+	Name             string              `koanf:"name"`
+	AddToEveryAction bool                `koanf:"addToEveryAction"`
+	MatchUsergroups  []string            `koanf:"matchUsergroups"`
+	MatchUsernames   []string            `koanf:"matchUsernames"`
+	Permissions      PermissionsList     `koanf:"permissions"`
+	Policy           ConfigurationPolicy `koanf:"policy"`
 }
 
 // ConfigurationPolicy defines global settings which are overridden with an ACL.
 type ConfigurationPolicy struct {
-	ShowDiagnostics bool `mapstructure:"showDiagnostics"`
-	ShowLogList     bool `mapstructure:"showLogList"`
+	ShowDiagnostics bool `koanf:"showDiagnostics"`
+	ShowLogList     bool `koanf:"showLogList"`
 }
 
 type PrometheusConfig struct {
-	Enabled          bool `mapstructure:"enabled"`
-	DefaultGoMetrics bool `mapstructure:"defaultGoMetrics"`
+	Enabled          bool `koanf:"enabled"`
+	DefaultGoMetrics bool `koanf:"defaultGoMetrics"`
 }
 
 // Config is the global config used through the whole app.
 type Config struct {
-	UseSingleHTTPFrontend           bool                       `mapstructure:"useSingleHTTPFrontend"`
-	ThemeName                       string                     `mapstructure:"themeName"`
-	ThemeCacheDisabled              bool                       `mapstructure:"themeCacheDisabled"`
-	ListenAddressSingleHTTPFrontend string                     `mapstructure:"listenAddressSingleHTTPFrontend"`
-	ListenAddressWebUI              string                     `mapstructure:"listenAddressWebUI"`
-	ListenAddressRestActions        string                     `mapstructure:"listenAddressRestActions"`
-	ListenAddressPrometheus         string                     `mapstructure:"listenAddressPrometheus"`
-	ExternalRestAddress             string                     `mapstructure:"externalRestAddress"`
-	LogLevel                        string                     `mapstructure:"logLevel"`
-	LogDebugOptions                 LogDebugOptions            `mapstructure:"logDebugOptions"`
-	LogHistoryPageSize              int64                      `mapstructure:"logHistoryPageSize"`
-	Actions                         []*Action                  `mapstructure:"actions"`
-	Entities                        []*EntityFile              `mapstructure:"entities"`
-	Dashboards                      []*DashboardComponent      `mapstructure:"dashboards"`
-	CheckForUpdates                 bool                       `mapstructure:"checkForUpdates"`
-	PageTitle                       string                     `mapstructure:"pageTitle"`
-	ShowFooter                      bool                       `mapstructure:"showFooter"`
-	ShowNavigation                  bool                       `mapstructure:"showNavigation"`
-	ShowNewVersions                 bool                       `mapstructure:"showNewVersions"`
-	EnableCustomJs                  bool                       `mapstructure:"enableCustomJs"`
-	AuthJwtCookieName               string                     `mapstructure:"authJwtCookieName"`
-	AuthJwtHeader                   string                     `mapstructure:"authJwtHeader"`
-	AuthJwtAud                      string                     `mapstructure:"authJwtAud"`
-	AuthJwtDomain                   string                     `mapstructure:"authJwtDomain"`
-	AuthJwtCertsURL                 string                     `mapstructure:"authJwtCertsUrl"`
-	AuthJwtHmacSecret               string                     `mapstructure:"authJwtHmacSecret"` // mutually exclusive with pub key config fields
-	AuthJwtClaimUsername            string                     `mapstructure:"authJwtClaimUsername"`
-	AuthJwtClaimUserGroup           string                     `mapstructure:"authJwtClaimUserGroup"`
-	AuthJwtPubKeyPath               string                     `mapstructure:"authJwtPubKeyPath"` // will read pub key from file on disk
-	AuthHttpHeaderUsername          string                     `mapstructure:"authHttpHeaderUsername"`
-	AuthHttpHeaderUserGroup         string                     `mapstructure:"authHttpHeaderUserGroup"`
-	AuthHttpHeaderUserGroupSep      string                     `mapstructure:"authHttpHeaderUserGroupSep"`
-	AuthLocalUsers                  AuthLocalUsersConfig       `mapstructure:"authLocalUsers"`
-	AuthLoginUrl                    string                     `mapstructure:"authLoginUrl"`
-	AuthRequireGuestsToLogin        bool                       `mapstructure:"authRequireGuestsToLogin"`
-	AuthOAuth2RedirectURL           string                     `mapstructure:"authOAuth2RedirectUrl"`
-	AuthOAuth2Providers             map[string]*OAuth2Provider `mapstructure:"authOAuth2Providers"`
-	DefaultPermissions              PermissionsList            `mapstructure:"defaultPermissions"`
-	DefaultPolicy                   ConfigurationPolicy        `mapstructure:"defaultPolicy"`
-	AccessControlLists              []*AccessControlList       `mapstructure:"accessControlLists"`
-	WebUIDir                        string                     `mapstructure:"webUIDir"`
-	CronSupportForSeconds           bool                       `mapstructure:"cronSupportForSeconds"`
-	SectionNavigationStyle          string                     `mapstructure:"sectionNavigationStyle"`
-	DefaultPopupOnStart             string                     `mapstructure:"defaultPopupOnStart"`
-	InsecureAllowDumpOAuth2UserData bool                       `mapstructure:"insecureAllowDumpOAuth2UserData"`
-	InsecureAllowDumpVars           bool                       `mapstructure:"insecureAllowDumpVars"`
-	InsecureAllowDumpSos            bool                       `mapstructure:"insecureAllowDumpSos"`
-	InsecureAllowDumpActionMap      bool                       `mapstructure:"insecureAllowDumpActionMap"`
-	InsecureAllowDumpJwtClaims      bool                       `mapstructure:"insecureAllowDumpJwtClaims"`
-	Prometheus                      PrometheusConfig           `mapstructure:"prometheus"`
-	SaveLogs                        SaveLogsConfig             `mapstructure:"saveLogs"`
-	DefaultIconForActions           string                     `mapstructure:"defaultIconForActions"`
-	DefaultIconForDirectories       string                     `mapstructure:"defaultIconForDirectories"`
-	DefaultIconForBack              string                     `mapstructure:"defaultIconForBack"`
-	AdditionalNavigationLinks       []*NavigationLink          `mapstructure:"additionalNavigationLinks"`
-	ServiceHostMode                 string                     `mapstructure:"serviceHostMode"`
-	StyleMods                       []string                   `mapstructure:"styleMods"`
-	BannerMessage                   string                     `mapstructure:"bannerMessage"`
-	BannerCSS                       string                     `mapstructure:"bannerCss"`
-	Include                         string                     `mapstructure:"include"`
+	UseSingleHTTPFrontend           bool                       `koanf:"useSingleHTTPFrontend"`
+	ThemeName                       string                     `koanf:"themeName"`
+	ThemeCacheDisabled              bool                       `koanf:"themeCacheDisabled"`
+	ListenAddressSingleHTTPFrontend string                     `koanf:"listenAddressSingleHTTPFrontend"`
+	ListenAddressWebUI              string                     `koanf:"listenAddressWebUI"`
+	ListenAddressRestActions        string                     `koanf:"listenAddressRestActions"`
+	ListenAddressPrometheus         string                     `koanf:"listenAddressPrometheus"`
+	ExternalRestAddress             string                     `koanf:"externalRestAddress"`
+	LogLevel                        string                     `koanf:"logLevel"`
+	LogDebugOptions                 LogDebugOptions            `koanf:"logDebugOptions"`
+	LogHistoryPageSize              int64                      `koanf:"logHistoryPageSize"`
+	Actions                         []*Action                  `koanf:"actions"`
+	Entities                        []*EntityFile              `koanf:"entities"`
+	Dashboards                      []*DashboardComponent      `koanf:"dashboards"`
+	CheckForUpdates                 bool                       `koanf:"checkForUpdates"`
+	PageTitle                       string                     `koanf:"pageTitle"`
+	ShowFooter                      bool                       `koanf:"showFooter"`
+	ShowNavigation                  bool                       `koanf:"showNavigation"`
+	ShowNewVersions                 bool                       `koanf:"showNewVersions"`
+	EnableCustomJs                  bool                       `koanf:"enableCustomJs"`
+	AuthJwtCookieName               string                     `koanf:"authJwtCookieName"`
+	AuthJwtHeader                   string                     `koanf:"authJwtHeader"`
+	AuthJwtAud                      string                     `koanf:"authJwtAud"`
+	AuthJwtDomain                   string                     `koanf:"authJwtDomain"`
+	AuthJwtCertsURL                 string                     `koanf:"authJwtCertsUrl"`
+	AuthJwtHmacSecret               string                     `koanf:"authJwtHmacSecret"` // mutually exclusive with pub key config fields
+	AuthJwtClaimUsername            string                     `koanf:"authJwtClaimUsername"`
+	AuthJwtClaimUserGroup           string                     `koanf:"authJwtClaimUserGroup"`
+	AuthJwtPubKeyPath               string                     `koanf:"authJwtPubKeyPath"` // will read pub key from file on disk
+	AuthHttpHeaderUsername          string                     `koanf:"authHttpHeaderUsername"`
+	AuthHttpHeaderUserGroup         string                     `koanf:"authHttpHeaderUserGroup"`
+	AuthHttpHeaderUserGroupSep      string                     `koanf:"authHttpHeaderUserGroupSep"`
+	AuthLocalUsers                  AuthLocalUsersConfig       `koanf:"authLocalUsers"`
+	AuthLoginUrl                    string                     `koanf:"authLoginUrl"`
+	AuthRequireGuestsToLogin        bool                       `koanf:"authRequireGuestsToLogin"`
+	AuthOAuth2RedirectURL           string                     `koanf:"authOAuth2RedirectUrl"`
+	AuthOAuth2Providers             map[string]*OAuth2Provider `koanf:"authOAuth2Providers"`
+	DefaultPermissions              PermissionsList            `koanf:"defaultPermissions"`
+	DefaultPolicy                   ConfigurationPolicy        `koanf:"defaultPolicy"`
+	AccessControlLists              []*AccessControlList       `koanf:"accessControlLists"`
+	WebUIDir                        string                     `koanf:"webUIDir"`
+	CronSupportForSeconds           bool                       `koanf:"cronSupportForSeconds"`
+	SectionNavigationStyle          string                     `koanf:"sectionNavigationStyle"`
+	DefaultPopupOnStart             string                     `koanf:"defaultPopupOnStart"`
+	InsecureAllowDumpOAuth2UserData bool                       `koanf:"insecureAllowDumpOAuth2UserData"`
+	InsecureAllowDumpVars           bool                       `koanf:"insecureAllowDumpVars"`
+	InsecureAllowDumpSos            bool                       `koanf:"insecureAllowDumpSos"`
+	InsecureAllowDumpActionMap      bool                       `koanf:"insecureAllowDumpActionMap"`
+	InsecureAllowDumpJwtClaims      bool                       `koanf:"insecureAllowDumpJwtClaims"`
+	Prometheus                      PrometheusConfig           `koanf:"prometheus"`
+	SaveLogs                        SaveLogsConfig             `koanf:"saveLogs"`
+	DefaultIconForActions           string                     `koanf:"defaultIconForActions"`
+	DefaultIconForDirectories       string                     `koanf:"defaultIconForDirectories"`
+	DefaultIconForBack              string                     `koanf:"defaultIconForBack"`
+	AdditionalNavigationLinks       []*NavigationLink          `koanf:"additionalNavigationLinks"`
+	ServiceHostMode                 string                     `koanf:"serviceHostMode"`
+	StyleMods                       []string                   `koanf:"styleMods"`
+	BannerMessage                   string                     `koanf:"bannerMessage"`
+	BannerCSS                       string                     `koanf:"bannerCss"`
+	Include                         string                     `koanf:"include"`
 
 	sourceFiles []string
 }
 
 type AuthLocalUsersConfig struct {
-	Enabled bool         `mapstructure:"enabled"`
-	Users   []*LocalUser `mapstructure:"users"`
+	Enabled bool         `koanf:"enabled"`
+	Users   []*LocalUser `koanf:"users"`
 }
 
 type LocalUser struct {
-	Username  string `mapstructure:"username"`
-	Usergroup string `mapstructure:"usergroup"`
-	Password  string `mapstructure:"password"`
+	Username  string `koanf:"username"`
+	Usergroup string `koanf:"usergroup"`
+	Password  string `koanf:"password"`
 }
 
 type OAuth2Provider struct {
-	Name               string
-	Title              string
-	ClientID           string
-	ClientSecret       string
-	Icon               string
-	Scopes             []string
-	AuthUrl            string
-	TokenUrl           string
-	WhoamiUrl          string
-	UsernameField      string
-	UserGroupField     string
-	InsecureSkipVerify bool
-	CallbackTimeout    int
-	CertBundlePath     string
+	Name               string   `koanf:"name"`
+	Title              string   `koanf:"title"`
+	ClientID           string   `koanf:"clientId"`
+	ClientSecret       string   `koanf:"clientSecret"`
+	Icon               string   `koanf:"icon"`
+	Scopes             []string `koanf:"scopes"`
+	AuthUrl            string   `koanf:"authUrl"`
+	TokenUrl           string   `koanf:"tokenUrl"`
+	WhoamiUrl          string   `koanf:"whoamiUrl"`
+	UsernameField      string   `koanf:"usernameField"`
+	UserGroupField     string   `koanf:"userGroupField"`
+	InsecureSkipVerify bool     `koanf:"insecureSkipVerify"`
+	CallbackTimeout    int      `koanf:"callbackTimeout"`
+	CertBundlePath     string   `koanf:"certBundlePath"`
 }
 
 type NavigationLink struct {
-	Title  string `mapstructure:"title"`
-	Url    string `mapstructure:"url"`
-	Target string `mapstructure:"target"`
+	Title  string `koanf:"title"`
+	Url    string `koanf:"url"`
+	Target string `koanf:"target"`
 }
 
 type SaveLogsConfig struct {
-	ResultsDirectory string `mapstructure:"resultsDirectory"`
-	OutputDirectory  string `mapstructure:"outputDirectory"`
+	ResultsDirectory string `koanf:"resultsDirectory"`
+	OutputDirectory  string `koanf:"outputDirectory"`
 }
 
 type LogDebugOptions struct {
-	SingleFrontendRequests       bool
-	SingleFrontendRequestHeaders bool
-	AclCheckStarted              bool
-	AclMatched                   bool
-	AclNotMatched                bool
-	AclNoneMatched               bool
+	SingleFrontendRequests       bool `koanf:"singleFrontendRequests"`
+	SingleFrontendRequestHeaders bool `koanf:"singleFrontendRequestHeaders"`
+	AclCheckStarted              bool `koanf:"aclCheckStarted"`
+	AclMatched                   bool `koanf:"aclMatched"`
+	AclNotMatched                bool `koanf:"aclNotMatched"`
+	AclNoneMatched               bool `koanf:"aclNoneMatched"`
 }
 
 type DashboardComponent struct {
-	Title    string
-	Type     string
-	Entity   string
-	Icon     string
-	CssClass string
-	Contents []*DashboardComponent
+	Title    string                `koanf:"title"`
+	Type     string                `koanf:"type"`
+	Entity   string                `koanf:"entity"`
+	Icon     string                `koanf:"icon"`
+	CssClass string                `koanf:"cssClass"`
+	Contents []*DashboardComponent `koanf:"contents"`
 }
 
 func DefaultConfig() *Config {

+ 19 - 225
service/internal/config/config_reloader.go

@@ -3,8 +3,6 @@ package config
 import (
 	"os"
 	"path/filepath"
-	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 
@@ -34,17 +32,6 @@ func AddListener(l func()) {
 	listeners = append(listeners, l)
 }
 
-// AppendSourceWithIncludes loads base config and any included configs
-func AppendSourceWithIncludes(cfg *Config, k *koanf.Koanf, configPath string) {
-	// Load base config first
-	AppendSource(cfg, k, configPath)
-
-	// Load included configs if specified
-	if cfg.Include != "" {
-		LoadIncludedConfigs(cfg, k, configPath)
-	}
-}
-
 func AppendSource(cfg *Config, k *koanf.Koanf, configPath string) {
 	log.Infof("Appending cfg source: %s", configPath)
 
@@ -52,70 +39,27 @@ func AppendSource(cfg *Config, k *koanf.Koanf, configPath string) {
 		return
 	}
 
-	loadCollectionsFallbacks(k, cfg)
-
-	applyConfigOverrides(k, cfg)
-
 	afterLoadFinalize(cfg, configPath)
 }
 
-func unmarshalRoot(k *koanf.Koanf, cfg *Config) bool {
-	if err := k.Unmarshal(".", cfg); err != nil {
-		log.Errorf("Error unmarshalling config: %v", err)
-		return false
-	}
-	return true
-}
-
-func loadCollectionsFallbacks(k *koanf.Koanf, cfg *Config) {
-	maybeUnmarshalActions(k, cfg)
-	maybeUnmarshalDashboards(k, cfg)
-	maybeUnmarshalEntities(k, cfg)
-	maybeUnmarshalAuthLocalUsers(k, cfg)
-}
-
-func maybeUnmarshalActions(k *koanf.Koanf, cfg *Config) {
-	if len(cfg.Actions) != 0 || !k.Exists("actions") {
-		return
-	}
-	var actions []*Action
-	if err := k.Unmarshal("actions", &actions); err == nil {
-		cfg.Actions = actions
-		log.Debugf("Manually loaded %d actions", len(actions))
-	}
-}
+func AppendSourceWithIncludes(cfg *Config, k *koanf.Koanf, configPath string) {
+	AppendSource(cfg, k, configPath)
 
-func maybeUnmarshalDashboards(k *koanf.Koanf, cfg *Config) {
-	if len(cfg.Dashboards) != 0 || !k.Exists("dashboards") {
-		return
-	}
-	var dashboards []*DashboardComponent
-	if err := k.Unmarshal("dashboards", &dashboards); err == nil {
-		cfg.Dashboards = dashboards
-		log.Debugf("Manually loaded %d dashboards", len(dashboards))
+	if cfg.Include != "" {
+		LoadIncludedConfigs(cfg, k, configPath)
 	}
 }
 
-func maybeUnmarshalEntities(k *koanf.Koanf, cfg *Config) {
-	if len(cfg.Entities) != 0 || !k.Exists("entities") {
-		return
-	}
-	var entities []*EntityFile
-	if err := k.Unmarshal("entities", &entities); err == nil {
-		cfg.Entities = entities
-		log.Debugf("Manually loaded %d entities", len(entities))
-	}
-}
+func unmarshalRoot(k *koanf.Koanf, cfg *Config) bool {
+	err := k.UnmarshalWithConf("", cfg, koanf.UnmarshalConf{
+		Tag: "koanf",
+	})
 
-func maybeUnmarshalAuthLocalUsers(k *koanf.Koanf, cfg *Config) {
-	if len(cfg.AuthLocalUsers.Users) != 0 || !k.Exists("authLocalUsers") {
-		return
-	}
-	var authLocalUsers AuthLocalUsersConfig
-	if err := k.Unmarshal("authLocalUsers", &authLocalUsers); err == nil {
-		cfg.AuthLocalUsers = authLocalUsers
-		log.Debugf("Manually loaded local auth config")
+	if err != nil {
+		log.Errorf("Error unmarshalling config: %v", err)
+		return false
 	}
+	return true
 }
 
 func afterLoadFinalize(cfg *Config, configPath string) {
@@ -130,31 +74,6 @@ func afterLoadFinalize(cfg *Config, configPath string) {
 	}
 }
 
-func applyConfigOverrides(k *koanf.Koanf, cfg *Config) {
-	// Override fields that should be read from config
-	// mapstructure tags should make most of this unnecessary, but keep for safety
-	boolVal(k, "showFooter", &cfg.ShowFooter)
-	boolVal(k, "showNavigation", &cfg.ShowNavigation)
-	boolVal(k, "checkForUpdates", &cfg.CheckForUpdates)
-	boolVal(k, "useSingleHTTPFrontend", &cfg.UseSingleHTTPFrontend)
-	stringVal(k, "logLevel", &cfg.LogLevel)
-	stringVal(k, "pageTitle", &cfg.PageTitle)
-	boolVal(k, "authRequireGuestsToLogin", &cfg.AuthRequireGuestsToLogin)
-	stringVal(k, "include", &cfg.Include)
-
-	// Handle nested defaultPolicy struct
-	if k.Exists("defaultPolicy") {
-		boolVal(k, "defaultPolicy.showDiagnostics", &cfg.DefaultPolicy.ShowDiagnostics)
-		boolVal(k, "defaultPolicy.showLogList", &cfg.DefaultPolicy.ShowLogList)
-	}
-
-	// Handle nested prometheus struct
-	if k.Exists("prometheus") {
-		boolVal(k, "prometheus.enabled", &cfg.Prometheus.Enabled)
-		boolVal(k, "prometheus.defaultGoMetrics", &cfg.Prometheus.DefaultGoMetrics)
-	}
-}
-
 // LoadIncludedConfigs loads configuration files from an include directory and merges them
 func LoadIncludedConfigs(cfg *Config, k *koanf.Koanf, baseConfigPath string) {
 	if cfg.Include == "" {
@@ -171,7 +90,7 @@ func LoadIncludedConfigs(cfg *Config, k *koanf.Koanf, baseConfigPath string) {
 
 	sort.Strings(yamlFiles)
 	for _, filename := range yamlFiles {
-		loadAndMergeIncludedFile(cfg, includePath, filename)
+		loadAndMergeIncludedFile(k, cfg, includePath, filename)
 	}
 
 	log.Infof("Finished loading %d included config file(s)", len(yamlFiles))
@@ -209,152 +128,26 @@ func listYamlFiles(includePath string) ([]string, bool) {
 	return yamlFiles, true
 }
 
-func loadAndMergeIncludedFile(cfg *Config, includePath, filename string) {
+func loadAndMergeIncludedFile(k *koanf.Koanf, cfg *Config, includePath, filename string) {
 	filePath := filepath.Join(includePath, filename)
 	log.Infof("Loading included config file: %s", filePath)
 
-	includeK := koanf.New(".")
-	if err := includeK.Load(file.Provider(filePath), yaml.Parser()); err != nil {
+	if err := k.Load(file.Provider(filePath), yaml.Parser()); err != nil {
 		log.Errorf("Error loading included config file %s: %v", filePath, err)
 		return
 	}
 
-	tempCfg := &Config{}
-	if err := includeK.Unmarshal(".", tempCfg); err != nil {
+	if err := k.Unmarshal(".", cfg); err != nil {
 		log.Errorf("Error unmarshalling included config file %s: %v", filePath, err)
 		return
 	}
 
-	loadCollectionsFallbacks(includeK, tempCfg)
-
-	mergeConfig(cfg, tempCfg)
-	log.Infof("Successfully loaded and merged %s", filename)
-}
-
-func mergeConfig(base *Config, overlay *Config) {
-	mergeSlices(base, overlay)
-	overrideSimple(base, overlay)
-	overrideNested(base, overlay)
-	overrideStrings(base, overlay)
-}
-
-func mergeSlices(base *Config, overlay *Config) {
-	if len(overlay.Actions) > 0 {
-		base.Actions = append(base.Actions, overlay.Actions...)
-	}
-	if len(overlay.Dashboards) > 0 {
-		base.Dashboards = append(base.Dashboards, overlay.Dashboards...)
-		log.Debugf("Merged %d dashboards from include", len(overlay.Dashboards))
-	}
-	if len(overlay.Entities) > 0 {
-		base.Entities = append(base.Entities, overlay.Entities...)
-		log.Debugf("Merged %d entities from include", len(overlay.Entities))
-	}
-	if len(overlay.AccessControlLists) > 0 {
-		base.AccessControlLists = append(base.AccessControlLists, overlay.AccessControlLists...)
-		log.Debugf("Merged %d access control lists from include", len(overlay.AccessControlLists))
-	}
-	if len(overlay.AuthLocalUsers.Users) > 0 {
-		base.AuthLocalUsers.Users = append(base.AuthLocalUsers.Users, overlay.AuthLocalUsers.Users...)
-		log.Debugf("Merged %d local users from include", len(overlay.AuthLocalUsers.Users))
-	}
-	if len(overlay.StyleMods) > 0 {
-		base.StyleMods = append(base.StyleMods, overlay.StyleMods...)
-	}
-	if len(overlay.AdditionalNavigationLinks) > 0 {
-		base.AdditionalNavigationLinks = append(base.AdditionalNavigationLinks, overlay.AdditionalNavigationLinks...)
-	}
-}
-
-func overrideSimple(base *Config, overlay *Config) {
-	if overlay.LogLevel != "" {
-		base.LogLevel = overlay.LogLevel
-	}
-	if overlay.PageTitle != "" {
-		base.PageTitle = overlay.PageTitle
-	}
-	if overlay.ShowFooter != base.ShowFooter {
-		base.ShowFooter = overlay.ShowFooter
-	}
-	if overlay.ShowNavigation != base.ShowNavigation {
-		base.ShowNavigation = overlay.ShowNavigation
-	}
-	if overlay.CheckForUpdates != base.CheckForUpdates {
-		base.CheckForUpdates = overlay.CheckForUpdates
-	}
-	if overlay.UseSingleHTTPFrontend != base.UseSingleHTTPFrontend {
-		base.UseSingleHTTPFrontend = overlay.UseSingleHTTPFrontend
-	}
-	if overlay.AuthRequireGuestsToLogin != base.AuthRequireGuestsToLogin {
-		base.AuthRequireGuestsToLogin = overlay.AuthRequireGuestsToLogin
-	}
-	if overlay.AuthLocalUsers.Enabled {
-		base.AuthLocalUsers.Enabled = overlay.AuthLocalUsers.Enabled
-	}
-}
-
-func overrideNested(base *Config, overlay *Config) {
-	// Only apply overrides when overlay explicitly enables the option.
-	// This mirrors the presence-check pattern used elsewhere to avoid
-	// unintentionally disabling an already-enabled base setting with a default false.
-	if overlay.DefaultPolicy.ShowDiagnostics {
-		base.DefaultPolicy.ShowDiagnostics = true
-	}
-	if overlay.DefaultPolicy.ShowLogList {
-		base.DefaultPolicy.ShowLogList = true
-	}
-	if overlay.Prometheus.Enabled {
-		base.Prometheus.Enabled = true
-	}
-	if overlay.Prometheus.DefaultGoMetrics {
-		base.Prometheus.DefaultGoMetrics = true
-	}
-}
-
-func overrideStrings(base *Config, overlay *Config) {
-	overrideString(&base.BannerMessage, overlay.BannerMessage)
-	overrideString(&base.BannerCSS, overlay.BannerCSS)
-	overrideString(&base.LogLevel, overlay.LogLevel)
-	overrideString(&base.PageTitle, overlay.PageTitle)
-	overrideString(&base.SectionNavigationStyle, overlay.SectionNavigationStyle)
-	overrideString(&base.DefaultPopupOnStart, overlay.DefaultPopupOnStart)
-}
-
-func overrideString(base *string, overlay string) {
-	if overlay != "" {
-		*base = overlay
-	}
-}
-
-func getActionTitles(actions []*Action) []string {
-	titles := make([]string, len(actions))
-	for i, action := range actions {
-		titles[i] = action.Title
-	}
-	return titles
+	log.Infof("Successfully loaded %s", filename)
 }
 
+/**
 var envRegex = regexp.MustCompile(`\${{ *?(\S+) *?}}`)
 
-// Helper functions to reduce repetitive if/set chains
-func stringVal(k *koanf.Koanf, key string, dest *string) {
-	if k.Exists(key) {
-		*dest = k.String(key)
-	}
-}
-
-func boolVal(k *koanf.Koanf, key string, dest *bool) {
-	if k.Exists(key) {
-		*dest = k.Bool(key)
-	}
-}
-
-func int64Val(k *koanf.Koanf, key string, dest *int64) {
-	if k.Exists(key) {
-		*dest = k.Int64(key)
-	}
-}
-
 func envDecodeHookFunc(from reflect.Type, to reflect.Type, data any) (any, error) {
 	log.Debugf("envDecodeHookFunc called: from=%v, to=%v, data=%v", from, to, data)
 	if from.Kind() != reflect.String {
@@ -375,3 +168,4 @@ func envDecodeHookFunc(from reflect.Type, to reflect.Type, data any) (any, error
 	log.Debugf("Environment variable interpolation result: %q -> %q", input, output)
 	return output, nil
 }
+*/