mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 18:29:15 +03:00
added: MeService
This commit is contained in:
parent
a852e82451
commit
e6880e3e0e
@ -1,12 +1,10 @@
|
|||||||
import { Exception } from '@adonisjs/core/build/standalone';
|
import { Exception, inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
import Hash from '@ioc:Adonis/Core/Hash';
|
|
||||||
import Logger from '@ioc:Adonis/Core/Logger';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models
|
* Services
|
||||||
*/
|
*/
|
||||||
import Contact from 'App/Models/Contact';
|
import MeService from 'App/Services/MeService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validators
|
* Validators
|
||||||
@ -14,89 +12,60 @@ import Contact from 'App/Models/Contact';
|
|||||||
import ChangePasswordValidator from 'App/Validators/Password/ChangePasswordValidator';
|
import ChangePasswordValidator from 'App/Validators/Password/ChangePasswordValidator';
|
||||||
import UpdateContactsValidator from 'App/Validators/Contacts/UpdateContactsValidator';
|
import UpdateContactsValidator from 'App/Validators/Contacts/UpdateContactsValidator';
|
||||||
|
|
||||||
export default class MeController {
|
import BaseController from '../../BaseController';
|
||||||
private readonly guard: 'api';
|
|
||||||
|
|
||||||
private readonly Contact: typeof Contact;
|
@inject()
|
||||||
|
export default class MeController extends BaseController {
|
||||||
|
private meService: MeService;
|
||||||
|
|
||||||
constructor() {
|
constructor(meService: MeService) {
|
||||||
this.Contact = Contact;
|
super();
|
||||||
|
this.meService = meService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows user info of the access token resource owner.
|
* Shows user info of the access token resource owner.
|
||||||
* GET /me
|
* GET /me
|
||||||
*/
|
*/
|
||||||
public async show({ response, auth }: HttpContextContract) {
|
public async show(ctx: HttpContextContract) {
|
||||||
const user = await auth.use(this.guard).authenticate();
|
const result = await this.meService.fetchUserContacts(ctx.auth);
|
||||||
|
|
||||||
await user.load('contacts');
|
if (!result.success && result.error) {
|
||||||
await user.load('roles');
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
return response.ok({
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
message: 'Fetched data about me.',
|
|
||||||
data: user,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change password of authenticated user
|
* Change password of authenticated user
|
||||||
* PATCH /me/password
|
* PATCH /me/password
|
||||||
*/
|
*/
|
||||||
public async changePassword({ request, response, auth }: HttpContextContract) {
|
public async changePassword(ctx: HttpContextContract) {
|
||||||
await request.validate(ChangePasswordValidator);
|
const payload = await ctx.request.validate(ChangePasswordValidator);
|
||||||
|
const result = await this.meService.changeUserPassword(payload.oldPassword, payload.newPassword, ctx.auth);
|
||||||
|
|
||||||
const user = await auth.use(this.guard).authenticate();
|
if (!result.success && result.error) {
|
||||||
const oldPassword: string = request.input('oldPassword');
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
const newPassword: string = request.input('newPassword');
|
|
||||||
|
|
||||||
if (oldPassword === newPassword) {
|
|
||||||
throw new Exception('The new password is the same as the old one.', 400, 'E_PASSWORD_VALUES');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
* Verify old password
|
|
||||||
*/
|
|
||||||
if (!(await Hash.verify(user.password, oldPassword))) {
|
|
||||||
throw new Exception('Invalid credentials.', 403, 'E_INVALID_CREDENTIALS');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update user password
|
|
||||||
*/
|
|
||||||
user.password = newPassword;
|
|
||||||
await user.save();
|
|
||||||
|
|
||||||
Logger.info(`Password of user with id: "${user.id}" was updated.`);
|
|
||||||
|
|
||||||
return response.ok({
|
|
||||||
message: 'The password was successfully changed to the new value.',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update contacts of authenticated user
|
* Update contacts of authenticated user
|
||||||
* PATCH /me/contacts
|
* PUT /me/contacts
|
||||||
*/
|
*/
|
||||||
public async updateContacts({ request, response, auth }: HttpContextContract) {
|
public async updateContacts(ctx: HttpContextContract) {
|
||||||
const payload = await request.validate(UpdateContactsValidator);
|
const payload = await ctx.request.validate(UpdateContactsValidator);
|
||||||
const user = await auth.use(this.guard).authenticate();
|
const result = await this.meService.updateUserContacts(payload, ctx.auth);
|
||||||
const contacts = await this.Contact.findByOrFail('user_id', user.id);
|
|
||||||
|
|
||||||
/**
|
if (!result.success && result.error) {
|
||||||
* Update contacts
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
*/
|
}
|
||||||
if (payload.email) contacts.email = payload.email;
|
|
||||||
contacts.phone_number = payload.phone_number ?? null;
|
|
||||||
contacts.vk_id = payload.vk_id ?? null;
|
|
||||||
contacts.twitter_id = payload.twitter_id ?? null;
|
|
||||||
contacts.telegram_id = payload.telegram_id ?? null;
|
|
||||||
|
|
||||||
await contacts.save();
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
|
||||||
return response.ok({
|
|
||||||
message: `Contacts has been successfully updated.`,
|
|
||||||
data: contacts,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new Ioc().make(MeController);
|
||||||
|
|||||||
12
app/Datatypes/Enums/StatusCodeEnum.ts
Normal file
12
app/Datatypes/Enums/StatusCodeEnum.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
enum StatusCodeEnum {
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
INTERNAL_SERVER = 500,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatusCodeEnum;
|
||||||
@ -9,9 +9,6 @@ export default class Contact extends BaseModel {
|
|||||||
@column({ serializeAs: null })
|
@column({ serializeAs: null })
|
||||||
public user_id: string;
|
public user_id: string;
|
||||||
|
|
||||||
@column()
|
|
||||||
public email: string;
|
|
||||||
|
|
||||||
@column()
|
@column()
|
||||||
public phone_number: string | null;
|
public phone_number: string | null;
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,9 @@ export default class User extends BaseModel {
|
|||||||
@column({ serializeAs: null })
|
@column({ serializeAs: null })
|
||||||
public login: string;
|
public login: string;
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public email: string;
|
||||||
|
|
||||||
@column({ serializeAs: null })
|
@column({ serializeAs: null })
|
||||||
public password: string;
|
public password: string;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Models
|
||||||
|
*/
|
||||||
import Contact from 'App/Models/Contact';
|
import Contact from 'App/Models/Contact';
|
||||||
import User from 'App/Models/User';
|
import User from 'App/Models/User';
|
||||||
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
|
|
||||||
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
|
/**
|
||||||
|
* Validators
|
||||||
|
*/
|
||||||
|
import CreateContactsValidator from 'App/Validators/Contacts/CreateContactsValidator';
|
||||||
|
import UpdateContactsValidator from 'App/Validators/Contacts/UpdateContactsValidator';
|
||||||
|
|
||||||
export default class ContactRepository {
|
export default class ContactRepository {
|
||||||
private Contact: typeof Contact;
|
private Contact: typeof Contact;
|
||||||
@ -10,6 +17,17 @@ export default class ContactRepository {
|
|||||||
this.Contact = Contact;
|
this.Contact = Contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get contacts by user id
|
||||||
|
*
|
||||||
|
* @param userId User id
|
||||||
|
* @returns User contacts or null
|
||||||
|
*/
|
||||||
|
public async getByUser(userId: string | number): Promise<Contact | null> {
|
||||||
|
const contacts = await this.Contact.query().where('user_id', userId).first();
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create user contacts in database
|
* Create user contacts in database
|
||||||
*
|
*
|
||||||
@ -17,10 +35,17 @@ export default class ContactRepository {
|
|||||||
* @param data Data contacts
|
* @param data Data contacts
|
||||||
* @returns Created contacts
|
* @returns Created contacts
|
||||||
*/
|
*/
|
||||||
public async create(user: User, data: Pick<CreateUserValidator['schema']['props'], 'email'>) {
|
public async create(user: User, data: CreateContactsValidator['schema']['props']): Promise<Contact> {
|
||||||
const contacts = await this.Contact.create(data);
|
const contacts = await this.Contact.create(data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate user
|
||||||
|
*/
|
||||||
await contacts.related('user').associate(user);
|
await contacts.related('user').associate(user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load updated contacts to user
|
||||||
|
*/
|
||||||
await user.load('contacts');
|
await user.load('contacts');
|
||||||
await user.load('roles');
|
await user.load('roles');
|
||||||
|
|
||||||
@ -32,13 +57,16 @@ export default class ContactRepository {
|
|||||||
*
|
*
|
||||||
* @param user User
|
* @param user User
|
||||||
* @param data Data to update
|
* @param data Data to update
|
||||||
* @returns
|
* @returns Updated contacts or null
|
||||||
*/
|
*/
|
||||||
public async update(user: User, data: Pick<UpdateUserValidator['schema']['props'], 'email'>) {
|
public async update(user: User, data: UpdateContactsValidator['schema']['props']): Promise<Contact | null> {
|
||||||
const contacts = await this.Contact.query().where('user_id', user.id).first();
|
const contacts = await this.Contact.query().where('user_id', user.id).first();
|
||||||
|
|
||||||
if (contacts) {
|
if (contacts) {
|
||||||
contacts.email = data.email;
|
contacts.phone_number = data.phone_number ?? null;
|
||||||
|
contacts.telegram_id = data.telegram_id ?? null;
|
||||||
|
contacts.twitter_id = data.twitter_id ?? null;
|
||||||
|
contacts.vk_id = data.vk_id ?? null;
|
||||||
|
|
||||||
await contacts.save();
|
await contacts.save();
|
||||||
await user.load('contacts');
|
await user.load('contacts');
|
||||||
@ -46,15 +74,6 @@ export default class ContactRepository {
|
|||||||
return contacts;
|
return contacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return null;
|
||||||
* Create new user contacts if contacts doesn't exist
|
|
||||||
*/
|
|
||||||
await user.related('contacts').create({
|
|
||||||
email: data.email,
|
|
||||||
});
|
|
||||||
|
|
||||||
await user.load('contacts');
|
|
||||||
|
|
||||||
return user.contacts;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Models
|
||||||
|
*/
|
||||||
import Role from 'App/Models/Role';
|
import Role from 'App/Models/Role';
|
||||||
|
|
||||||
export default class RoleRepository {
|
export default class RoleRepository {
|
||||||
|
|||||||
@ -1,5 +1,15 @@
|
|||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
|
||||||
|
import Hash from '@ioc:Adonis/Core/Hash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models
|
||||||
|
*/
|
||||||
import User from 'App/Models/User';
|
import User from 'App/Models/User';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validators
|
||||||
|
*/
|
||||||
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
|
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
|
||||||
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
|
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
|
||||||
|
|
||||||
@ -16,7 +26,7 @@ export default class UserRepository {
|
|||||||
* @param id User id
|
* @param id User id
|
||||||
* @returns User or null
|
* @returns User or null
|
||||||
*/
|
*/
|
||||||
public async getById(id: string | number) {
|
public async getById(id: string | number): Promise<User | null> {
|
||||||
const data = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
|
const data = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -28,7 +38,7 @@ export default class UserRepository {
|
|||||||
* @param value Comparsion column
|
* @param value Comparsion column
|
||||||
* @returns User or null
|
* @returns User or null
|
||||||
*/
|
*/
|
||||||
public async getByColumn(column: string, value: string | number) {
|
public async getByColumn(column: string, value: string | number): Promise<User | null> {
|
||||||
const data = await this.User.query().preload('contacts').preload('roles').where(column, value).first();
|
const data = await this.User.query().preload('contacts').preload('roles').where(column, value).first();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -39,7 +49,7 @@ export default class UserRepository {
|
|||||||
* @param role Role
|
* @param role Role
|
||||||
* @returns Array of users with role
|
* @returns Array of users with role
|
||||||
*/
|
*/
|
||||||
public async getAllByRole(role: string) {
|
public async getAllByRole(role: string): Promise<User[]> {
|
||||||
const data = await this.User.query()
|
const data = await this.User.query()
|
||||||
.preload('contacts')
|
.preload('contacts')
|
||||||
.preload('roles')
|
.preload('roles')
|
||||||
@ -53,7 +63,7 @@ export default class UserRepository {
|
|||||||
* @param params Params to find
|
* @param params Params to find
|
||||||
* @returns Array of users
|
* @returns Array of users
|
||||||
*/
|
*/
|
||||||
public async getAll(params?: any) {
|
public async getAll(params?: any): Promise<User[]> {
|
||||||
const { email, login, first_name, last_name, role }: any = params || {};
|
const { email, login, first_name, last_name, role }: any = params || {};
|
||||||
const query = this.User.query();
|
const query = this.User.query();
|
||||||
|
|
||||||
@ -87,14 +97,8 @@ export default class UserRepository {
|
|||||||
* @param data Data for create user
|
* @param data Data for create user
|
||||||
* @returns Created user
|
* @returns Created user
|
||||||
*/
|
*/
|
||||||
public async create(data: CreateUserValidator['schema']['props']) {
|
public async create(data: CreateUserValidator['schema']['props']): Promise<User> {
|
||||||
const createdUser = await this.User.create({
|
const createdUser = await this.User.create(data);
|
||||||
first_name: data.first_name,
|
|
||||||
last_name: data.last_name,
|
|
||||||
login: data.login,
|
|
||||||
password: data.password,
|
|
||||||
});
|
|
||||||
|
|
||||||
return createdUser;
|
return createdUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,18 +109,11 @@ export default class UserRepository {
|
|||||||
* @param data Data to update
|
* @param data Data to update
|
||||||
* @returns User or null if user is not found
|
* @returns User or null if user is not found
|
||||||
*/
|
*/
|
||||||
public async update(id: number | string, data: UpdateUserValidator['schema']['props']) {
|
public async update(id: number | string, data: UpdateUserValidator['schema']['props']): Promise<User | null> {
|
||||||
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
|
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
await user
|
await user.merge(data).save();
|
||||||
.merge({
|
|
||||||
first_name: data.first_name,
|
|
||||||
last_name: data.last_name,
|
|
||||||
login: data.login,
|
|
||||||
password: data.password,
|
|
||||||
})
|
|
||||||
.save();
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +126,7 @@ export default class UserRepository {
|
|||||||
* @param id User id
|
* @param id User id
|
||||||
* @returns Deleted user or null if user is not found
|
* @returns Deleted user or null if user is not found
|
||||||
*/
|
*/
|
||||||
public async delete(id: number | string) {
|
public async delete(id: number | string): Promise<User | null> {
|
||||||
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
|
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
@ -139,4 +136,9 @@ export default class UserRepository {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async updatePassword(password: string, id: string | number): Promise<any> {
|
||||||
|
const hashedPass = await Hash.make(password);
|
||||||
|
return this.User.query().where('id', id).update({ password: hashedPass }).first();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,11 @@ import Logger from '@ioc:Adonis/Core/Logger';
|
|||||||
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||||
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enums
|
||||||
|
*/
|
||||||
|
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reposirories
|
* Reposirories
|
||||||
*/
|
*/
|
||||||
@ -17,7 +22,7 @@ import UserRepository from 'App/Repositories/UserRepository';
|
|||||||
export default class AuthService {
|
export default class AuthService {
|
||||||
private userRepository: UserRepository;
|
private userRepository: UserRepository;
|
||||||
|
|
||||||
private guard: 'api';
|
private authGuard: 'api';
|
||||||
|
|
||||||
constructor(userRepository: UserRepository) {
|
constructor(userRepository: UserRepository) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
@ -37,11 +42,11 @@ export default class AuthService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
message: 'User not found.',
|
message: 'User not found.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_USER_NOT_FOUND',
|
code: 'E_NOT_FOUND',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -52,7 +57,7 @@ export default class AuthService {
|
|||||||
if (!(await Hash.verify(user.password, password))) {
|
if (!(await Hash.verify(user.password, password))) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 403,
|
status: StatusCodeEnum.UNAUTHORIZED,
|
||||||
message: 'Invalid credentials.',
|
message: 'Invalid credentials.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
@ -64,7 +69,7 @@ export default class AuthService {
|
|||||||
/**
|
/**
|
||||||
* Create new token
|
* Create new token
|
||||||
*/
|
*/
|
||||||
const token = await auth.use(this.guard).generate(user, {
|
const token = await auth.use(this.authGuard).generate(user, {
|
||||||
expiresIn: '1d',
|
expiresIn: '1d',
|
||||||
userRoles: user.roles.map(role => role.slug),
|
userRoles: user.roles.map(role => role.slug),
|
||||||
});
|
});
|
||||||
@ -73,7 +78,7 @@ export default class AuthService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'Login success.',
|
message: 'Login success.',
|
||||||
data: token.toJSON(),
|
data: token.toJSON(),
|
||||||
};
|
};
|
||||||
@ -86,11 +91,11 @@ export default class AuthService {
|
|||||||
* @returns Logout message
|
* @returns Logout message
|
||||||
*/
|
*/
|
||||||
public async logout(auth: AuthContract): Promise<IResponse> {
|
public async logout(auth: AuthContract): Promise<IResponse> {
|
||||||
await auth.use(this.guard).revoke();
|
await auth.use(this.authGuard).revoke();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'Logout success.',
|
message: 'Logout success.',
|
||||||
data: {},
|
data: {},
|
||||||
};
|
};
|
||||||
|
|||||||
134
app/Services/MeService.ts
Normal file
134
app/Services/MeService.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
|
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||||
|
import Hash from '@ioc:Adonis/Core/Hash';
|
||||||
|
import Logger from '@ioc:Adonis/Core/Logger';
|
||||||
|
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
||||||
|
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||||
|
import ContactRepository from 'App/Repositories/ContactRepository';
|
||||||
|
import UserRepository from 'App/Repositories/UserRepository';
|
||||||
|
import UpdateContactsValidator from 'App/Validators/Contacts/UpdateContactsValidator';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class MeService {
|
||||||
|
private userRepository: UserRepository;
|
||||||
|
|
||||||
|
private contactRepository: ContactRepository;
|
||||||
|
|
||||||
|
private authGuard: 'api';
|
||||||
|
|
||||||
|
constructor(userRepository: UserRepository, contactRepository: ContactRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.contactRepository = contactRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get authorized user data
|
||||||
|
*
|
||||||
|
* @param auth AuthContract
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async fetchUserContacts(auth: AuthContract): Promise<IResponse> {
|
||||||
|
const user = await auth.use(this.authGuard).authenticate();
|
||||||
|
|
||||||
|
await user.load('roles');
|
||||||
|
await user.load('contacts');
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Fetched authorized user data.',
|
||||||
|
data: user,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change password of authorized user
|
||||||
|
*
|
||||||
|
* @param oldPassword Old password
|
||||||
|
* @param newPassword New password
|
||||||
|
* @param auth AuthContract
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async changeUserPassword(oldPassword: string, newPassword: string, auth: AuthContract): Promise<IResponse> {
|
||||||
|
const user = await auth.use(this.authGuard).authenticate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify old password
|
||||||
|
*/
|
||||||
|
if (!(await Hash.verify(user.password, oldPassword))) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.UNAUTHORIZED,
|
||||||
|
message: 'Invalid credentials.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_INVALID_CREDENTIALS',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update password
|
||||||
|
*/
|
||||||
|
const passwordUpdated = await this.userRepository.updatePassword(newPassword, user.id);
|
||||||
|
|
||||||
|
if (passwordUpdated) {
|
||||||
|
Logger.info(`Password of user with id: "${user.id}" was updated.`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Password updated.',
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.BAD_REQUEST,
|
||||||
|
message: 'Unable to change password',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_BAD_REQUEST',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user conctact
|
||||||
|
*
|
||||||
|
* @param data Data to update
|
||||||
|
* @param auth AuthContract
|
||||||
|
* @returns Updated user contacts
|
||||||
|
*/
|
||||||
|
public async updateUserContacts(
|
||||||
|
data: UpdateContactsValidator['schema']['props'],
|
||||||
|
auth: AuthContract
|
||||||
|
): Promise<IResponse> {
|
||||||
|
const user = await auth.use(this.authGuard).authenticate();
|
||||||
|
const contacts = await this.contactRepository.update(user, data);
|
||||||
|
|
||||||
|
if (contacts) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'User contacts updated.',
|
||||||
|
data: contacts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create user contacts if they don't exist
|
||||||
|
*/
|
||||||
|
await this.contactRepository.create(user, data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'User contacts updated.',
|
||||||
|
data: user.contacts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Ioc().make(MeService);
|
||||||
@ -5,6 +5,11 @@ import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
|||||||
*/
|
*/
|
||||||
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enums
|
||||||
|
*/
|
||||||
|
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models
|
* Models
|
||||||
*/
|
*/
|
||||||
@ -13,7 +18,6 @@ import Role from 'App/Models/Role';
|
|||||||
/**
|
/**
|
||||||
* Repositories
|
* Repositories
|
||||||
*/
|
*/
|
||||||
import ContactRepository from 'App/Repositories/ContactRepository';
|
|
||||||
import RoleRepository from 'App/Repositories/RoleRepository';
|
import RoleRepository from 'App/Repositories/RoleRepository';
|
||||||
import UserRepository from 'App/Repositories/UserRepository';
|
import UserRepository from 'App/Repositories/UserRepository';
|
||||||
|
|
||||||
@ -27,13 +31,10 @@ import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
|
|||||||
export default class UserService {
|
export default class UserService {
|
||||||
private userRepository: UserRepository;
|
private userRepository: UserRepository;
|
||||||
|
|
||||||
private contactRepository: ContactRepository;
|
|
||||||
|
|
||||||
private roleRepository: RoleRepository;
|
private roleRepository: RoleRepository;
|
||||||
|
|
||||||
constructor(userRepository: UserRepository, contactRepository: ContactRepository, roleRepository: RoleRepository) {
|
constructor(userRepository: UserRepository, roleRepository: RoleRepository) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.contactRepository = contactRepository;
|
|
||||||
this.roleRepository = roleRepository;
|
this.roleRepository = roleRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,18 +50,18 @@ export default class UserService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
message: 'User not found.',
|
message: 'User not found.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_USER_NOT_FOUND',
|
code: 'E_NOT_FOUND',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'Fetched user.',
|
message: 'Fetched user.',
|
||||||
data: user,
|
data: user,
|
||||||
};
|
};
|
||||||
@ -77,7 +78,7 @@ export default class UserService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'Fetched all users.',
|
message: 'Fetched all users.',
|
||||||
data: users,
|
data: users,
|
||||||
};
|
};
|
||||||
@ -92,12 +93,9 @@ export default class UserService {
|
|||||||
public async createUser(data: CreateUserValidator['schema']['props']): Promise<IResponse> {
|
public async createUser(data: CreateUserValidator['schema']['props']): Promise<IResponse> {
|
||||||
const user = await this.userRepository.create(data);
|
const user = await this.userRepository.create(data);
|
||||||
|
|
||||||
/* Associate user contacts */
|
|
||||||
await this.contactRepository.create(user, { email: data.email });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 201,
|
status: StatusCodeEnum.CREATED,
|
||||||
message: 'User created.',
|
message: 'User created.',
|
||||||
data: user,
|
data: user,
|
||||||
};
|
};
|
||||||
@ -116,23 +114,18 @@ export default class UserService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
message: 'User not found.',
|
message: 'User not found.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_USER_NOT_FOUND',
|
code: 'E_NOT_FOUND',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update user contacts
|
|
||||||
*/
|
|
||||||
await this.contactRepository.update(user, data);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'User updated.',
|
message: 'User updated.',
|
||||||
data: user,
|
data: user,
|
||||||
};
|
};
|
||||||
@ -150,18 +143,18 @@ export default class UserService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
message: 'User not found.',
|
message: 'User not found.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_USER_NOT_FOUND',
|
code: 'E_NOT_FOUND',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'User deleted.',
|
message: 'User deleted.',
|
||||||
data: user,
|
data: user,
|
||||||
};
|
};
|
||||||
@ -180,11 +173,11 @@ export default class UserService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
message: 'User not found.',
|
message: 'User not found.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_USER_NOT_FOUND',
|
code: 'E_NOT_FOUND',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -208,11 +201,11 @@ export default class UserService {
|
|||||||
if (attachedRole) {
|
if (attachedRole) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.BAD_REQUEST,
|
||||||
message: `Role "${attachedRole.name}" already attached to that user.`,
|
message: `Role "${attachedRole.name}" already attached to that user.`,
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_ROLE_ATTACHED',
|
code: 'E_BAD_REQUEST',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -222,7 +215,7 @@ export default class UserService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'Roles attached.',
|
message: 'Roles attached.',
|
||||||
data: user.roles,
|
data: user.roles,
|
||||||
};
|
};
|
||||||
@ -241,11 +234,11 @@ export default class UserService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
message: 'User not found.',
|
message: 'User not found.',
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_USER_NOT_FOUND',
|
code: 'E_NOT_FOUND',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -266,11 +259,11 @@ export default class UserService {
|
|||||||
if (notAttachedRole) {
|
if (notAttachedRole) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status: 404,
|
status: StatusCodeEnum.BAD_REQUEST,
|
||||||
message: `Role "${notAttachedRole.name}" not attached to that user.`,
|
message: `Role "${notAttachedRole.name}" not attached to that user.`,
|
||||||
data: {},
|
data: {},
|
||||||
error: {
|
error: {
|
||||||
code: 'E_ROLE_NOT_ATTACHED',
|
code: 'E_BAD_REQUEST',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -280,7 +273,7 @@ export default class UserService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 200,
|
status: StatusCodeEnum.OK,
|
||||||
message: 'Roles detached.',
|
message: 'Roles detached.',
|
||||||
data: user.roles,
|
data: user.roles,
|
||||||
};
|
};
|
||||||
|
|||||||
20
app/Validators/Contacts/CreateContactsValidator.ts
Normal file
20
app/Validators/Contacts/CreateContactsValidator.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { schema, rules } from '@ioc:Adonis/Core/Validator';
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
|
||||||
|
export default class CreateContactsValidator {
|
||||||
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
phone_number: schema.string.optional({}, [
|
||||||
|
rules.mobile({
|
||||||
|
locales: ['ru-RU', 'en-US'],
|
||||||
|
strict: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
vk_id: schema.string.optional({}, [rules.regex(/^[a-zA-Z0-9_]+$/)]),
|
||||||
|
twitter_id: schema.string.optional({}, [rules.regex(/^[a-zA-Z0-9_]+$/)]),
|
||||||
|
telegram_id: schema.string.optional({}, [rules.regex(/^[a-zA-Z0-9_]+$/)]),
|
||||||
|
});
|
||||||
|
|
||||||
|
public messages = {};
|
||||||
|
}
|
||||||
@ -5,7 +5,6 @@ export default class UpdateContactsValidator {
|
|||||||
constructor(protected ctx: HttpContextContract) {}
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
public schema = schema.create({
|
public schema = schema.create({
|
||||||
email: schema.string.optional({}, [rules.email()]),
|
|
||||||
phone_number: schema.string.optional({}, [
|
phone_number: schema.string.optional({}, [
|
||||||
rules.mobile({
|
rules.mobile({
|
||||||
locales: ['ru-RU', 'en-US'],
|
locales: ['ru-RU', 'en-US'],
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export default class CreateUserValidator {
|
|||||||
first_name: schema.string(),
|
first_name: schema.string(),
|
||||||
last_name: schema.string(),
|
last_name: schema.string(),
|
||||||
login: schema.string({}, [rules.maxLength(128), rules.unique({ table: 'users', column: 'login' })]),
|
login: schema.string({}, [rules.maxLength(128), rules.unique({ table: 'users', column: 'login' })]),
|
||||||
email: schema.string({}, [rules.email(), rules.unique({ table: 'contacts', column: 'email' })]),
|
email: schema.string({}, [rules.email(), rules.unique({ table: 'users', column: 'email' })]),
|
||||||
password: schema.string({}, [rules.minLength(6), rules.maxLength(128)]),
|
password: schema.string({}, [rules.minLength(6), rules.maxLength(128)]),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export default class UpdateUserValidator {
|
|||||||
first_name: schema.string(),
|
first_name: schema.string(),
|
||||||
last_name: schema.string(),
|
last_name: schema.string(),
|
||||||
login: schema.string({}, [rules.unique({ table: 'users', column: 'login' })]),
|
login: schema.string({}, [rules.unique({ table: 'users', column: 'login' })]),
|
||||||
email: schema.string({}, [rules.email(), rules.unique({ table: 'contacts', column: 'email' })]),
|
email: schema.string({}, [rules.email(), rules.unique({ table: 'users', column: 'email' })]),
|
||||||
password: schema.string(),
|
password: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import User from 'App/Models/User';
|
|||||||
|
|
||||||
export const ContactFactory = Factory.define(Contact, ({ faker }) => {
|
export const ContactFactory = Factory.define(Contact, ({ faker }) => {
|
||||||
return {
|
return {
|
||||||
email: faker.internet.exampleEmail(),
|
phone_number: faker.phone.phoneNumber(),
|
||||||
};
|
};
|
||||||
}).build();
|
}).build();
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ export const UserFactory = Factory.define(User, ({ faker }) => {
|
|||||||
first_name: faker.name.firstName(),
|
first_name: faker.name.firstName(),
|
||||||
last_name: faker.name.lastName(),
|
last_name: faker.name.lastName(),
|
||||||
login: `${faker.lorem.word(6)}${faker.datatype.number(10)}`,
|
login: `${faker.lorem.word(6)}${faker.datatype.number(10)}`,
|
||||||
|
email: faker.internet.exampleEmail(),
|
||||||
password: '12345',
|
password: '12345',
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export default class Users extends BaseSchema {
|
|||||||
table.string('first_name', 24).notNullable();
|
table.string('first_name', 24).notNullable();
|
||||||
table.string('last_name', 24).notNullable();
|
table.string('last_name', 24).notNullable();
|
||||||
table.string('login', 128).notNullable().unique();
|
table.string('login', 128).notNullable().unique();
|
||||||
|
table.string('email').notNullable().unique();
|
||||||
table.string('password').notNullable();
|
table.string('password').notNullable();
|
||||||
table.timestamp('created_at', { useTz: true });
|
table.timestamp('created_at', { useTz: true });
|
||||||
table.timestamp('updated_at', { useTz: true });
|
table.timestamp('updated_at', { useTz: true });
|
||||||
|
|||||||
@ -7,7 +7,6 @@ export default class Contacts extends BaseSchema {
|
|||||||
this.schema.createTable(this.tableName, table => {
|
this.schema.createTable(this.tableName, table => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
table.string('user_id', 21).unsigned().references('users.id').unique().onDelete('CASCADE');
|
table.string('user_id', 21).unsigned().references('users.id').unique().onDelete('CASCADE');
|
||||||
table.string('email').unique().notNullable();
|
|
||||||
table.string('phone_number').unique();
|
table.string('phone_number').unique();
|
||||||
table.string('vk_id').unique();
|
table.string('vk_id').unique();
|
||||||
table.string('twitter_id').unique();
|
table.string('twitter_id').unique();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user