From cb9d8d236a11ea2dba89dba70b3a4256343bf3e0 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Wed, 15 Oct 2025 23:32:54 +0200 Subject: [PATCH] Config: Move Portal flag to ClientConfig struct Signed-off-by: Michael Mayer --- frontend/src/common/config.js | 2 +- frontend/src/options/options.js | 4 ++-- frontend/src/page/settings/general.vue | 4 ++-- internal/api/api_auth_jwt.go | 2 +- internal/api/cluster_metrics.go | 2 +- internal/api/cluster_nodes.go | 8 ++++---- internal/api/cluster_nodes_register.go | 2 +- internal/api/cluster_summary.go | 4 ++-- internal/api/cluster_theme.go | 2 +- internal/commands/auth_jwt_test.go | 2 +- internal/commands/cluster_health.go | 2 +- internal/commands/cluster_nodes_list.go | 2 +- internal/commands/cluster_nodes_mod.go | 2 +- internal/commands/cluster_nodes_remove.go | 2 +- internal/commands/cluster_nodes_rotate.go | 2 +- internal/commands/cluster_nodes_show.go | 2 +- internal/commands/cluster_summary.go | 2 +- internal/commands/jwt_helpers.go | 2 +- internal/config/client_config.go | 4 ++++ internal/config/client_config_test.go | 1 + internal/config/config_cluster.go | 8 ++++---- internal/config/config_cluster_test.go | 4 ++-- internal/config/customize/feature.go | 1 - internal/config/customize/settings.go | 1 - internal/config/settings.go | 2 -- internal/photoprism/get/jwt.go | 4 ++-- internal/server/routes_wellknown.go | 2 +- internal/service/cluster/node/bootstrap.go | 2 +- internal/service/cluster/policy.go | 2 +- 29 files changed, 40 insertions(+), 39 deletions(-) diff --git a/frontend/src/common/config.js b/frontend/src/common/config.js index 0a2e89ab1..ff5373125 100644 --- a/frontend/src/common/config.js +++ b/frontend/src/common/config.js @@ -820,7 +820,7 @@ export default class Config { // isPortal returns true if this is a cluster portal server. isPortal() { - return this.feature("portal"); + return this.values && this.values.portal; } // isPro returns true if this is team version. diff --git a/frontend/src/options/options.js b/frontend/src/options/options.js index 8f63c65b2..c1b5fbc34 100644 --- a/frontend/src/options/options.js +++ b/frontend/src/options/options.js @@ -192,8 +192,8 @@ export const ItemsPerPage = () => [ { text: "100", title: "100", value: 100 }, ]; -export const StartPages = (features) => { - if (features.portal) { +export const StartPages = (features, isPortal) => { + if (isPortal) { return [{ value: "default", text: $gettext("Default"), visible: true }]; } return [ diff --git a/frontend/src/page/settings/general.vue b/frontend/src/page/settings/general.vue index 0b697c609..d6b3097b0 100644 --- a/frontend/src/page/settings/general.vue +++ b/frontend/src/page/settings/general.vue @@ -64,7 +64,7 @@ name via registry; otherwise treat key as name. name := clean.DNSLabel(key) - if conf.IsPortal() { + if conf.Portal() { if r, err := reg.NewClientRegistryWithConfig(conf); err == nil { if n, err := r.FindByNodeUUID(key); err == nil && n != nil { name = n.Name diff --git a/internal/commands/cluster_nodes_show.go b/internal/commands/cluster_nodes_show.go index 408047b8e..4cd4905a1 100644 --- a/internal/commands/cluster_nodes_show.go +++ b/internal/commands/cluster_nodes_show.go @@ -24,7 +24,7 @@ var ClusterNodesShowCommand = &cli.Command{ func clusterNodesShowAction(ctx *cli.Context) error { return CallWithDependencies(ctx, func(conf *config.Config) error { - if !conf.IsPortal() { + if !conf.Portal() { return cli.Exit(fmt.Errorf("node show is only available on a Portal node"), 2) } diff --git a/internal/commands/cluster_summary.go b/internal/commands/cluster_summary.go index 3ce1babf8..e93dee650 100644 --- a/internal/commands/cluster_summary.go +++ b/internal/commands/cluster_summary.go @@ -24,7 +24,7 @@ var ClusterSummaryCommand = &cli.Command{ func clusterSummaryAction(ctx *cli.Context) error { return CallWithDependencies(ctx, func(conf *config.Config) error { - if !conf.IsPortal() { + if !conf.Portal() { return fmt.Errorf("cluster summary is only available on a Portal node") } diff --git a/internal/commands/jwt_helpers.go b/internal/commands/jwt_helpers.go index 6598277b0..05cee205a 100644 --- a/internal/commands/jwt_helpers.go +++ b/internal/commands/jwt_helpers.go @@ -28,7 +28,7 @@ var allowedJWTScope = func() map[string]struct{} { // requirePortal returns a CLI error when the active configuration is not a portal node. func requirePortal(conf *config.Config) error { - if conf == nil || !conf.IsPortal() { + if conf == nil || !conf.Portal() { return cli.Exit(errors.New("command requires a Portal node"), 2) } return nil diff --git a/internal/config/client_config.go b/internal/config/client_config.go index 81b19a5c2..f25d533f0 100644 --- a/internal/config/client_config.go +++ b/internal/config/client_config.go @@ -62,6 +62,7 @@ type ClientConfig struct { Trace bool `json:"trace"` Test bool `json:"test"` Demo bool `json:"demo"` + Portal bool `json:"portal"` Sponsor bool `json:"sponsor"` ReadOnly bool `json:"readonly"` UploadNSFW bool `json:"uploadNSFW"` @@ -312,6 +313,7 @@ func (c *Config) ClientPublic() *ClientConfig { Trace: c.Trace(), Test: c.Test(), Demo: c.Demo(), + Portal: c.Portal(), Sponsor: c.Sponsor(), ReadOnly: c.ReadOnly(), Public: c.Public(), @@ -407,6 +409,7 @@ func (c *Config) ClientShare() *ClientConfig { Trace: c.Trace(), Test: c.Test(), Demo: c.Demo(), + Portal: c.Portal(), Sponsor: c.Sponsor(), ReadOnly: c.ReadOnly(), UploadNSFW: c.UploadNSFW(), @@ -510,6 +513,7 @@ func (c *Config) ClientUser(withSettings bool) *ClientConfig { Trace: c.Trace(), Test: c.Test(), Demo: c.Demo(), + Portal: c.Portal(), Sponsor: c.Sponsor(), ReadOnly: c.ReadOnly(), UploadNSFW: c.UploadNSFW(), diff --git a/internal/config/client_config_test.go b/internal/config/client_config_test.go index ea9642bba..d0b1245cd 100644 --- a/internal/config/client_config_test.go +++ b/internal/config/client_config_test.go @@ -53,6 +53,7 @@ func TestConfig_ClientConfig(t *testing.T) { assert.Equal(t, true, cfg.Debug) assert.Equal(t, AuthModePublic, cfg.AuthMode) assert.Equal(t, false, cfg.Demo) + assert.Equal(t, false, cfg.Portal) assert.Equal(t, true, cfg.Sponsor) assert.Equal(t, false, cfg.ReadOnly) diff --git a/internal/config/config_cluster.go b/internal/config/config_cluster.go index e3f9e80e5..a06265d2d 100644 --- a/internal/config/config_cluster.go +++ b/internal/config/config_cluster.go @@ -84,8 +84,8 @@ func (c *Config) PortalUrl() string { return c.options.PortalUrl } -// IsPortal returns true if the configured node type is "portal". -func (c *Config) IsPortal() bool { +// Portal returns true if the configured node type is "portal". +func (c *Config) Portal() bool { return c.NodeRole() == cluster.RolePortal } @@ -123,7 +123,7 @@ func (c *Config) JoinToken() string { } } - if !c.IsPortal() { + if !c.Portal() { return "" } @@ -182,7 +182,7 @@ func (c *Config) NodeName() string { } // Default: portal nodes → "portal". - if c.IsPortal() { + if c.Portal() { return "portal" } diff --git a/internal/config/config_cluster_test.go b/internal/config/config_cluster_test.go index 47da2a1df..11d0758ac 100644 --- a/internal/config/config_cluster_test.go +++ b/internal/config/config_cluster_test.go @@ -94,11 +94,11 @@ func TestConfig_Cluster(t *testing.T) { c := NewConfig(CliTestContext()) // Defaults - assert.False(t, c.IsPortal()) + assert.False(t, c.Portal()) // Toggle values c.Options().NodeRole = string(cluster.RolePortal) - assert.True(t, c.IsPortal()) + assert.True(t, c.Portal()) c.Options().NodeRole = "" }) t.Run("JWKSUrlSetter", func(t *testing.T) { diff --git a/internal/config/customize/feature.go b/internal/config/customize/feature.go index 84ea58341..e84436e8a 100644 --- a/internal/config/customize/feature.go +++ b/internal/config/customize/feature.go @@ -21,7 +21,6 @@ type FeatureSettings struct { People bool `json:"people" yaml:"People"` Places bool `json:"places" yaml:"Places"` Private bool `json:"private" yaml:"Private"` - Portal bool `json:"portal" yaml:"-"` Ratings bool `json:"ratings" yaml:"Ratings"` Reactions bool `json:"reactions" yaml:"Reactions"` Review bool `json:"review" yaml:"Review"` diff --git a/internal/config/customize/settings.go b/internal/config/customize/settings.go index f06c605e1..a21a89651 100644 --- a/internal/config/customize/settings.go +++ b/internal/config/customize/settings.go @@ -97,7 +97,6 @@ func NewSettings(theme, language, timeZone string) *Settings { Services: true, Account: true, Delete: true, - Portal: false, }, Import: ImportSettings{ Path: RootPath, diff --git a/internal/config/settings.go b/internal/config/settings.go index 686a5de28..3cccd3466 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -36,8 +36,6 @@ func (c *Config) initSettings() { log.Debugf("settings: saved to %s ", settingsFile) } - c.settings.Features.Portal = c.IsPortal() - i18n.SetDir(c.LocalesPath()) c.settings.Propagate() diff --git a/internal/photoprism/get/jwt.go b/internal/photoprism/get/jwt.go index 04e075008..7e4f6728b 100644 --- a/internal/photoprism/get/jwt.go +++ b/internal/photoprism/get/jwt.go @@ -22,7 +22,7 @@ var ( func initJWTManager() { if conf == nil { return - } else if !conf.IsPortal() { + } else if !conf.Portal() { return } @@ -121,7 +121,7 @@ func IssuePortalJWT(spec jwt.ClaimsSpec) (string, error) { func IssuePortalJWTForNode(nodeUUID string, scopes []string, ttl time.Duration) (string, error) { if conf == nil { return "", errors.New("jwt: missing config") - } else if !conf.IsPortal() { + } else if !conf.Portal() { return "", errors.New("jwt: not supported on nodes") } diff --git a/internal/server/routes_wellknown.go b/internal/server/routes_wellknown.go index c026fc5de..222331ef8 100644 --- a/internal/server/routes_wellknown.go +++ b/internal/server/routes_wellknown.go @@ -28,7 +28,7 @@ func registerWellknownRoutes(router *gin.Engine, conf *config.Config) { // Registers the "/.well-known/jwks.json" endpoint for cluster JWT verification. router.GET(conf.BaseUri("/.well-known/jwks.json"), func(c *gin.Context) { - if !conf.IsPortal() { + if !conf.Portal() { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } diff --git a/internal/service/cluster/node/bootstrap.go b/internal/service/cluster/node/bootstrap.go index ae55d740b..d649e2b96 100644 --- a/internal/service/cluster/node/bootstrap.go +++ b/internal/service/cluster/node/bootstrap.go @@ -46,7 +46,7 @@ func InitConfig(c *config.Config) error { role := c.NodeRole() // Skip on portal nodes and unknown node types. - if c.IsPortal() || (role != cluster.RoleInstance && role != cluster.RoleService) { + if c.Portal() || (role != cluster.RoleInstance && role != cluster.RoleService) { return nil } diff --git a/internal/service/cluster/policy.go b/internal/service/cluster/policy.go index 05755088c..f99ef9a3e 100644 --- a/internal/service/cluster/policy.go +++ b/internal/service/cluster/policy.go @@ -4,7 +4,7 @@ import "time" // BootstrapAutoJoinEnabled indicates whether cluster bootstrap logic is enabled // for nodes by default. Portal nodes ignore this value; gating is decided by -// runtime checks (e.g., conf.IsPortal() and conf.NodeRole()). +// runtime checks (e.g., conf.Portal() and conf.NodeRole()). var BootstrapAutoJoinEnabled = true // BootstrapAutoThemeEnabled indicates whether bootstrap should attempt to