added: MeService

This commit is contained in:
Sergey Yarkov 2021-08-08 02:00:56 +03:00
parent a852e82451
commit e6880e3e0e
17 changed files with 308 additions and 151 deletions

View File

@ -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);

View 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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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 {

View File

@ -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();
}
} }

View File

@ -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
View 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);

View File

@ -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,
}; };

View 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 = {};
}

View File

@ -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'],

View File

@ -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)]),
}); });

View File

@ -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(),
}); });

View File

@ -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',
}; };
}) })

View File

@ -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 });

View File

@ -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();