diff --git a/api/server.go b/api/server.go index c87138fd..9e5fcbd6 100644 --- a/api/server.go +++ b/api/server.go @@ -3152,14 +3152,33 @@ func (s *Server) handleLogout(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Logged out"}) } -// handleRegister Handle user registration request +// handleRegister Handle user registration request. +// Registration is only open when no users exist yet (first-time setup) OR when +// explicitly enabled via REGISTRATION_ENABLED=true env var AND within MAX_USERS limit. func (s *Server) handleRegister(c *gin.Context) { - // Check if registration is allowed - if !config.Get().RegistrationEnabled { - c.JSON(http.StatusForbidden, gin.H{"error": "Registration is disabled"}) + userCount, err := s.store.User().Count() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check user count"}) return } + // First-time setup: allow registration when DB is empty, regardless of config. + firstTimeSetup := userCount == 0 + + if !firstTimeSetup { + // After first user exists: require explicit opt-in via REGISTRATION_ENABLED=true. + if !config.Get().RegistrationEnabled { + c.JSON(http.StatusForbidden, gin.H{"error": "Registration is disabled"}) + return + } + // Enforce max users limit. + maxUsers := config.Get().MaxUsers + if maxUsers > 0 && userCount >= maxUsers { + c.JSON(http.StatusForbidden, gin.H{"error": "Maximum number of users reached"}) + return + } + } + var req struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,min=6"` @@ -3171,26 +3190,12 @@ func (s *Server) handleRegister(c *gin.Context) { } // Check if email already exists - _, err := s.store.User().GetByEmail(req.Email) + _, err = s.store.User().GetByEmail(req.Email) if err == nil { c.JSON(http.StatusConflict, gin.H{"error": "Email already registered"}) return } - // Check max users limit (only for new users) - maxUsers := config.Get().MaxUsers - if maxUsers > 0 { - userCount, err := s.store.User().Count() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check user count"}) - return - } - if userCount >= maxUsers { - c.JSON(http.StatusForbidden, gin.H{"error": "Not on whitelist"}) - return - } - } - // Generate password hash passwordHash, err := auth.HashPassword(req.Password) if err != nil { diff --git a/config/config.go b/config/config.go index 91127d22..46b9a352 100644 --- a/config/config.go +++ b/config/config.go @@ -55,8 +55,8 @@ type Config struct { func Init() { cfg := &Config{ APIServerPort: 8080, - RegistrationEnabled: true, - MaxUsers: 10, // Default: 10 users allowed + RegistrationEnabled: false, // Default: closed after first user registers (first-time setup always allowed) + MaxUsers: 1, // Default: single-user deployment ExperienceImprovement: true, // Default: enabled to help improve the product // Database defaults DBType: "sqlite",