Apply full refactor updates plus pipeline/email UX confirmations
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
This commit is contained in:
@@ -1,93 +1,93 @@
|
||||
import bcrypt from 'bcryptjs'
|
||||
|
||||
const SALT_ROUNDS = 12
|
||||
|
||||
/**
|
||||
* Hash a password using bcrypt
|
||||
*/
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return bcrypt.hash(password, SALT_ROUNDS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a password against a hash
|
||||
*/
|
||||
export async function verifyPassword(
|
||||
password: string,
|
||||
hash: string
|
||||
): Promise<boolean> {
|
||||
return bcrypt.compare(password, hash)
|
||||
}
|
||||
|
||||
interface PasswordValidation {
|
||||
valid: boolean
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password meets requirements:
|
||||
* - Minimum 8 characters
|
||||
* - At least one uppercase letter
|
||||
* - At least one lowercase letter
|
||||
* - At least one number
|
||||
*/
|
||||
export function validatePassword(password: string): PasswordValidation {
|
||||
const errors: string[] = []
|
||||
|
||||
if (password.length < 8) {
|
||||
errors.push('Password must be at least 8 characters long')
|
||||
}
|
||||
|
||||
if (!/[A-Z]/.test(password)) {
|
||||
errors.push('Password must contain at least one uppercase letter')
|
||||
}
|
||||
|
||||
if (!/[a-z]/.test(password)) {
|
||||
errors.push('Password must contain at least one lowercase letter')
|
||||
}
|
||||
|
||||
if (!/[0-9]/.test(password)) {
|
||||
errors.push('Password must contain at least one number')
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password strength score (0-4)
|
||||
* 0 = very weak, 4 = very strong
|
||||
*/
|
||||
export function getPasswordStrength(password: string): {
|
||||
score: number
|
||||
label: 'Very Weak' | 'Weak' | 'Fair' | 'Strong' | 'Very Strong'
|
||||
} {
|
||||
let score = 0
|
||||
|
||||
// Length
|
||||
if (password.length >= 8) score++
|
||||
if (password.length >= 12) score++
|
||||
|
||||
// Character variety
|
||||
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) score++
|
||||
if (/[0-9]/.test(password)) score++
|
||||
if (/[^a-zA-Z0-9]/.test(password)) score++
|
||||
|
||||
// Normalize to 0-4
|
||||
const normalizedScore = Math.min(4, score)
|
||||
|
||||
const labels: Record<number, 'Very Weak' | 'Weak' | 'Fair' | 'Strong' | 'Very Strong'> = {
|
||||
0: 'Very Weak',
|
||||
1: 'Weak',
|
||||
2: 'Fair',
|
||||
3: 'Strong',
|
||||
4: 'Very Strong',
|
||||
}
|
||||
|
||||
return {
|
||||
score: normalizedScore,
|
||||
label: labels[normalizedScore],
|
||||
}
|
||||
}
|
||||
import bcrypt from 'bcryptjs'
|
||||
|
||||
const SALT_ROUNDS = 12
|
||||
|
||||
/**
|
||||
* Hash a password using bcrypt
|
||||
*/
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return bcrypt.hash(password, SALT_ROUNDS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a password against a hash
|
||||
*/
|
||||
export async function verifyPassword(
|
||||
password: string,
|
||||
hash: string
|
||||
): Promise<boolean> {
|
||||
return bcrypt.compare(password, hash)
|
||||
}
|
||||
|
||||
interface PasswordValidation {
|
||||
valid: boolean
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password meets requirements:
|
||||
* - Minimum 8 characters
|
||||
* - At least one uppercase letter
|
||||
* - At least one lowercase letter
|
||||
* - At least one number
|
||||
*/
|
||||
export function validatePassword(password: string): PasswordValidation {
|
||||
const errors: string[] = []
|
||||
|
||||
if (password.length < 8) {
|
||||
errors.push('Password must be at least 8 characters long')
|
||||
}
|
||||
|
||||
if (!/[A-Z]/.test(password)) {
|
||||
errors.push('Password must contain at least one uppercase letter')
|
||||
}
|
||||
|
||||
if (!/[a-z]/.test(password)) {
|
||||
errors.push('Password must contain at least one lowercase letter')
|
||||
}
|
||||
|
||||
if (!/[0-9]/.test(password)) {
|
||||
errors.push('Password must contain at least one number')
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password strength score (0-4)
|
||||
* 0 = very weak, 4 = very strong
|
||||
*/
|
||||
export function getPasswordStrength(password: string): {
|
||||
score: number
|
||||
label: 'Very Weak' | 'Weak' | 'Fair' | 'Strong' | 'Very Strong'
|
||||
} {
|
||||
let score = 0
|
||||
|
||||
// Length
|
||||
if (password.length >= 8) score++
|
||||
if (password.length >= 12) score++
|
||||
|
||||
// Character variety
|
||||
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) score++
|
||||
if (/[0-9]/.test(password)) score++
|
||||
if (/[^a-zA-Z0-9]/.test(password)) score++
|
||||
|
||||
// Normalize to 0-4
|
||||
const normalizedScore = Math.min(4, score)
|
||||
|
||||
const labels: Record<number, 'Very Weak' | 'Weak' | 'Fair' | 'Strong' | 'Very Strong'> = {
|
||||
0: 'Very Weak',
|
||||
1: 'Weak',
|
||||
2: 'Fair',
|
||||
3: 'Strong',
|
||||
4: 'Very Strong',
|
||||
}
|
||||
|
||||
return {
|
||||
score: normalizedScore,
|
||||
label: labels[normalizedScore],
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user