refactor: UsersController

This commit is contained in:
Sergey Yarkov 2021-08-06 19:26:47 +03:00
parent 419495d39c
commit f8e3417f1f
23 changed files with 610 additions and 226 deletions

View File

@ -4,7 +4,7 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
/** /**
* Datatype * Datatype
*/ */
import Role from 'App/Datatype/Role'; import Role from 'App/Datatypes/Enums/RoleEnum';
/** /**
* Models * Models

View File

@ -1,11 +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';
/** /**
* Models * Services
*/ */
import Role from 'App/Models/Role'; import UserService from 'App/Services/UserService';
import User from 'App/Models/User';
/** /**
* Validators * Validators
@ -17,15 +16,13 @@ import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
import BaseController from '../../BaseController'; import BaseController from '../../BaseController';
@inject()
export default class UsersController extends BaseController { export default class UsersController extends BaseController {
private readonly User: typeof User; private userService: UserService;
private readonly Role: typeof Role; constructor(userService: UserService) {
constructor() {
super(); super();
this.User = User; this.userService = userService;
this.Role = Role;
} }
/** /**
@ -33,13 +30,14 @@ export default class UsersController extends BaseController {
* GET /users * GET /users
*/ */
public async list({ response }: HttpContextContract) { public async list(ctx: HttpContextContract) {
const users = await this.User.query().preload('contacts').preload('roles'); const result = await this.userService.fetchUsers(ctx.request.qs());
return response.ok({ if (!result.success && result.error) {
message: 'Fetched all users.', throw new Exception(result.message, result.status, result.error.code);
data: users, }
});
return this.sendResponse(ctx, result.data, result.message, result.status);
} }
/** /**
@ -47,16 +45,14 @@ export default class UsersController extends BaseController {
* GET /users/:id * GET /users/:id
*/ */
public async show({ response, params }: HttpContextContract) { public async show(ctx: HttpContextContract) {
const user = await this.User.findOrFail(params.id); const result = await this.userService.fetchUser(ctx.params.id);
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 user with id: "${user.id}"`,
data: user,
});
} }
/** /**
@ -64,20 +60,15 @@ export default class UsersController extends BaseController {
* POST /users * POST /users
*/ */
public async create({ response, request }: HttpContextContract) { public async create(ctx: HttpContextContract) {
await request.validate(CreateUserValidator); const payload = await ctx.request.validate(CreateUserValidator);
const result = await this.userService.createUser(payload);
const user = await this.User.create({ if (!result.success && result.error) {
first_name: request.input('first_name'), throw new Exception(result.message, result.status, result.error.code);
last_name: request.input('last_name'), }
login: request.input('login'),
password: request.input('password'),
});
await user.related('contacts').create({ email: request.input('email') }); return this.sendResponse(ctx, result.data, result.message, result.status);
await user.load('contacts');
return response.created({ message: 'Successfully created.', data: user });
} }
/** /**
@ -85,34 +76,15 @@ export default class UsersController extends BaseController {
* PATCH /users/:id * PATCH /users/:id
*/ */
public async update({ request, response, params }: HttpContextContract) { public async update(ctx: HttpContextContract) {
await request.validate(UpdateUserValidator); const payload = await ctx.request.validate(UpdateUserValidator);
const result = await this.userService.updateUser(ctx.params.id, payload);
const user = await this.User.findOrFail(params.id); if (!result.success && result.error) {
const input: Pick<User, 'first_name' | 'last_name' | 'login' | 'password'> = { throw new Exception(result.message, result.status, result.error.code);
first_name: request.input('first_name'), }
last_name: request.input('last_name'),
login: request.input('login'),
password: request.input('password'),
};
/** return this.sendResponse(ctx, result.data, result.message, result.status);
* Update roles
*/
Object.keys(input).forEach(k => {
if (input[k] !== undefined) {
user[k] = input[k];
}
});
await user.save();
await user.load('roles');
await user.load('contacts');
return response.ok({
message: `User with id: "${user.id}" was successfully updated.`,
data: user,
});
} }
/** /**
@ -120,74 +92,45 @@ export default class UsersController extends BaseController {
* DELETE /users/:id * DELETE /users/:id
*/ */
public async delete({ response, params }: HttpContextContract) { public async delete(ctx: HttpContextContract) {
const user = await this.User.query().preload('contacts').preload('roles').where('id', params.id).firstOrFail(); const result = await this.userService.deleteUser(ctx.params.id);
await user.delete(); if (!result.success && result.error) {
throw new Exception(result.message, result.status, result.error.code);
}
return response.ok({ return this.sendResponse(ctx, result.data, result.message, result.status);
message: `Used with id: "${user.id}" was successfully deleted.`,
data: user,
});
} }
/** /**
* Attach array of roles to user * Attach array of roles to user
* POST /users/:id/attach-roles * POST /users/:id/attach-roles
*/ */
public async attachRoles({ request, response, params }: HttpContextContract) { public async attachRoles(ctx: HttpContextContract) {
await request.validate(AddRoleToUserValidator); const payload = await ctx.request.validate(AddRoleToUserValidator);
const result = await this.userService.attachRolesToUser(ctx.params.id, payload.roles);
const input: string[] | undefined = request.input('roles'); if (!result.success && result.error) {
const user = await this.User.query().preload('roles').where('id', params.id).firstOrFail(); throw new Exception(result.message, result.status, result.error.code);
const roles = await this.findRolesBySlug(input); }
await this.User.attachRoles(user, roles); return this.sendResponse(ctx, result.data, result.message, result.status);
return response.ok({
message: 'Roles attached',
data: user.roles,
});
} }
/** /**
* Detach array of roles from user * Detach array of roles from user
* DELETE /users/:id/detach-roles * DELETE /users/:id/detach-roles
*/ */
public async detachRoles({ request, response, params }: HttpContextContract) { public async detachRoles(ctx: HttpContextContract) {
await request.validate(DelRoleFromUserValidator); const payload = await ctx.request.validate(DelRoleFromUserValidator);
const result = await this.userService.detachRolesFromUser(ctx.params.id, payload.roles);
const input: string[] | undefined = request.input('roles'); if (!result.success && result.error) {
const user = await this.User.query().preload('roles').where('id', params.id).firstOrFail(); throw new Exception(result.message, result.status, result.error.code);
const roles = await this.findRolesBySlug(input);
await this.User.detachRoles(user, roles);
return response.ok({
message: 'Roles detached',
data: user.roles,
});
}
/**
* Finds roles by input value and throw error if role does not exist.
*
* @param input Array of roles to find them
* @returns Finded roles
*/
private async findRolesBySlug(input: string[] | undefined): Promise<Role[]> {
if (!input) {
throw new Exception('Unable to find "roles" field in input.', 400, 'E_ROLES_FIELD_NOT_PROVIDED');
} }
const roles = await this.Role.query().whereIn('slug', input); return this.sendResponse(ctx, result.data, result.message, result.status);
input.forEach(val => {
if (!roles.map(r => r.slug).includes(val)) {
throw new Exception(`Unable to find role: "${val}" using input value "roles"`, 404, 'E_ROLE_NOT_FOUND');
}
});
return roles;
} }
} }
new Ioc().make(UsersController);

View File

@ -1,7 +1,7 @@
enum Role { enum RoleEnum {
ADMIN = 'admin', ADMIN = 'admin',
TEACHER = 'teacher', TEACHER = 'teacher',
STUDENT = 'student', STUDENT = 'student',
} }
export default Role; export default RoleEnum;

View File

@ -0,0 +1,9 @@
export default interface IResponse {
success: boolean;
status: number;
message: string;
data: object;
error?: {
code: string;
};
}

View File

@ -1,5 +1,6 @@
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'; import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm';
import User from './User';
export default class Contact extends BaseModel { export default class Contact extends BaseModel {
@column({ isPrimary: true }) @column({ isPrimary: true })
@ -23,6 +24,11 @@ export default class Contact extends BaseModel {
@column() @column()
public telegram_id: string | null; public telegram_id: string | null;
@belongsTo(() => User, {
foreignKey: 'user_id',
})
public user: BelongsTo<typeof User>;
@column.dateTime({ autoCreate: true, serializeAs: null }) @column.dateTime({ autoCreate: true, serializeAs: null })
public createdAt: DateTime; public createdAt: DateTime;

View File

@ -1,7 +1,6 @@
/* eslint-disable import/no-cycle */ /* eslint-disable import/no-cycle */
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { Exception } from '@adonisjs/core/build/standalone';
import { import {
BaseModel, BaseModel,
beforeCreate, beforeCreate,
@ -55,45 +54,6 @@ export default class User extends BaseModel {
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null }) @column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
public updatedAt: DateTime; public updatedAt: DateTime;
/**
* Checks if the input role is already attached, and if so, throws an error,
* otherwise it attaches the role to the user.
*
* @param user User
* @param inputRoles Array of roles to attach
*/
public static async attachRoles(user: User, inputRoles: Role[]): Promise<void> {
const currentRolesSlugs: string[] = user.roles.map(r => r.slug);
const inputRolesSlugs: string[] = inputRoles.map(r => r.slug);
currentRolesSlugs.forEach(s => {
if (inputRolesSlugs.includes(s)) {
throw new Exception(`Role "${s}" already attached to that user.`, 400, 'E_ROLE_ATTACHED');
}
});
await user.related('roles').attach(inputRoles.map(r => r.id));
await user.load('roles');
}
/**
* Before detach, checks if input role is attached to that user and
* if not, throws an error.
*
* @param user User
* @param inputRoles Array of roles to detach
*/
public static async detachRoles(user: User, inputRoles: Role[]): Promise<void> {
inputRoles.forEach(role => {
if (!user.roles.map(r => r.id).includes(role.id)) {
throw new Exception(`Role "${role.slug}" not attached to that user.`, 400, 'E_ROLE_NOT_ATTACHED');
}
});
user.related('roles').detach(inputRoles.map(r => r.id));
await user.load('roles');
}
/** /**
* Assign id to user by nanoid. * Assign id to user by nanoid.
* @param user User * @param user User

View File

@ -0,0 +1,56 @@
import Contact from 'App/Models/Contact';
import User from 'App/Models/User';
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
export default class ContactRepository {
private Contact: typeof Contact;
constructor() {
this.Contact = Contact;
}
/**
* Creates user contacts in database
*
* @param user User for associate the contacts
* @param data Data contacts
* @returns Created contacts
*/
public async create(user: User, data: Pick<CreateUserValidator['schema']['props'], 'email'>) {
const contacts = await this.Contact.create(data);
await contacts.related('user').associate(user);
await user.load('contacts');
await user.load('roles');
return contacts;
}
public async update(user: User, data: Pick<UpdateUserValidator['schema']['props'], 'email'>) {
const contacts = await this.Contact.query().where('user_id', user.id).first();
/**
* Update user contacts
*/
if (contacts) {
contacts.email = data.email;
await contacts.save();
await user.load('contacts');
return contacts;
}
/**
* 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

@ -0,0 +1,26 @@
import Role from 'App/Models/Role';
export default class RoleRepository {
private Role: typeof Role;
constructor() {
this.Role = Role;
}
public async getAll(params?: any) {
const { roles }: any = params || {};
const query = this.Role.query();
if (roles) {
query.whereIn('slug', roles);
}
const data = await query;
return data;
}
public async getBySlug(slug: string) {
const role = await this.Role.query().where('slug', slug);
return role;
}
}

View File

@ -0,0 +1,106 @@
/* eslint-disable @typescript-eslint/naming-convention */
import User from 'App/Models/User';
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
export default class UserRepository {
private User: typeof User;
constructor() {
this.User = User;
}
public async getById(id: string | number) {
const data = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
return data;
}
public async getAllByRole(role: string) {
const data = await this.User.query()
.preload('contacts')
.preload('roles')
.whereHas('roles', q => q.where('slug', role));
return data;
}
public async getAll(params?: any) {
const { email, login, first_name, last_name, role }: any = params || {};
const query = this.User.query();
if (email) {
query.preload('contacts').whereHas('contacts', q => q.where('email', 'like', `%${email}%`));
}
if (login) {
query.where('login', 'like', `%${login}%`);
}
if (first_name) {
query.where('first_name', 'like', `%${first_name}%`);
}
if (last_name) {
query.where('last_name', 'like', `%${last_name}%`);
}
if (role) {
query.preload('roles').whereHas('roles', q => q.where('slug', role));
}
const data = await query.preload('contacts').preload('roles');
return data;
}
public async create(data: CreateUserValidator['schema']['props']) {
const createdUser = await this.User.create({
first_name: data.first_name,
last_name: data.last_name,
login: data.login,
password: data.password,
});
return createdUser;
}
/**
* Update user
*
* @param id User id
* @param data Data to update
* @returns User or null if user is not found
*/
public async update(id: number | string, data: UpdateUserValidator['schema']['props']) {
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
if (user) {
await user
.merge({
first_name: data.first_name,
last_name: data.last_name,
login: data.login,
password: data.password,
})
.save();
return user;
}
return null;
}
/**
* Delete user
*
* @param id User id
* @returns Deleted user or null if user is not found
*/
public async delete(id: number | string) {
const user = await this.User.query().preload('contacts').preload('roles').where('id', id).first();
if (user) {
await user.delete();
return user;
}
return null;
}
}

261
app/Services/UserService.ts Normal file
View File

@ -0,0 +1,261 @@
import { inject, Ioc } from '@adonisjs/core/build/standalone';
import IResponse from 'App/Datatypes/Interfaces/IResponse';
import Role from 'App/Models/Role';
import ContactRepository from 'App/Repositories/ContactRepository';
import RoleRepository from 'App/Repositories/RoleRepository';
import UserRepository from 'App/Repositories/UserRepository';
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
@inject()
export default class UserService {
private userRepository: UserRepository;
private contactRepository: ContactRepository;
private roleRepository: RoleRepository;
constructor(userRepository: UserRepository, contactRepository: ContactRepository, roleRepository: RoleRepository) {
this.userRepository = userRepository;
this.contactRepository = contactRepository;
this.roleRepository = roleRepository;
}
/**
* Fetch one user by id
*
* @param id Id of user
* @returns Response data
*/
public async fetchUser(id: string | number): Promise<IResponse> {
const user = await this.userRepository.getById(id);
if (!user) {
return {
success: false,
status: 404,
message: 'User not found.',
data: {},
error: {
code: 'USER_NOT_FOUND',
},
};
}
return {
success: true,
status: 200,
message: 'Fetched user.',
data: user,
};
}
/**
* Fetch list of users
*
* @param params Params for finding user
* @returns Response data
*/
public async fetchUsers(params?: any): Promise<IResponse> {
const users = await this.userRepository.getAll(params);
return {
success: true,
status: 200,
message: 'Fetched all users.',
data: users,
};
}
/**
* Create new user
*
* @param data Input for create new user
* @returns Response data
*/
public async createUser(data: CreateUserValidator['schema']['props']): Promise<IResponse> {
const user = await this.userRepository.create(data);
/* Associate user contacts */
await this.contactRepository.create(user, { email: data.email });
return {
success: true,
status: 201,
message: 'User created.',
data: user,
};
}
public async updateUser(id: string | number, data: UpdateUserValidator['schema']['props']): Promise<IResponse> {
const user = await this.userRepository.update(id, data);
if (!user) {
return {
success: false,
status: 404,
message: 'User not found.',
data: {},
error: {
code: 'E_USER_NOT_FOUND',
},
};
}
/**
* Update user contacts
*/
await this.contactRepository.update(user, data);
return {
success: true,
status: 200,
message: 'User updated.',
data: user,
};
}
public async deleteUser(id: string | number): Promise<IResponse> {
const user = await this.userRepository.delete(id);
if (!user) {
return {
success: false,
status: 404,
message: 'User not found.',
data: {},
error: {
code: 'E_USER_NOT_FOUND',
},
};
}
return {
success: true,
status: 200,
message: 'User deleted.',
data: user,
};
}
/**
* Attach array of roles to user
*
* @param id User id
* @param input Roles
* @returns Result
*/
public async attachRolesToUser(id: string | number, input: string[]): Promise<IResponse> {
const user = await this.userRepository.getById(id);
if (!user) {
return {
success: false,
status: 404,
message: 'User not found.',
data: {},
error: {
code: 'E_USER_NOT_FOUND',
},
};
}
const roles = await this.roleRepository.getAll({ roles: input });
let attachedRole: Role | undefined;
/**
* Set attachedRole variable if some role already
* attached to user.
*/
roles.some(inputRole => {
const existed = user.roles.find(role => role.slug === inputRole.slug);
if (existed) {
attachedRole = inputRole;
return true;
}
return false;
});
if (attachedRole) {
return {
success: false,
status: 404,
message: `Role "${attachedRole.name}" already attached to that user.`,
data: {},
error: {
code: 'E_ROLE_ATTACHED',
},
};
}
await user.related('roles').attach([...roles.map(r => r.id)]);
await user.load('roles');
return {
success: true,
status: 200,
message: 'Roles attached.',
data: user.roles,
};
}
/**
* Detach array of role from user
*
* @param id User id
* @param input Roles
* @returns Result
*/
public async detachRolesFromUser(id: string | number, input: string[]): Promise<IResponse> {
const user = await this.userRepository.getById(id);
if (!user) {
return {
success: false,
status: 404,
message: 'User not found.',
data: {},
error: {
code: 'E_USER_NOT_FOUND',
},
};
}
const roles = await this.roleRepository.getAll({ roles: input });
let notAttachedRole: Role | undefined;
/**
* Set notAttachedRole variable if some role
* not attached to user.
*/
roles.forEach(role => {
if (!user.roles.map(r => r.id).includes(role.id)) {
notAttachedRole = role;
}
});
if (notAttachedRole) {
return {
success: false,
status: 404,
message: `Role "${notAttachedRole.name}" not attached to that user.`,
data: {},
error: {
code: 'E_ROLE_NOT_ATTACHED',
},
};
}
await user.related('roles').detach([...roles.map(r => r.id)]);
await user.load('roles');
return {
success: true,
status: 200,
message: 'Roles detached.',
data: user.roles,
};
}
}
new Ioc().make(UserService);

View File

@ -1,11 +1,12 @@
import { schema } from '@ioc:Adonis/Core/Validator'; import { schema } from '@ioc:Adonis/Core/Validator';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
export default class AddRoleToUserValidator { export default class AddRoleToUserValidator {
constructor(protected ctx: HttpContextContract) {} constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({ public schema = schema.create({
roles: schema.array().members(schema.string()), roles: schema.array().members(schema.enum(Object.values(RoleEnum))),
}); });
public messages = {}; public messages = {};

View File

@ -1,11 +1,12 @@
import { schema } from '@ioc:Adonis/Core/Validator'; import { schema } from '@ioc:Adonis/Core/Validator';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
export default class DelRoleFromUserValidator { export default class DelRoleFromUserValidator {
constructor(protected ctx: HttpContextContract) {} constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({ public schema = schema.create({
roles: schema.array().members(schema.string()), roles: schema.array().members(schema.enum(Object.values(RoleEnum))),
}); });
public messages = {}; public messages = {};

View File

@ -5,11 +5,11 @@ export default class UpdateUserValidator {
constructor(protected ctx: HttpContextContract) {} constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({ public schema = schema.create({
first_name: schema.string.optional({}, [rules.unique({ table: 'users', column: 'first_name' })]), first_name: schema.string(),
last_name: schema.string.optional({}, [rules.unique({ table: 'users', column: 'last_name' })]), last_name: schema.string(),
login: schema.string.optional({}, [rules.unique({ table: 'users', column: 'login' })]), login: schema.string({}, [rules.unique({ table: 'users', column: 'login' })]),
email: schema.string.optional({}, [rules.email(), rules.unique({ table: 'contacts', column: 'email' })]), email: schema.string({}, [rules.email(), rules.unique({ table: 'contacts', column: 'email' })]),
password: schema.string.optional({}, [rules.unique({ table: 'users', column: 'password' })]), password: schema.string(),
}); });
public messages = {}; public messages = {};

View File

@ -17,6 +17,4 @@ import Application from '@ioc:Adonis/Core/Application';
| |
*/ */
export default listDirectoryFiles(__dirname, Application.appRoot, [ export default listDirectoryFiles(__dirname, Application.appRoot, ['./commands/index']);
'./commands/index',
]);

View File

@ -32,12 +32,7 @@ const bodyParserConfig: BodyParserConfig = {
encoding: 'utf-8', encoding: 'utf-8',
limit: '1mb', limit: '1mb',
strict: true, strict: true,
types: [ types: ['application/json', 'application/json-patch+json', 'application/vnd.api+json', 'application/csp-report'],
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report',
],
}, },
/* /*

View File

@ -41,6 +41,7 @@ const redisConfig: RedisConfig = {
password: Env.get('REDIS_PASSWORD', ''), password: Env.get('REDIS_PASSWORD', ''),
db: 0, db: 0,
keyPrefix: '', keyPrefix: '',
healthCheck: true,
}, },
}, },
}; };

View File

@ -5,7 +5,7 @@
* file. * file.
*/ */
import User from 'App/Models/User' import User from 'App/Models/User';
declare module '@ioc:Adonis/Addons/Auth' { declare module '@ioc:Adonis/Addons/Auth' {
/* /*
@ -34,9 +34,9 @@ declare module '@ioc:Adonis/Addons/Auth' {
| |
*/ */
user: { user: {
implementation: LucidProviderContract<typeof User>, implementation: LucidProviderContract<typeof User>;
config: LucidProviderConfig<typeof User>, config: LucidProviderConfig<typeof User>;
}, };
} }
/* /*
@ -65,8 +65,8 @@ declare module '@ioc:Adonis/Addons/Auth' {
| |
*/ */
api: { api: {
implementation: OATGuardContract<'user', 'api'>, implementation: OATGuardContract<'user', 'api'>;
config: OATGuardConfig<'user'>, config: OATGuardConfig<'user'>;
}, };
} }
} }

38
package-lock.json generated
View File

@ -145,11 +145,41 @@
} }
}, },
"@adonisjs/fold": { "@adonisjs/fold": {
"version": "8.1.4", "version": "8.1.5",
"resolved": "https://registry.npmjs.org/@adonisjs/fold/-/fold-8.1.4.tgz", "resolved": "https://registry.npmjs.org/@adonisjs/fold/-/fold-8.1.5.tgz",
"integrity": "sha512-+B56Lxb19TZ2DkK2deZH1kTfqyXVUKwrfNirVZ3JfGeO6RbXjW3IYeUTJ7MWzcbiSTqmHXw2HC+TTIpE/EEDpg==", "integrity": "sha512-eqbq0zQd0TV7AoN/RP4vUUg442BP4GSc0U1CsNYGKJ3YBhxfgThVLDlgn1K4/nadC1lv4/Gf3ZSnLj1Rx5OzFA==",
"requires": { "requires": {
"@poppinss/utils": "^3.1.3" "@poppinss/utils": "^3.1.4"
},
"dependencies": {
"@poppinss/utils": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@poppinss/utils/-/utils-3.2.0.tgz",
"integrity": "sha512-gnqlrxE4zMvqW+LpviyCWCg4+RShCYCFyBdVTyg9ZZEEgPBV9bEY+YB5r/2klFrKx30k9nfClHVI2n8ZKmXgsw==",
"requires": {
"@types/bytes": "^3.1.1",
"@types/he": "^1.1.2",
"buffer-alloc": "^1.2.0",
"bytes": "^3.1.0",
"change-case": "^4.1.2",
"cuid": "^2.1.8",
"fs-readdir-recursive": "^1.1.0",
"he": "^1.2.0",
"kind-of": "^6.0.3",
"lodash": "^4.17.21",
"ms": "^2.1.3",
"pluralize": "^8.0.0",
"require-all": "^3.0.0",
"resolve-from": "^5.0.0",
"slugify": "^1.6.0",
"truncatise": "0.0.8"
}
},
"slugify": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.0.tgz",
"integrity": "sha512-FkMq+MQc5hzYgM86nLuHI98Acwi3p4wX+a5BO9Hhw4JdK4L7WueIiZ4tXEobImPqBz2sVcV0+Mu3GRB30IGang=="
}
} }
}, },
"@adonisjs/hash": { "@adonisjs/hash": {

View File

@ -3,19 +3,11 @@ import { ApplicationContract } from '@ioc:Adonis/Core/Application';
export default class AppProvider { export default class AppProvider {
constructor(protected app: ApplicationContract) {} constructor(protected app: ApplicationContract) {}
public register() { public register() {}
// Register your own bindings
}
public async boot() { public async boot() {}
// IoC container is ready
}
public async ready() { public async ready() {}
// App is ready
}
public async shutdown() { public async shutdown() {}
// Cleanup, since app is going down
}
} }

11
routes/apis/v1/health.ts Normal file
View File

@ -0,0 +1,11 @@
import Route from '@ioc:Adonis/Core/Route';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import HealthCheck from '@ioc:Adonis/Core/HealthCheck';
Route.group(() => {
Route.get('/', async (ctx: HttpContextContract) => {
const report = await HealthCheck.getReport();
return report.healthy ? ctx.response.ok(report) : ctx.response.badRequest(report);
}).as('health');
}).prefix('health');

View File

@ -12,6 +12,13 @@ Route.group(() => {
require('./auth'); require('./auth');
Route.group(() => { Route.group(() => {
/**
*
* Health check
*
*/
require('./health');
/** /**
* *
* Users module * Users module

View File

@ -2,14 +2,14 @@ import Route from '@ioc:Adonis/Core/Route';
Route.group(() => { Route.group(() => {
Route.get('/', 'Api/v1/UsersController.list').middleware('role:admin,teacher,student').as('users.list'); Route.get('/', 'Api/v1/UsersController.list').middleware('role:admin,teacher,student').as('users.list');
Route.post('/', 'Api/v1/UsersController.create').middleware('role:admin').as('users.create'); Route.post('/', 'Api/v1/UsersController.create').middleware('role:admin,student').as('users.create');
Route.get('/:id', 'Api/v1/UsersController.show').middleware('role:admin,teacher,student').as('users.show'); Route.get('/:id', 'Api/v1/UsersController.show').middleware('role:admin,teacher,student').as('users.show');
Route.patch('/:id', 'Api/v1/UsersController.update').middleware('role:admin').as('users.update'); Route.put('/:id', 'Api/v1/UsersController.update').middleware('role:admin,student,teacher').as('users.update');
Route.delete('/:id', 'Api/v1/UsersController.delete').middleware('role:admin').as('users.delete'); Route.delete('/:id', 'Api/v1/UsersController.delete').middleware('role:admin,student,teacher').as('users.delete');
Route.post('/:id/attach-roles', 'Api/v1/UsersController.attachRoles') Route.post('/:id/attach-roles', 'Api/v1/UsersController.attachRoles')
.middleware('role:admin') .middleware('role:admin,student,teacher')
.as('users.attach-role'); .as('users.attach-role');
Route.delete('/:id/detach-roles', 'Api/v1/UsersController.detachRoles') Route.delete('/:id/detach-roles', 'Api/v1/UsersController.detachRoles')
.middleware('role:admin') .middleware('role:admin,student,teacher')
.as('users.detach-role'); .as('users.detach-role');
}).prefix('users'); }).prefix('users');

View File

@ -1,36 +1,17 @@
{ {
"extends": "./node_modules/adonis-preset-ts/tsconfig", "extends": "./node_modules/adonis-preset-ts/tsconfig",
"include": [ "include": ["**/*"],
"**/*" "exclude": ["node_modules", "build"],
],
"exclude": [
"node_modules",
"build"
],
"compilerOptions": { "compilerOptions": {
"outDir": "build", "outDir": "build",
"rootDir": "./", "rootDir": "./",
"sourceMap": true, "sourceMap": true,
"paths": { "paths": {
"App/*": [ "App/*": ["./app/*"],
"./app/*" "Config/*": ["./config/*"],
], "Contracts/*": ["./contracts/*"],
"Config/*": [ "Database/*": ["./database/*"]
"./config/*"
],
"Contracts/*": [
"./contracts/*"
],
"Database/*": [
"./database/*"
]
}, },
"types": [ "types": ["@adonisjs/core", "@adonisjs/repl", "@adonisjs/lucid", "@adonisjs/auth", "@adonisjs/redis"]
"@adonisjs/core",
"@adonisjs/repl",
"@adonisjs/lucid",
"@adonisjs/auth",
"@adonisjs/redis"
]
} }
} }