Files
MOPC-Portal/src/lib/password.ts
Matt a606292aaa Initial commit: MOPC platform with Docker deployment setup
Full Next.js 15 platform with tRPC, Prisma, PostgreSQL, NextAuth.
Includes production Dockerfile (multi-stage, port 7600), docker-compose
with registry-based image pull, Gitea Actions CI workflow, nginx config
for portal.monaco-opc.com, deployment scripts, and DEPLOYMENT.md guide.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:41:32 +01:00

94 lines
2.0 KiB
TypeScript

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],
}
}