feat: added last login column & about column to user model

This commit is contained in:
Sergey Yarkov 2022-02-02 17:14:52 +03:00
parent 7e81d7b4ff
commit a0cba27c48
9 changed files with 105 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import MeService from 'App/Services/MeService';
*/ */
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';
import UpdateUserInfoValidator from 'App/Validators/User/UpdateUserInfoValidator';
import BaseController from '../../BaseController'; import BaseController from '../../BaseController';
@ -108,6 +109,21 @@ export default class MeController extends BaseController {
return this.sendResponse(ctx, result.data, result.message, result.status); return this.sendResponse(ctx, result.data, result.message, result.status);
} }
/**
* Update personal info of user
* PATCH /me/info
*/
public async updateInfo(ctx: HttpContextContract) {
const payload = await ctx.request.validate(UpdateUserInfoValidator);
const result = await this.meService.updateUserInfo(payload, ctx.auth);
if (!result.success && result.error) {
throw new Exception(result.message, result.status, result.error.code);
}
return this.sendResponse(ctx, result.data, result.message, result.status);
}
} }
new Ioc().make(MeController); new Ioc().make(MeController);

View File

@ -70,6 +70,12 @@ export default class User extends BaseModel {
}) })
public lessons_progress: HasMany<typeof LessonProgress>; public lessons_progress: HasMany<typeof LessonProgress>;
@column()
public about: string | null;
@column.dateTime()
public last_login: DateTime | null;
@column.dateTime({ autoCreate: true }) @column.dateTime({ autoCreate: true })
public createdAt: DateTime; public createdAt: DateTime;

View File

@ -1,4 +1,5 @@
import Hash from '@ioc:Adonis/Core/Hash'; import Hash from '@ioc:Adonis/Core/Hash';
import { DateTime } from 'luxon';
/** /**
* Models * Models
@ -11,6 +12,7 @@ import Role from 'App/Models/Role';
*/ */
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';
import UpdateUserInfoValidator from 'App/Validators/User/UpdateUserInfoValidator';
export default class UserRepository { export default class UserRepository {
private User: typeof User; private User: typeof User;
@ -159,6 +161,31 @@ export default class UserRepository {
return null; return null;
} }
/**
* Updates the time of last login column in database for user
*
* @param id User id
*/
public async updateLastLogin(id: number | string) {
await this.User.query().where('id', id).update({ last_login: DateTime.now() }).first();
}
/**
* Update personal info of user
*
* @param data New fields
*/
public async updateInfo(id: string | number, data: UpdateUserInfoValidator['schema']['props']) {
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
if (user) {
await user.merge(data).save();
return { about: user.about };
}
return null;
}
/** /**
* Rewrite all user roles * Rewrite all user roles
* *

View File

@ -70,8 +70,16 @@ export default class AuthService {
userName: user.fullname, userName: user.fullname,
}); });
/**
* Set cookie toke to response
*/
ctx.response.cookie('token', token.token); ctx.response.cookie('token', token.token);
/**
* Save last login date
*/
await this.userRepository.updateLastLogin(user.id);
return { return {
success: true, success: true,
status: HttpStatusEnum.OK, status: HttpStatusEnum.OK,

View File

@ -13,6 +13,7 @@ import UpdateContactsValidator from 'App/Validators/Contacts/UpdateContactsValid
import CodeErrorEnum from 'App/Datatypes/Enums/CodeErrorEnum'; import CodeErrorEnum from 'App/Datatypes/Enums/CodeErrorEnum';
import RoleHelper from 'App/Helpers/RoleHelper'; import RoleHelper from 'App/Helpers/RoleHelper';
import RoleEnum from 'App/Datatypes/Enums/RoleEnum'; import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
import UpdateUserInfoValidator from 'App/Validators/User/UpdateUserInfoValidator';
@inject() @inject()
export default class MeService { export default class MeService {
@ -279,6 +280,37 @@ export default class MeService {
data: user.contacts, data: user.contacts,
}; };
} }
public async updateUserInfo(
data: UpdateUserInfoValidator['schema']['props'],
auth: AuthContract
): Promise<IResponse> {
const user = await auth.use(this.authGuard).authenticate();
const info = await this.userRepository.updateInfo(user.id, data);
if (info) {
const { about } = info;
return {
success: true,
status: HttpStatusEnum.OK,
message: 'User info updated.',
data: {
about,
},
};
}
return {
success: false,
status: HttpStatusEnum.SERVICE_UNAVAILABLE,
message: 'Cannot update personal info.',
data: {},
error: {
code: 'E_SERVICE_UNAVAILABLE',
},
};
}
} }
new Ioc().make(MeService); new Ioc().make(MeService);

View File

@ -0,0 +1,12 @@
import { schema } from '@ioc:Adonis/Core/Validator';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
export default class UpdateUserInfoValidator {
constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({
about: schema.string.optional(),
});
public messages = {};
}

View File

@ -8,6 +8,7 @@ export default class UpdateUserValidator {
public schema = schema.create({ public schema = schema.create({
first_name: schema.string.optional(), first_name: schema.string.optional(),
last_name: schema.string.optional(), last_name: schema.string.optional(),
about: schema.string.optional(),
login: schema.string.optional({}, [rules.unique({ table: 'users', column: 'login' })]), login: schema.string.optional({}, [rules.unique({ table: 'users', column: 'login' })]),
email: schema.string.optional({}, [rules.email(), rules.unique({ table: 'users', column: 'email' })]), email: schema.string.optional({}, [rules.email(), rules.unique({ table: 'users', column: 'email' })]),
password: schema.string.optional(), password: schema.string.optional(),

View File

@ -8,9 +8,11 @@ export default class Users extends BaseSchema {
table.string('id', 21).primary(); table.string('id', 21).primary();
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.text('about');
table.string('login', 128).notNullable().unique(); table.string('login', 128).notNullable().unique();
table.string('email').notNullable().unique(); table.string('email').notNullable().unique();
table.string('password').notNullable(); table.string('password').notNullable();
table.timestamp('last_login', { useTz: true });
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

@ -2,6 +2,7 @@ import Route from '@ioc:Adonis/Core/Route';
Route.group(() => { Route.group(() => {
Route.get('/', 'Api/v1/MeController.show').middleware('role:admin,teacher,student').as('me.show'); Route.get('/', 'Api/v1/MeController.show').middleware('role:admin,teacher,student').as('me.show');
Route.patch('/info', 'Api/v1/MeController.updateInfo').middleware('role:admin,teacher,student').as('me.update-info');
Route.patch('/email', 'Api/v1/MeController.changeEmail') Route.patch('/email', 'Api/v1/MeController.changeEmail')
.middleware('role:admin,teacher,student') .middleware('role:admin,teacher,student')
.as('me.change-email'); .as('me.change-email');