import crypto from 'crypto-random-string' import md5 from 'md5' import { ObjectId } from 'mongodb' import { POST, QRequestController, SECURE, } from './../../modules/RequestController' const sendEmail = require('../../email/email') export default class UsersController extends QRequestController { @POST @SECURE({ allow: ['admin', 'manager'] }) async create() { const users = this.db.collection('users') const auth = this.session?.auth const newUser = this.body newUser.email = newUser.email.toLowerCase() const isExist = await users.findOne({ email: newUser.email }) if (isExist) { return this.answer( false, undefined, 'Пользователь с таким email уже существует' ) } const password = crypto({ length: 10 }) newUser.password = md5(password) newUser.createDate = new Date().toISOString() newUser.createdBy = auth._id // Проверка на подмену if (newUser.role === 'admin' && auth.role !== 'admin') { newUser.role = 'manager' } await users.insertOne(newUser) const config = this.config // Отправляем email c информацией о регистрации this.sendEmail( 'register', newUser.email, 'Для Вас создан аккаунт 🎉', newUser.name, newUser.email, password, `${config.protocol}://${config.staticIP}`, `${config.protocol}://${config.staticIP}?admin` ) return this.answer(true) } sendEmail( template: string, to: string, subject: string, name?: string, email?: string, password?: string, site?: string, link?: string, code?: string, who?: string ) { sendEmail( { template, to, subject, }, { name, email, password, site, link, code, who, } ) } @POST @SECURE({ allow: ['admin', 'manager'] }) async delete() { const users = this.db.collection('users') const auth = this.session?.auth const user = this.body const userToDelete = await users.findOne({ _id: new ObjectId(user._id) }) if (!userToDelete) { return this.answer( false, undefined, 'Не найден пользователь для удаления' ) } if ( !( auth.role === 'admin' || (auth.role === 'manager' && userToDelete.createdBy === auth._id) ) ) { return this.answer(false, undefined, 'Недостаточно прав') } await users.findOneAndDelete({ _id: new ObjectId(userToDelete._id) }) return this.answer(true) } @POST @SECURE() async update() { const users = this.db.collection('users') const auth = this.session?.auth const update = this.body const originalUser = await users.findOne({ _id: new ObjectId(update._id) }) if (!originalUser) { return this.answer( false, undefined, 'Не найден пользователь для обновления' ) } if ( !( auth.role === 'admin' || (auth.role === 'manager' ? auth._id === originalUser._id.toString() : originalUser.createdBy === auth._id) ) ) { return this.answer(false, undefined, 'Недостаточно прав') } delete update._id delete update.createdBy const { value } = await users.findOneAndUpdate( { _id: new ObjectId(originalUser._id) }, { $set: update }, { returnDocument: 'after' } ) // если обновляет сам себя if (auth._id === originalUser._id.toString()) { delete value?.password this.setSession('auth', value) } return this.answer(true) } @POST @SECURE({ allow: ['admin', 'manager'] }) async getAll() { const auth = this.session?.auth const users = await this.db .collection('users') .find({ _id: { $ne: new ObjectId(auth._id) } }) .project({ password: 0, admin: 0 }) .toArray() return this.answer(true, users) } @POST async anonReset() { const users = this.db.collection('users') const auth = this.session?.auth const email = this.body.email.toLowerCase() if (auth) { return this.answer(false, undefined, 'Вы уже авторизованы') } const user = await users.findOne({ email }) if (!user) { return this.answer( false, undefined, 'Пользователь с таким email не найден' ) } const numberCode = (length: number) => { let result = '' const characters = '0123456789' const charactersLength = characters.length for (let i = 0; i < length; i++) { result += characters.charAt( Math.floor(Math.random() * charactersLength) ) } return result } const resetPassCode = numberCode(4) await users.findOneAndUpdate( { _id: user._id }, { $set: { resetPassCode } }, { returnDocument: 'after' } ) // Таймер для сброса setTimeout( () => { users.findOneAndUpdate( { _id: user._id }, { $set: { resetPassCode: null } }, { returnDocument: 'after' } ) }, 1000 * 60 * 5 // 5 минут ) const config = this.config this.sendEmail( 'reset-code', user.email, 'Код для сброса пароля 🆘', user.name, undefined, undefined, `${config.protocol}://${config.staticIP}`, undefined, resetPassCode ) return this.answer(true) } @POST async resetPassword() { const users = this.db.collection('users') const auth = this.session?.auth const email = this.body.email.toLowerCase() const resetCode = this.body.resetCode // Проверяем на авторизацию if (!auth && !resetCode) { return this.answer(false, undefined, 'Недостаточно прав') } const userForUpdate = await users.findOne({ email }) if (!userForUpdate) { return this.answer( false, undefined, 'Не найден пользователь для смены пароля' ) } if (resetCode && userForUpdate.resetPassCode !== resetCode) { return this.answer(false, undefined, 'Неверный код восстановления') } // Создаем новый пароль const password = crypto({ length: 10 }) userForUpdate.password = md5(password) // Если меняет пароль не себе — то должен быть админом или менеджером if ( !resetCode && !(auth._id !== userForUpdate._id ? ['admin', 'manager'].includes(auth.role) && userForUpdate.role !== 'admin' : true) ) { return this.answer(false, undefined, 'Недостаточно прав') } await users.findOneAndUpdate( { _id: new ObjectId(userForUpdate._id) }, { $set: { password: userForUpdate.password } }, { returnDocument: 'after' } ) const config = this.config this.sendEmail( 'reset-password', userForUpdate.email, 'Ваш пароль сброшен 🔑', userForUpdate.name, undefined, password, `${config.protocol}://${config.staticIP}?admin`, `${config.protocol}://${config.staticIP}?admin`, undefined, !resetCode && auth._id !== userForUpdate._id ? `${auth.name} сбросил Ваш` : 'Вы сбросили' ) // Сбрасываем код подтверждения if (resetCode) { users.findOneAndUpdate( { _id: userForUpdate._id }, { $set: { resetPassCode: null } }, { returnDocument: 'after' } ) } return this.answer(true) } }