api/app/services/auth_service.ts
2025-06-19 15:15:59 +02:00

107 lines
3.1 KiB
TypeScript

import encryption from '@adonisjs/core/services/encryption'
import cache from '@adonisjs/cache/services/main'
import env from '#start/env'
import User from '#models/user'
import { CmpStrAsync } from 'cmpstr'
const cmp = CmpStrAsync.create().setMetric('levenshtein').setFlags('i')
export class AuthService {
async generateToken(email: string, userId: number, expiresIn: string) {
// Generate magic link token
const identifier = `${email}:${userId}`
const token = encryption.encrypt(identifier, expiresIn)
const magicLink = env.get('PUBLIC_AUTH_URL') + '/success?signature=' + token
// Generate code
const formattedOTP = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, '0')
await cache.set({
key: 'auth:otp:' + formattedOTP,
value: identifier,
ttl: expiresIn,
})
const emailTitle = `${formattedOTP} est votre code de vérification pour ${env.get('APP_NAME')}`
return {
emailTitle,
formattedOTP,
magicLink,
expiresIn,
email,
token,
}
}
parseIdentifier(id: string) {
const [email, userId] = id.split(':')
return {
email: email.toLowerCase(),
userId: parseInt(userId, 10),
}
}
async validateCode(code: string) {
// Validate code
const key = 'auth:otp:' + code
const id = await cache.get({ key })
if (!id) return { success: false, userId: null, email: null }
// Delete code from cache
await cache.delete({ key })
return {
success: true,
...this.parseIdentifier(id)
}
}
parseNameFromEmail(email: string): Promise<string> {
// Parse name from email
return new Promise((resolve, reject) => {
try {
const [firstName, lastName] = email.split('@')[0].split('.')
resolve(`${firstName} ${lastName}`.toLowerCase())
} catch (error) {
reject(new Error('Invalid email format'))
}
})
}
findUserByEmail(email: string) {
return User.query().where('email', email).first()
}
async findUser(email: string) {
// Try to find user by email
let user: User | null | undefined = await this.findUserByEmail(email)
if (user) return user
// If not found, try to parse name from email and find user by name
const name = await this.parseNameFromEmail(email)
const users = await User.query()
// Select all users with no email
.whereNull('email')
const names = users.map((user) => `${user.firstName} ${user.lastName}`.toLowerCase())
// Search for similar names
const data = await cmp.searchAsync(name, names)
if (data.length === 0) {
console.warn(`No user found for email "${email}" or name "${name}"`)
return null
}
const source = data[0]
const { match: similarity } = await cmp.testAsync(source, name)
console.log(similarity)
// If similarity is high enough, return the user
if (similarity > 0.8) {
user = users.find((u) => `${u.firstName} ${u.lastName}`.toLowerCase() === source)
return user
}
console.warn(
`No user found for email "${email}" or name "${name}". Similarity: ${similarity} with source "${source}"`
)
}
}