diff --git a/VERSION b/VERSION index 3d370b4..4b7ce3d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.151 +1.1.152 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 0523620..4254c91 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.151" +var version = "1.1.152" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index 0d3e993..ce3568d 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -11,7 +11,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) -var version = "1.1.151" +var version = "1.1.152" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index 3a62d46..602dfe5 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -41,7 +41,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.1.151" +var version = "1.1.152" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/internal/services/setup/setup.go b/internal/services/setup/setup.go index 59cbd96..5890abb 100644 --- a/internal/services/setup/setup.go +++ b/internal/services/setup/setup.go @@ -168,6 +168,10 @@ func (s *Store) Complete(req Request) (*State, error) { // CompleteAsNode configures this node as a secondary cluster member. // No admin credentials are created; login becomes available after PG // replication brings users from the primary. +// +// Idempotent: if setup was already completed (e.g. the node was +// previously configured as standalone), it is converted to cluster-node +// mode. Calling again after a successful join is a no-op. func (s *Store) CompleteAsNode(req NodeRequest) (*State, error) { if !looksLikeFQDN(req.FQDN) { return nil, fmt.Errorf("fqdn %q does not look like a fully-qualified hostname", req.FQDN) @@ -179,16 +183,22 @@ func (s *Store) CompleteAsNode(req NodeRequest) (*State, error) { if err != nil { return nil, err } - if prev.Completed { - return nil, errors.New("setup already completed") - } now := time.Now().UTC() + completedAt := &now + if prev.CompletedAt != nil { + completedAt = prev.CompletedAt + } st := &State{ FQDN: strings.TrimSpace(req.FQDN), ACMEEmail: strings.ToLower(strings.TrimSpace(req.ACMEEmail)), IsClusterNode: true, Completed: true, - CompletedAt: &now, + CompletedAt: completedAt, + // Carry over non-auth fields from previous state. + LicenseKey: prev.LicenseKey, + IPv6Enabled: prev.IPv6Enabled, + BackupRetentionKeep: prev.BackupRetentionKeep, + AuditRetentionDays: prev.AuditRetentionDays, } if err := s.Save(st); err != nil { return nil, err