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 { // 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}"` ) } }