mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
fix(auth): single-user deployment by default, no open registration
Registration logic redesigned: - Empty DB (first-time setup): registration always open, no config needed - After first user exists: registration closed by default - Multi-user opt-in: set REGISTRATION_ENABLED=true + MAX_USERS=N in .env Config defaults changed: - RegistrationEnabled: true → false (closed after first user) - MaxUsers: 10 → 1 (single-user deployment default) This eliminates the confusion of multiple users appearing in a personal deployment where Telegram is bound to a single admin account.
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user