some refactor

This commit is contained in:
Sergey Yarkov 2021-08-02 00:32:33 +03:00
parent a9715971e4
commit 033eccdcc0
13 changed files with 195 additions and 133 deletions

View File

@ -13,18 +13,7 @@
"Database": "database", "Database": "database",
"Contracts": "contracts" "Contracts": "contracts"
}, },
"preloads": [ "preloads": ["./routes/index", "./start/kernel"],
"./start/routes", "providers": ["./providers/AppProvider", "@adonisjs/core", "@adonisjs/lucid", "@adonisjs/auth", "@adonisjs/redis"],
"./start/kernel" "aceProviders": ["@adonisjs/repl"]
],
"providers": [
"./providers/AppProvider",
"@adonisjs/core",
"@adonisjs/lucid",
"@adonisjs/auth",
"@adonisjs/redis"
],
"aceProviders": [
"@adonisjs/repl"
]
} }

View File

@ -1,16 +1,20 @@
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import Logger from '@ioc:Adonis/Core/Logger'; import Logger from '@ioc:Adonis/Core/Logger';
import Hash from '@ioc:Adonis/Core/Hash'; import Hash from '@ioc:Adonis/Core/Hash';
import User from 'App/Models/User';
import { Exception } from '@adonisjs/core/build/standalone'; import { Exception } from '@adonisjs/core/build/standalone';
/**
* Models
*/
import User from 'App/Models/User';
export default class AuthController { export default class AuthController {
private user: typeof User; private User: typeof User;
private guard: 'api'; private guard: 'api';
constructor() { constructor() {
this.user = User; this.User = User;
} }
/** /**
@ -21,7 +25,7 @@ export default class AuthController {
public async login({ auth, request }: HttpContextContract) { public async login({ auth, request }: HttpContextContract) {
const login = request.input('login'); const login = request.input('login');
const password = request.input('password'); const password = request.input('password');
const user = await this.user.query().preload('roles').where('login', login).firstOrFail(); const user = await this.User.query().preload('roles').where('login', login).firstOrFail();
if (!(await Hash.verify(user.password, password))) { if (!(await Hash.verify(user.password, password))) {
throw new Exception('Invalid credentials.', 403, 'E_INVALID_CREDENTIALS'); throw new Exception('Invalid credentials.', 403, 'E_INVALID_CREDENTIALS');

View File

@ -1,7 +1,15 @@
import { Exception } from '@adonisjs/core/build/standalone'; import { Exception } from '@adonisjs/core/build/standalone';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
/**
* Models
*/
import Course from 'App/Models/Course'; import Course from 'App/Models/Course';
import User from 'App/Models/User'; import User from 'App/Models/User';
/**
* Validators
*/
import AddCourseToUserValidator from 'App/Validators/AddCourseToUserValidator'; import AddCourseToUserValidator from 'App/Validators/AddCourseToUserValidator';
import CreateCourseValidator from 'App/Validators/CreateCourseValidator'; import CreateCourseValidator from 'App/Validators/CreateCourseValidator';
import DelCourseFromUserValidator from 'App/Validators/DelCourseFromUserValidator'; import DelCourseFromUserValidator from 'App/Validators/DelCourseFromUserValidator';
@ -21,7 +29,7 @@ export default class CoursesController {
* List of all Courses * List of all Courses
* GET /courses * GET /courses
*/ */
public async index({ response }: HttpContextContract) { public async list({ response }: HttpContextContract) {
const courses = await this.Course.query().preload('teacher').preload('category').preload('lessons'); const courses = await this.Course.query().preload('teacher').preload('category').preload('lessons');
return response.ok({ return response.ok({
@ -145,7 +153,7 @@ export default class CoursesController {
* Delete Course by "id" * Delete Course by "id"
* DELETE /courses/:id * DELETE /courses/:id
*/ */
async destroy({ response, params }: HttpContextContract) { public async delete({ response, params }: HttpContextContract) {
const course = await this.Course.query() const course = await this.Course.query()
.preload('teacher') .preload('teacher')
.preload('category') .preload('category')
@ -165,7 +173,7 @@ export default class CoursesController {
* Update Course by "id" * Update Course by "id"
* PATCH /courses/:id * PATCH /courses/:id
*/ */
async update({ response, request, params }: HttpContextContract) { public async update({ response, request, params }: HttpContextContract) {
const payload = await request.validate(UpdateCourseValidator); const payload = await request.validate(UpdateCourseValidator);
const course = await this.Course.findOrFail(params.id); const course = await this.Course.findOrFail(params.id);
@ -193,7 +201,7 @@ export default class CoursesController {
* Attach student to course * Attach student to course
* POST /courses/:id/attach-student * POST /courses/:id/attach-student
*/ */
async attachStudent({ response, request, params }: HttpContextContract) { public async attachStudent({ response, request, params }: HttpContextContract) {
try { try {
const payload = await request.validate(AddCourseToUserValidator); const payload = await request.validate(AddCourseToUserValidator);
const student = await this.User.findOrFail(payload.student_id); const student = await this.User.findOrFail(payload.student_id);
@ -225,7 +233,7 @@ export default class CoursesController {
/** /**
* Detach student from course * Detach student from course
*/ */
async detachStudent({ response, request, params }: HttpContextContract) { public async detachStudent({ response, request, params }: HttpContextContract) {
const payload = await request.validate(DelCourseFromUserValidator); const payload = await request.validate(DelCourseFromUserValidator);
const course = await this.Course.query().preload('students').where('id', params.id).firstOrFail(); const course = await this.Course.query().preload('students').where('id', params.id).firstOrFail();
const candidate = course.students.find(student => student.id === payload.student_id); const candidate = course.students.find(student => student.id === payload.student_id);

View File

@ -2,24 +2,32 @@ import { Exception } 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 Hash from '@ioc:Adonis/Core/Hash';
import Logger from '@ioc:Adonis/Core/Logger'; import Logger from '@ioc:Adonis/Core/Logger';
import ChangePasswordValidator from 'App/Validators/ChangePasswordValidator';
/**
* Models
*/
import Contact from 'App/Models/Contact'; import Contact from 'App/Models/Contact';
/**
* Validators
*/
import ChangePasswordValidator from 'App/Validators/ChangePasswordValidator';
import UpdateContactsValidator from 'App/Validators/UpdateContactsValidator'; import UpdateContactsValidator from 'App/Validators/UpdateContactsValidator';
export default class MeController { export default class MeController {
private readonly guard: 'api'; private readonly guard: 'api';
private readonly contact: typeof Contact; private readonly Contact: typeof Contact;
constructor() { constructor() {
this.contact = Contact; this.Contact = Contact;
} }
/** /**
* Shows user info of the access token resource owner. * Shows user info of the access token resource owner.
* GET /me * GET /me
*/ */
public async index({ response, auth }: HttpContextContract) { public async show({ response, auth }: HttpContextContract) {
const user = await auth.use(this.guard).authenticate(); const user = await auth.use(this.guard).authenticate();
await user.load('contacts'); await user.load('contacts');
@ -73,7 +81,7 @@ export default class MeController {
public async updateContacts({ request, response, auth }: HttpContextContract) { public async updateContacts({ request, response, auth }: HttpContextContract) {
const payload = await request.validate(UpdateContactsValidator); const payload = await request.validate(UpdateContactsValidator);
const user = await auth.use(this.guard).authenticate(); const user = await auth.use(this.guard).authenticate();
const contacts = await this.contact.findByOrFail('user_id', user.id); const contacts = await this.Contact.findByOrFail('user_id', user.id);
/** /**
* Update contacts * Update contacts

View File

@ -1,20 +1,28 @@
import { Exception } from '@adonisjs/core/build/standalone'; import { Exception } from '@adonisjs/core/build/standalone';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
/**
* Models
*/
import Role from 'App/Models/Role'; import Role from 'App/Models/Role';
import User from 'App/Models/User'; import User from 'App/Models/User';
/**
* Validators
*/
import AddRoleToUserValidator from 'App/Validators/AddRoleToUserValidator'; import AddRoleToUserValidator from 'App/Validators/AddRoleToUserValidator';
import CreateUserValidator from 'App/Validators/CreateUserValidator'; import CreateUserValidator from 'App/Validators/CreateUserValidator';
import DelRoleFromUserValidator from 'App/Validators/DelRoleFromUserValidator'; import DelRoleFromUserValidator from 'App/Validators/DelRoleFromUserValidator';
import UpdateUserValidator from 'App/Validators/UpdateUserValidator'; import UpdateUserValidator from 'App/Validators/UpdateUserValidator';
export default class UsersController { export default class UsersController {
private readonly user: typeof User; private readonly User: typeof User;
private readonly role: typeof Role; private readonly Role: typeof Role;
constructor() { constructor() {
this.user = User; this.User = User;
this.role = Role; this.Role = Role;
} }
/** /**
@ -22,8 +30,8 @@ export default class UsersController {
* GET /users * GET /users
*/ */
public async index({ response }: HttpContextContract) { public async list({ response }: HttpContextContract) {
const users = await this.user.query().preload('contacts').preload('roles'); const users = await this.User.query().preload('contacts').preload('roles');
return response.ok({ return response.ok({
message: 'Fetched all users.', message: 'Fetched all users.',
@ -37,7 +45,7 @@ export default class UsersController {
*/ */
public async show({ response, params }: HttpContextContract) { public async show({ response, params }: HttpContextContract) {
const user = await this.user.findOrFail(params.id); const user = await this.User.findOrFail(params.id);
await user.load('contacts'); await user.load('contacts');
await user.load('roles'); await user.load('roles');
@ -56,7 +64,7 @@ export default class UsersController {
public async create({ response, request }: HttpContextContract) { public async create({ response, request }: HttpContextContract) {
await request.validate(CreateUserValidator); await request.validate(CreateUserValidator);
const user = await this.user.create({ const user = await this.User.create({
first_name: request.input('first_name'), first_name: request.input('first_name'),
last_name: request.input('last_name'), last_name: request.input('last_name'),
login: request.input('login'), login: request.input('login'),
@ -77,7 +85,7 @@ export default class UsersController {
public async update({ request, response, params }: HttpContextContract) { public async update({ request, response, params }: HttpContextContract) {
await request.validate(UpdateUserValidator); await request.validate(UpdateUserValidator);
const user = await this.user.findOrFail(params.id); const user = await this.User.findOrFail(params.id);
const input: Pick<User, 'first_name' | 'last_name' | 'login' | 'password'> = { const input: Pick<User, 'first_name' | 'last_name' | 'login' | 'password'> = {
first_name: request.input('first_name'), first_name: request.input('first_name'),
last_name: request.input('last_name'), last_name: request.input('last_name'),
@ -109,8 +117,8 @@ export default class UsersController {
* DELETE /users/:id * DELETE /users/:id
*/ */
public async destroy({ response, params }: HttpContextContract) { public async delete({ response, params }: HttpContextContract) {
const user = await this.user.query().preload('contacts').preload('roles').where('id', params.id).firstOrFail(); const user = await this.User.query().preload('contacts').preload('roles').where('id', params.id).firstOrFail();
await user.delete(); await user.delete();
@ -128,10 +136,10 @@ export default class UsersController {
await request.validate(AddRoleToUserValidator); await request.validate(AddRoleToUserValidator);
const input: string[] | undefined = request.input('roles'); const input: string[] | undefined = request.input('roles');
const user = await this.user.query().preload('roles').where('id', params.id).firstOrFail(); const user = await this.User.query().preload('roles').where('id', params.id).firstOrFail();
const roles = await this.findRolesBySlug(input); const roles = await this.findRolesBySlug(input);
await this.user.attachRoles(user, roles); await this.User.attachRoles(user, roles);
return response.ok({ return response.ok({
message: 'Roles attached', message: 'Roles attached',
@ -147,10 +155,10 @@ export default class UsersController {
await request.validate(DelRoleFromUserValidator); await request.validate(DelRoleFromUserValidator);
const input: string[] | undefined = request.input('roles'); const input: string[] | undefined = request.input('roles');
const user = await this.user.query().preload('roles').where('id', params.id).firstOrFail(); const user = await this.User.query().preload('roles').where('id', params.id).firstOrFail();
const roles = await this.findRolesBySlug(input); const roles = await this.findRolesBySlug(input);
await this.user.detachRoles(user, roles); await this.User.detachRoles(user, roles);
return response.ok({ return response.ok({
message: 'Roles detached', message: 'Roles detached',
@ -169,7 +177,7 @@ export default class UsersController {
throw new Exception('Unable to find "roles" field in input.', 400, 'E_ROLES_FIELD_NOT_PROVIDED'); 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); const roles = await this.Role.query().whereIn('slug', input);
input.forEach(val => { input.forEach(val => {
if (!roles.map(r => r.slug).includes(val)) { if (!roles.map(r => r.slug).includes(val)) {

8
routes/api.ts Normal file
View File

@ -0,0 +1,8 @@
/* eslint-disable global-require */
import Route from '@ioc:Adonis/Core/Route';
Route.group(() => {
require('./apis/v1/index');
})
.prefix('api')
.as('api');

6
routes/apis/v1/auth.ts Normal file
View File

@ -0,0 +1,6 @@
import Route from '@ioc:Adonis/Core/Route';
Route.group(() => {
Route.post('login', 'AuthController.login').as('auth.login');
Route.post('logout', 'AuthController.logout').middleware('auth').as('auth.logout');
}).prefix('auth');

32
routes/apis/v1/courses.ts Normal file
View File

@ -0,0 +1,32 @@
import Route from '@ioc:Adonis/Core/Route';
Route.group(() => {
Route.get('/', 'CoursesController.list').middleware('role:admin,teacher,student').as('courses.list');
Route.post('/', 'CoursesController.create').middleware('role:admin,teacher').as('courses.create');
Route.get('/:id', 'CoursesController.show').middleware('role:admin,teacher,student').as('courses.show');
Route.delete('/:id', 'CoursesController.delete').middleware('role:admin,teacher').as('courses.delete');
Route.patch('/:id', 'CoursesController.update').middleware('role:admin,teacher').as('courses.update');
Route.get('/:id/teacher', 'CoursesController.showTeacher')
.middleware('role:admin,teacher,student')
.as('courses.show-teacher');
Route.get('/:id/category', 'CoursesController.showCategory')
.middleware('role:admin,teacher,student')
.as('courses.show-category');
Route.get('/:id/lessons', 'CoursesController.showLessons')
.middleware('role:admin,teacher,student')
.as('courses.show-lessons');
Route.get('/:id/students', 'CoursesController.showStudents')
.middleware('role:admin,teacher,student')
.as('courses.show-students');
Route.post('/:id/attach-student', 'CoursesController.attachStudent')
.middleware('role:admin,teacher')
.as('courses.attach-student');
Route.delete('/:id/detach-student', 'CoursesController.detachStudent')
.middleware('role:admin,teacher')
.as('courses.detach-student');
}).prefix('courses');

40
routes/apis/v1/index.ts Normal file
View File

@ -0,0 +1,40 @@
/* eslint-disable global-require */
import Route from '@ioc:Adonis/Core/Route';
Route.group(() => {
Route.get('/', () => `Educt Backend API.`).as('index');
/**
*
* Auth module
*
*/
require('./auth');
Route.group(() => {
/**
*
* Users module
*
*/
require('./users');
/**
*
* Me module
*
*/
require('./me');
/**
*
* Courses module
*
*/
require('./courses');
}).middleware('auth');
})
.prefix('v1')
.as('v1');

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

@ -0,0 +1,11 @@
import Route from '@ioc:Adonis/Core/Route';
Route.group(() => {
Route.get('/', 'MeController.show').middleware('role:admin,teacher,student').as('me.show');
Route.patch('/password', 'MeController.changePassword')
.middleware('role:admin,teacher,student')
.as('me.change-password');
Route.put('/contacts', 'MeController.updateContacts')
.middleware('role:admin,teacher,student')
.as('me.update-contacts');
}).prefix('me');

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

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

26
routes/index.ts Normal file
View File

@ -0,0 +1,26 @@
/* eslint-disable global-require */
import Route from '@ioc:Adonis/Core/Route';
/*
|--------------------------------------------------------------------------
| Routes
|--------------------------------------------------------------------------
|
| This file is dedicated for defining HTTP routes. A single file is enough
| for majority of projects, however you can define routes in different
| files and just make sure to import them inside this file. For example
|
| Define routes in following two files
| ├── start/routes/cart.ts
| ├── start/routes/customer.ts
|
| and then import them inside `start/routes.ts` as follows
|
| import './routes/cart'
| import './routes/customer'
|
*/
Route.group(() => {
require('./api');
});

View File

@ -1,89 +0,0 @@
/*
|--------------------------------------------------------------------------
| Routes
|--------------------------------------------------------------------------
|
| This file is dedicated for defining HTTP routes. A single file is enough
| for majority of projects, however you can define routes in different
| files and just make sure to import them inside this file. For example
|
| Define routes in following two files
| ├── start/routes/cart.ts
| ├── start/routes/customer.ts
|
| and then import them inside `start/routes.ts` as follows
|
| import './routes/cart'
| import './routes/customer'
|
*/
import Route from '@ioc:Adonis/Core/Route';
Route.get('/', () => `Educt Backend API.`);
Route.group(() => {
/* Auth controller */
Route.post('login', 'AuthController.login').as('login');
Route.post('logout', 'AuthController.logout').middleware('auth').as('logout');
/* API */
Route.group(() => {
/**
* Users controller
*/
Route.get('users', 'UsersController.index').middleware('role:admin,teacher,student').as('showAllUsers');
Route.post('users', 'UsersController.create').middleware('role:admin').as('createUser');
Route.get('users/:id', 'UsersController.show').middleware('role:admin,teacher,student').as('showUserById');
Route.patch('users/:id', 'UsersController.update').middleware('role:admin').as('updateUser');
Route.delete('users/:id', 'UsersController.destroy').middleware('role:admin').as('deleteUser');
Route.post('users/:id/attach-roles', 'UsersController.attachRoles').middleware('role:admin').as('attachUserRole');
Route.delete('users/:id/detach-roles', 'UsersController.detachRoles').middleware('role:admin').as('detachUserRole');
/**
* Me controller
*/
Route.get('me', 'MeController.index').middleware('role:admin,teacher,student').as('showMe');
Route.patch('me/password', 'MeController.changePassword')
.middleware('role:admin,teacher,student')
.as('changePassword');
Route.put('me/contacts', 'MeController.updateContacts')
.middleware('role:admin,teacher,student')
.as('updateContacts');
/**
* Courses controller
*/
Route.get('courses', 'CoursesController.index').middleware('role:admin,teacher,student').as('showAllCourses');
Route.post('courses', 'CoursesController.create').middleware('role:admin,teacher').as('createCourse');
Route.get('courses/:id', 'CoursesController.show').middleware('role:admin,teacher,student').as('showCourseById');
Route.delete('courses/:id', 'CoursesController.destroy').middleware('role:admin,teacher').as('deleteCourseById');
Route.patch('courses/:id', 'CoursesController.update').middleware('role:admin,teacher').as('updateCourse');
Route.get('courses/:id/teacher', 'CoursesController.showTeacher')
.middleware('role:admin,teacher,student')
.as('showCourseTeacher');
Route.get('courses/:id/category', 'CoursesController.showCategory')
.middleware('role:admin,teacher,student')
.as('showCourseCategory');
Route.get('courses/:id/lessons', 'CoursesController.showLessons')
.middleware('role:admin,teacher,student')
.as('showCourseLessons');
Route.get('courses/:id/students', 'CoursesController.showStudents')
.middleware('role:admin,teacher,student')
.as('showCourseStudents');
Route.post('courses/:id/attach-student', 'CoursesController.attachStudent')
.middleware('role:admin,teacher')
.as('attachUserCourse');
Route.delete('courses/:id/detach-student', 'CoursesController.detachStudent')
.middleware('role:admin,teacher')
.as('detachUserCourse');
}).middleware('auth');
})
.prefix('/api/v1')
.as('api');