import { NextRequest, NextResponse } from "next/server"; import bcrypt from "bcryptjs"; import crypto from "crypto"; import { db } from "@/lib/db"; import { ACCESS_TOKEN_EXPIRES_IN, REFRESH_TOKEN_EXPIRES_IN, createMobileTokenPair, } from "@/lib/mobileTokens"; const IRAN_MOBILE_REGEX = /^(\+98|98|0)?9\d{9}$/; function normalizeIranMobile(phone: string) { const digits = phone.replace(/[^\d+]/g, ""); if (!IRAN_MOBILE_REGEX.test(digits)) return null; if (digits.startsWith("+98")) return `0${digits.slice(3)}`; if (digits.startsWith("98")) return `0${digits.slice(2)}`; if (digits.startsWith("9")) return `0${digits}`; return digits; } function buildPhoneEmail(phone: string) { return `${phone}@mobile.local`; } export async function POST(req: NextRequest) { const { phone, code, name } = await req.json().catch(() => ({})); const normalizedPhone = typeof phone === "string" ? normalizeIranMobile(phone) : null; const normalizedCode = typeof code === "string" ? code.trim() : ""; if (!normalizedPhone || !/^\d{6}$/.test(normalizedCode)) { return NextResponse.json({ error: "شماره موبایل یا کد ورود معتبر نیست" }, { status: 400 }); } const otp = await db.loginOtp.findFirst({ where: { phone: normalizedPhone, consumedAt: null, expiresAt: { gt: new Date() }, }, orderBy: { createdAt: "desc" }, }); if (!otp || otp.attempts >= 5) { return NextResponse.json({ error: "کد ورود نامعتبر یا منقضی شده است" }, { status: 400 }); } const isValidCode = await bcrypt.compare(normalizedCode, otp.codeHash); if (!isValidCode) { await db.loginOtp.update({ where: { id: otp.id }, data: { attempts: { increment: 1 } }, }); return NextResponse.json({ error: "کد ورود نامعتبر است" }, { status: 400 }); } const user = await db.user.upsert({ where: { phone: normalizedPhone }, update: { name: typeof name === "string" && name.trim() ? name.trim() : undefined, }, create: { phone: normalizedPhone, email: buildPhoneEmail(normalizedPhone), name: typeof name === "string" && name.trim() ? name.trim() : null, password: await bcrypt.hash(crypto.randomBytes(24).toString("hex"), 10), }, }); await db.loginOtp.update({ where: { id: otp.id }, data: { consumedAt: new Date() }, }); const tokens = await createMobileTokenPair(user.id); return NextResponse.json({ accessToken: tokens.accessToken, token: tokens.accessToken, tokenType: "Bearer", expiresIn: ACCESS_TOKEN_EXPIRES_IN, expiresAt: tokens.accessTokenExpiresAt.toISOString(), refreshToken: tokens.refreshToken, refreshExpiresIn: REFRESH_TOKEN_EXPIRES_IN, refreshExpiresAt: tokens.refreshTokenExpiresAt.toISOString(), user: { id: user.id, name: user.name, phone: user.phone, role: user.role, }, }); }