216 lines
6.4 KiB
JavaScript
216 lines
6.4 KiB
JavaScript
'use strict';
|
|
|
|
const { User, sequelize } = require('../../models');
|
|
const { isAdmin } = require('../../services/rolesService');
|
|
const { logError } = require('../../services/logService');
|
|
const { getConfig } = require('../../config/config');
|
|
const {
|
|
isRegistrationEnabled,
|
|
createUnverifiedUser,
|
|
sendVerificationEmail,
|
|
verifyUserEmail,
|
|
} = require('./registrationService');
|
|
const packageJson = require('../../../package.json');
|
|
const {
|
|
ValidationError,
|
|
NotFoundError,
|
|
UnauthorizedError,
|
|
ForbiddenError,
|
|
} = require('../../shared/errors');
|
|
|
|
class AuthService {
|
|
getVersion() {
|
|
return { version: packageJson.version };
|
|
}
|
|
|
|
async getRegistrationStatus() {
|
|
return { enabled: await isRegistrationEnabled() };
|
|
}
|
|
|
|
async register(email, password) {
|
|
const transaction = await sequelize.transaction();
|
|
|
|
try {
|
|
if (!(await isRegistrationEnabled())) {
|
|
await transaction.rollback();
|
|
throw new NotFoundError('Registration is not enabled');
|
|
}
|
|
|
|
if (!email || !password) {
|
|
await transaction.rollback();
|
|
throw new ValidationError('Email and password are required');
|
|
}
|
|
|
|
const { user, verificationToken } = await createUnverifiedUser(
|
|
email,
|
|
password,
|
|
transaction
|
|
);
|
|
|
|
const emailResult = await sendVerificationEmail(
|
|
user,
|
|
verificationToken
|
|
);
|
|
|
|
if (!emailResult.success) {
|
|
await transaction.rollback();
|
|
logError(
|
|
new Error(emailResult.reason),
|
|
'Email sending failed during registration, rolling back user creation'
|
|
);
|
|
throw new Error(
|
|
'Failed to send verification email. Please try again later.'
|
|
);
|
|
}
|
|
|
|
await transaction.commit();
|
|
|
|
return {
|
|
message:
|
|
'Registration successful. Please check your email to verify your account.',
|
|
};
|
|
} catch (error) {
|
|
if (!transaction.finished) {
|
|
await transaction.rollback();
|
|
}
|
|
|
|
if (error.message === 'Email already registered') {
|
|
throw new ValidationError(error.message);
|
|
}
|
|
if (
|
|
error.message === 'Invalid email format' ||
|
|
error.message === 'Password must be at least 6 characters long'
|
|
) {
|
|
throw new ValidationError(error.message);
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async verifyEmail(token) {
|
|
if (!token) {
|
|
throw new ValidationError('Verification token is required');
|
|
}
|
|
|
|
try {
|
|
await verifyUserEmail(token);
|
|
const config = getConfig();
|
|
return { redirect: `${config.frontendUrl}/login?verified=true` };
|
|
} catch (error) {
|
|
const config = getConfig();
|
|
let errorParam = 'invalid';
|
|
|
|
if (error.message === 'Email already verified') {
|
|
errorParam = 'already_verified';
|
|
} else if (error.message === 'Verification token has expired') {
|
|
errorParam = 'expired';
|
|
}
|
|
|
|
logError('Email verification error:', error);
|
|
return {
|
|
redirect: `${config.frontendUrl}/login?verified=false&error=${errorParam}`,
|
|
};
|
|
}
|
|
}
|
|
|
|
async getCurrentUser(session) {
|
|
if (session && session.userId) {
|
|
const user = await User.findByPk(session.userId, {
|
|
attributes: [
|
|
'uid',
|
|
'email',
|
|
'name',
|
|
'surname',
|
|
'language',
|
|
'appearance',
|
|
'timezone',
|
|
'avatar_image',
|
|
],
|
|
});
|
|
if (user) {
|
|
const admin = await isAdmin(user.uid);
|
|
return {
|
|
user: {
|
|
uid: user.uid,
|
|
email: user.email,
|
|
name: user.name,
|
|
surname: user.surname,
|
|
language: user.language,
|
|
appearance: user.appearance,
|
|
timezone: user.timezone,
|
|
avatar_image: user.avatar_image,
|
|
is_admin: admin,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
return { user: null };
|
|
}
|
|
|
|
async login(email, password, session) {
|
|
if (!email || !password) {
|
|
throw new ValidationError('Invalid login parameters.');
|
|
}
|
|
|
|
const user = await User.findOne({ where: { email } });
|
|
if (!user) {
|
|
throw new UnauthorizedError('Invalid credentials');
|
|
}
|
|
|
|
const isValidPassword = await User.checkPassword(
|
|
password,
|
|
user.password_digest
|
|
);
|
|
if (!isValidPassword) {
|
|
throw new UnauthorizedError('Invalid credentials');
|
|
}
|
|
|
|
if (!user.email_verified) {
|
|
const error = new ForbiddenError(
|
|
'Please verify your email address before logging in.'
|
|
);
|
|
error.email_not_verified = true;
|
|
throw error;
|
|
}
|
|
|
|
session.userId = user.id;
|
|
|
|
await new Promise((resolve, reject) => {
|
|
session.save((err) => {
|
|
if (err) reject(err);
|
|
else resolve();
|
|
});
|
|
});
|
|
|
|
const admin = await isAdmin(user.uid);
|
|
return {
|
|
user: {
|
|
uid: user.uid,
|
|
email: user.email,
|
|
name: user.name,
|
|
surname: user.surname,
|
|
language: user.language,
|
|
appearance: user.appearance,
|
|
timezone: user.timezone,
|
|
avatar_image: user.avatar_image,
|
|
is_admin: admin,
|
|
},
|
|
};
|
|
}
|
|
|
|
logout(session) {
|
|
return new Promise((resolve, reject) => {
|
|
session.destroy((err) => {
|
|
if (err) {
|
|
logError('Logout error:', err);
|
|
reject(new Error('Could not log out'));
|
|
} else {
|
|
resolve({ message: 'Logged out successfully' });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = new AuthService();
|