70 lines
2.1 KiB
JavaScript
70 lines
2.1 KiB
JavaScript
const {
|
|
validateUsername,
|
|
USERNAME_MIN_LENGTH,
|
|
USERNAME_MAX_LENGTH,
|
|
} = require('./usernameValidation')
|
|
|
|
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
|
|
const isUsernameTaken = async (UserModel, userName, excludeUserId) => {
|
|
const existing = await UserModel.findOne({
|
|
user_name: { $regex: new RegExp(`^${escapeRegex(userName)}$`, 'i') }
|
|
})
|
|
|
|
if (!existing) return false
|
|
if (excludeUserId && existing._id.toString() === excludeUserId) return false
|
|
return true
|
|
}
|
|
|
|
const padBase = (base) => {
|
|
let value = base.replace(/[^a-zA-Z0-9._-]/g, '')
|
|
if (!/^[a-zA-Z0-9]/.test(value)) value = `u${value}`
|
|
while (value.length < USERNAME_MIN_LENGTH - 2) {
|
|
value = `${value}0`
|
|
}
|
|
return value.slice(0, Math.max(USERNAME_MIN_LENGTH - 2, 1))
|
|
}
|
|
|
|
const buildCandidates = (base, attempt) => {
|
|
const random2 = () => String(Math.floor(Math.random() * 90 + 10))
|
|
const random3 = () => String(Math.floor(Math.random() * 900 + 100))
|
|
const stem = padBase(base)
|
|
|
|
const builders = [
|
|
() => `${stem}${random2()}`,
|
|
() => `${stem}_${random2()}`,
|
|
() => `${stem}.${random2()}`,
|
|
() => `${stem}-${random2()}`,
|
|
() => `${stem}${random3()}`,
|
|
() => `${stem.slice(0, Math.max(USERNAME_MIN_LENGTH - 3, 1))}${random3()}`,
|
|
]
|
|
|
|
return builders[attempt % builders.length]()
|
|
}
|
|
|
|
const trimToMax = (value) =>
|
|
value.length > USERNAME_MAX_LENGTH ? value.slice(0, USERNAME_MAX_LENGTH) : value
|
|
|
|
const generateUsernameSuggestions = async (UserModel, baseUserName, excludeUserId) => {
|
|
const base = trimToMax(baseUserName.trim().toLowerCase())
|
|
const suggestions = []
|
|
let attempt = 0
|
|
|
|
while (suggestions.length < 3 && attempt < 40) {
|
|
const candidate = trimToMax(buildCandidates(base, attempt))
|
|
attempt += 1
|
|
|
|
if (validateUsername(candidate)) continue
|
|
if (suggestions.includes(candidate)) continue
|
|
if (await isUsernameTaken(UserModel, candidate, excludeUserId)) continue
|
|
|
|
suggestions.push(candidate)
|
|
}
|
|
|
|
return suggestions
|
|
}
|
|
|
|
module.exports = {
|
|
generateUsernameSuggestions,
|
|
}
|