Add max user registration limit

- Parse MAX_USERS from env in registrationConfig
- Count total users with new UserRepository.countUsers()
- Enforce registration cap in the signup flow
- Expose MAX_USERS in docker-compose and env example
- Bump frontend version to 2.1.2
This commit is contained in:
2026-01-18 16:42:42 +01:00
parent 5e3b71a015
commit 1fdc55afa1
7 changed files with 43 additions and 12 deletions

View File

@@ -22,6 +22,9 @@ CORS_ORIGIN=http://localhost:80
# User Registration
ALLOW_REGISTRATION=true
# Maximum number of users allowed to register (leave empty for unlimited)
# Only applies if ALLOW_REGISTRATION is true
MAX_USERS=50
# Rate Limiting (optional - default: 100 requests per 15 minutes)
# RATE_LIMIT_WINDOW defines the time window in MINUTES

View File

@@ -27,7 +27,8 @@ export const serverConfig = {
}
export const registrationConfig = {
allowRegistration: process.env.ALLOW_REGISTRATION !== 'false'
allowRegistration: process.env.ALLOW_REGISTRATION !== 'false',
maxUsers: process.env.MAX_USERS ? parseInt(process.env.MAX_USERS) : null
}
export const rateLimitConfig = {

View File

@@ -158,6 +158,20 @@ export class UserRepository extends Repository {
return result.rowCount > 0
}
/**
* Count total users in the system
*/
async countUsers() {
try {
const result = await dbManager.query(
`SELECT COUNT(*) as count FROM ${this.tableName}`
)
return parseInt(result.rows[0].count, 10) || 0
} catch (error) {
throw new Error('Failed to count users')
}
}
/**
* Get user statistics
*/

View File

@@ -126,17 +126,28 @@ export default async function authRoutes(fastify, options) {
config: { rateLimit: { max: 3, timeWindow: '15 minutes' } }
},
async (request, reply) => {
try {
// Check if registration is allowed
if (!registrationConfig.allowRegistration) {
return reply.code(403).send({
error: 'Registration Disabled',
message: 'User registration is currently disabled'
})
}
try {
// Check if registration is allowed
if (!registrationConfig.allowRegistration) {
return reply.code(403).send({
error: 'Registration Disabled',
message: 'User registration is currently disabled'
})
}
// LAYER 1: Schema validation
const validatedData = registerSchema.parse(request.body)
// Check if max user limit has been reached (only if allowRegistration is true and MAX_USERS is set)
if (registrationConfig.allowRegistration && registrationConfig.maxUsers) {
const userCount = await userRepo.countUsers()
if (userCount >= registrationConfig.maxUsers) {
return reply.code(403).send({
error: 'Registration Disabled',
message: 'User registration limit has been reached'
})
}
}
// LAYER 1: Schema validation
const validatedData = registerSchema.parse(request.body)
// LAYER 2: Business logic validation
// Check username uniqueness

View File

@@ -338,6 +338,7 @@ services:
- CORS_ORIGIN=\${CORS_ORIGIN:-https://yourdomain.com}
- LOG_LEVEL=\${LOG_LEVEL:-warn}
- ALLOW_REGISTRATION=\${ALLOW_REGISTRATION:-false}
- MAX_USERS=\${MAX_USERS:-}
restart: unless-stopped
deploy:
resources:

View File

@@ -70,6 +70,7 @@ services:
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost}
- LOG_LEVEL=${LOG_LEVEL:-info}
- ALLOW_REGISTRATION=${ALLOW_REGISTRATION:-true}
- MAX_USERS=${MAX_USERS:-}
volumes:
- ./backend/src:/app/src
restart: unless-stopped

View File

@@ -1 +1 @@
2.1.1
2.1.2