base commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import crypto from 'node:crypto'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { prisma } from './prisma.js'
|
||||
import { sendLoginCodeEmail } from './email.js'
|
||||
|
||||
export function normalizeEmail(email) {
|
||||
return String(email || '').trim().toLowerCase()
|
||||
}
|
||||
|
||||
export function randomCode6() {
|
||||
return String(Math.floor(100000 + Math.random() * 900000))
|
||||
}
|
||||
|
||||
export function sha256(input) {
|
||||
return crypto.createHash('sha256').update(input).digest('hex')
|
||||
}
|
||||
|
||||
export async function issueEmailCode({ email, purpose, userId = null }) {
|
||||
const code = randomCode6()
|
||||
const expiresAt = new Date(Date.now() + 10 * 60 * 1000)
|
||||
await prisma.authCode.create({
|
||||
data: {
|
||||
email,
|
||||
purpose,
|
||||
userId,
|
||||
codeHash: sha256(`${email}:${purpose}:${code}:${userId ?? ''}`),
|
||||
expiresAt,
|
||||
},
|
||||
})
|
||||
await sendLoginCodeEmail({ to: email, code })
|
||||
}
|
||||
|
||||
export async function verifyEmailCode({ email, purpose, code, userId = null }) {
|
||||
const now = new Date()
|
||||
const codeHash = sha256(`${email}:${purpose}:${code}:${userId ?? ''}`)
|
||||
|
||||
const found = await prisma.authCode.findFirst({
|
||||
where: {
|
||||
email,
|
||||
purpose,
|
||||
userId,
|
||||
codeHash,
|
||||
usedAt: null,
|
||||
expiresAt: { gt: now },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
if (!found) return false
|
||||
|
||||
await prisma.authCode.update({
|
||||
where: { id: found.id },
|
||||
data: { usedAt: now },
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
export async function hashPassword(password) {
|
||||
return bcrypt.hash(password, 10)
|
||||
}
|
||||
|
||||
export async function verifyPassword(password, passwordHash) {
|
||||
return bcrypt.compare(password, passwordHash)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
function hasSmtpEnv() {
|
||||
return Boolean(process.env.SMTP_HOST && process.env.SMTP_PORT && process.env.SMTP_USER && process.env.SMTP_PASS)
|
||||
}
|
||||
|
||||
export async function sendLoginCodeEmail({ to, code }) {
|
||||
if (!hasSmtpEnv()) {
|
||||
// dev fallback
|
||||
console.log(`[DEV] login code for ${to}: ${code}`)
|
||||
return
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT),
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
})
|
||||
|
||||
const from = process.env.MAIL_FROM || process.env.SMTP_USER
|
||||
|
||||
await transporter.sendMail({
|
||||
from,
|
||||
to,
|
||||
subject: 'Код входа',
|
||||
text: `Ваш код: ${code}\n\nЕсли это были не вы — просто проигнорируйте письмо.`,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user