"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getMe = getMe; exports.login = login; exports.forgotPassword = forgotPassword; exports.updateLanguage = updateLanguage; exports.resetPassword = resetPassword; const bcryptjs_1 = __importDefault(require("bcryptjs")); const crypto_1 = __importDefault(require("crypto")); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const tokens_1 = require("../../security/tokens"); const errors_1 = require("../../http/errors"); const notificationService_1 = require("../../services/notificationService"); const emailTranslations_1 = require("../../lib/emailTranslations"); const auth_presenter_1 = require("./auth.presenter"); const repo = __importStar(require("./auth.employee.repo")); const RESET_TOKEN_TTL_MINUTES = 60; function signEmployeeToken(employeeId) { return (0, tokens_1.signActorToken)(employeeId, 'employee', { expiresIn: (process.env.JWT_EXPIRY ?? '8h'), }); } function getEmployeePasswordResetVersion(passwordHash) { return crypto_1.default .createHash('sha256') .update(`${process.env.JWT_SECRET}:${passwordHash ?? 'no-password-set'}`) .digest('hex'); } function signEmployeePasswordResetToken(employeeId, passwordHash) { return jsonwebtoken_1.default.sign({ sub: employeeId, type: 'employee_password_reset', pwdv: getEmployeePasswordResetVersion(passwordHash), }, process.env.JWT_SECRET, { algorithm: 'HS256', issuer: 'rentaldrivego-api', audience: 'employee_password_reset', expiresIn: `${RESET_TOKEN_TTL_MINUTES}m`, }); } function verifyEmployeePasswordResetToken(token) { try { const payload = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], issuer: 'rentaldrivego-api', audience: 'employee_password_reset', }); if (typeof payload.sub !== 'string' || payload.type !== 'employee_password_reset' || typeof payload.pwdv !== 'string') { return null; } return payload; } catch { return null; } } function ensureDashboardBasePath(baseUrl) { try { const url = new URL(baseUrl); const pathname = url.pathname.replace(/\/$/, ''); if (!pathname.endsWith('/dashboard')) url.pathname = `${pathname}/dashboard`; return url.toString().replace(/\/$/, ''); } catch { const trimmed = baseUrl.replace(/\/$/, ''); return trimmed.endsWith('/dashboard') ? trimmed : `${trimmed}/dashboard`; } } async function getMe(employeeId) { const employee = await repo.findEmployeeWithCompanyById(employeeId); if (!employee || !employee.isActive) { throw new errors_1.AppError('Employee account not found or inactive', 401, 'unauthenticated'); } return (0, auth_presenter_1.presentEmployeeSession)(employee); } async function login(body) { const employee = await repo.findEmployeeWithCompanyByEmail(body.email); if (!employee || !employee.isActive) { throw new errors_1.AppError('Invalid email or password', 401, 'invalid_credentials'); } if (!employee.passwordHash) { throw new errors_1.AppError('This account does not have a password yet. Use the invitation or reset email to set one first.', 401, 'password_not_set'); } const validPassword = await bcryptjs_1.default.compare(body.password, employee.passwordHash); if (!validPassword) { throw new errors_1.AppError('Invalid email or password', 401, 'invalid_credentials'); } return (0, auth_presenter_1.presentEmployeeSession)(employee, signEmployeeToken(employee.id)); } async function forgotPassword(email) { const employee = await repo.findActiveEmployeeByEmail(email); if (employee) { const resetToken = signEmployeePasswordResetToken(employee.id, employee.passwordHash); const dashboardUrl = ensureDashboardBasePath(process.env.DASHBOARD_URL ?? process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3000/dashboard'); const resetUrl = `${dashboardUrl}/reset-password?token=${encodeURIComponent(resetToken)}`; const lang = employee.preferredLanguage ?? 'fr'; await (0, notificationService_1.sendTransactionalEmail)({ to: employee.email, subject: emailTranslations_1.resetPasswordEmail.subject(lang), html: emailTranslations_1.resetPasswordEmail.html(resetUrl, employee.firstName, RESET_TOKEN_TTL_MINUTES, lang), text: emailTranslations_1.resetPasswordEmail.text(resetUrl, employee.firstName, RESET_TOKEN_TTL_MINUTES, lang), }).catch((err) => { console.error('[ForgotPassword] Email delivery failed:', err?.message); console.error('[ForgotPassword] Provider config — resendKey present:', !!process.env.RESEND_API_KEY, '| smtpHost:', process.env.MAIL_HOST ?? 'not set'); }); } return { message: 'If that email is registered, a reset link has been sent.' }; } async function updateLanguage(employeeId, language) { await repo.updatePreferredLanguage(employeeId, language); return { language }; } async function findEmployeeFromResetToken(token) { try { const employee = await repo.findEmployeeByResetToken(token); if (employee) return employee; } catch (err) { console.error('[EmployeeResetPassword] Stored token lookup failed:', err?.message ?? String(err)); } const payload = verifyEmployeePasswordResetToken(token); if (!payload) return null; const employee = await repo.findEmployeeById(payload.sub); if (!employee || !employee.isActive) return null; if (payload.pwdv !== getEmployeePasswordResetVersion(employee.passwordHash)) { return null; } return employee; } async function resetPassword(token, password) { const employee = await findEmployeeFromResetToken(token); if (!employee) { throw new errors_1.AppError('Reset link is invalid or has expired.', 400, 'invalid_token'); } await repo.resetPassword(employee.id, await bcryptjs_1.default.hash(password, 12)); return { message: 'Password updated successfully. You can now sign in.' }; } //# sourceMappingURL=auth.employee.service.js.map