Olá!
+O sistema ${clientName} solicitou conexão com sua conta NewWhats.
+Válido por 10 minutos. Se não foi você, ignore este email.
+ `, + }) + + logger.info(`[pair] OTP enviado para ${to}`) + return true +} + +// ─── PairService ────────────────────────────────────────────────────────────── + +export class PairService { + + // Etapa 1 — solicitar pareamento + async request(input: { + email: string + clientSystem: string + clientName: string + clientUrl: string | null + }) { + const user = await prisma.user.findUnique({ where: { email: input.email } }) + if (!user) { + // Resposta genérica — não revela se o email existe ou não + return { ok: true, message: 'Se o email existir, um código será enviado.' } + } + + if (!user.isActive) { + throw Object.assign(new Error('Conta desativada'), { statusCode: 403 }) + } + + // Gera OTP de 6 dígitos + const code = Math.floor(100_000 + Math.random() * 900_000).toString() + + const payload: OtpPayload = { + code, + clientSystem: input.clientSystem, + clientName: input.clientName, + clientUrl: input.clientUrl, + } + + await dragonfly.setJson(otpKey(input.email), payload, OTP_TTL) + const emailSent = await sendOtpEmail(input.email, code, input.clientName) + + // Quando SMTP não está configurado, devolve o código na resposta + // para que o admin possa completar o pareamento manualmente. + if (!emailSent) { + return { ok: true, message: 'SMTP não configurado.', dev_code: code } + } + + return { ok: true, message: 'Se o email existir, um código será enviado.' } + } + + // Etapa 2 — confirmar com o código recebido + async confirm(input: { + email: string + code: string + clientSystem: string + clientName: string + clientUrl: string | null + }) { + const stored = await dragonfly.getJsonNenhuma conversa encontrada
+