Files
alrahma_sunday_school_api/apps/api/dist/modules/auth/auth.employee.service.js
T
2026-06-11 03:22:12 -04:00

180 lines
7.7 KiB
JavaScript

"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