mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
lessons crud & some refactor
This commit is contained in:
parent
619bc2bc9d
commit
6979e0b912
@ -1,6 +1,11 @@
|
||||
import { Exception } from '@adonisjs/core/build/standalone';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
/**
|
||||
* Datatype
|
||||
*/
|
||||
import Role from 'App/Datatype/Role';
|
||||
|
||||
/**
|
||||
* Models
|
||||
*/
|
||||
@ -10,10 +15,10 @@ import User from 'App/Models/User';
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
import AddCourseToUserValidator from 'App/Validators/AddCourseToUserValidator';
|
||||
import CreateCourseValidator from 'App/Validators/CreateCourseValidator';
|
||||
import DelCourseFromUserValidator from 'App/Validators/DelCourseFromUserValidator';
|
||||
import UpdateCourseValidator from 'App/Validators/UpdateCourseValidator';
|
||||
import AddCourseToUserValidator from 'App/Validators/Course/AddCourseToUserValidator';
|
||||
import CreateCourseValidator from 'App/Validators/Course/CreateCourseValidator';
|
||||
import DelCourseFromUserValidator from 'App/Validators/Course/DelCourseFromUserValidator';
|
||||
import UpdateCourseValidator from 'App/Validators/Course/UpdateCourseValidator';
|
||||
|
||||
export default class CoursesController {
|
||||
private readonly Course: typeof Course;
|
||||
@ -130,7 +135,7 @@ export default class CoursesController {
|
||||
* Check user on role Teacher
|
||||
*/
|
||||
const roles = teacher.roles.map(r => r.slug);
|
||||
if (!roles.includes('teacher')) {
|
||||
if (!roles.includes(Role.TEACHER)) {
|
||||
throw new Exception(`User with id "${teacher.id}" not teacher.`, 400, 'ERR_USER_NOT_TEACHER');
|
||||
}
|
||||
|
||||
@ -212,7 +217,7 @@ export default class CoursesController {
|
||||
|
||||
const studentRoles = student.roles.map(r => r.slug);
|
||||
|
||||
if (!studentRoles.includes('student')) {
|
||||
if (!studentRoles.includes(Role.STUDENT)) {
|
||||
throw new Exception(`User cannot be attached to the course without "student" role.`, 400, 'E_USER_ROLE');
|
||||
}
|
||||
|
||||
|
||||
110
app/Controllers/Http/Api/v1/LessonsController.ts
Normal file
110
app/Controllers/Http/Api/v1/LessonsController.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
/**
|
||||
* Models
|
||||
*/
|
||||
import Lesson from 'App/Models/Lesson';
|
||||
import Course from 'App/Models/Course';
|
||||
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
import CreateLessonValidator from 'App/Validators/Lesson/CreateLessonValidator';
|
||||
import UpdateLessonValidator from 'App/Validators/Lesson/UpdateLessonValidator';
|
||||
|
||||
import BaseController from '../../BaseController';
|
||||
|
||||
export default class LessonsController extends BaseController {
|
||||
private readonly Lesson: typeof Lesson;
|
||||
|
||||
private readonly Course: typeof Course;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.Lesson = Lesson;
|
||||
this.Course = Course;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of all lessons
|
||||
* GET /lessons
|
||||
*/
|
||||
public async list(ctx: HttpContextContract) {
|
||||
const lessons = await this.Lesson.query();
|
||||
|
||||
return this.sendResponse(ctx, lessons, 'Fetched list lessons.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one by id
|
||||
* GET /lessons/:id
|
||||
*/
|
||||
public async show(ctx: HttpContextContract) {
|
||||
const lesson = await this.Lesson.findOrFail(ctx.params.id);
|
||||
|
||||
return this.sendResponse(ctx, lesson, 'Fetched lesson by id.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new lesson
|
||||
* POST /lessons
|
||||
*/
|
||||
public async create(ctx: HttpContextContract) {
|
||||
const payload = await ctx.request.validate(CreateLessonValidator);
|
||||
const course = await this.Course.findOrFail(payload.course_id);
|
||||
const lesson = await this.Lesson.create({
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
});
|
||||
|
||||
await lesson.related('course').associate(course);
|
||||
await lesson.related('content').create({
|
||||
video_url: payload.video_url,
|
||||
});
|
||||
|
||||
await lesson.load('content');
|
||||
|
||||
return this.sendResponse(ctx, lesson, 'Created new lesson.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete lesson by id.
|
||||
* DELETE /lessons/:id
|
||||
*/
|
||||
public async delete(ctx: HttpContextContract) {
|
||||
const lesson = await this.Lesson.findOrFail(ctx.params.id);
|
||||
|
||||
await lesson.delete();
|
||||
|
||||
return this.sendResponse(ctx, lesson, 'Lesson deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update lesson by id.
|
||||
* PUT /lessons/:id
|
||||
*/
|
||||
public async update(ctx: HttpContextContract) {
|
||||
const payload = await ctx.request.validate(UpdateLessonValidator);
|
||||
const lesson = await this.Lesson.firstOrFail(ctx.params.id);
|
||||
|
||||
await lesson
|
||||
.merge({
|
||||
course_id: payload.course_id,
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
})
|
||||
.save();
|
||||
|
||||
return this.sendResponse(ctx, lesson, 'Lesson updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lesson content by id
|
||||
* GET /lessons/:id/content
|
||||
*/
|
||||
public async getContent(ctx: HttpContextContract) {
|
||||
const lesson = await this.Lesson.query().preload('content').where('id', ctx.params.id).firstOrFail();
|
||||
|
||||
return this.sendResponse(ctx, lesson.content, 'Lesson content fetched.');
|
||||
}
|
||||
}
|
||||
@ -11,8 +11,8 @@ import Contact from 'App/Models/Contact';
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
import ChangePasswordValidator from 'App/Validators/ChangePasswordValidator';
|
||||
import UpdateContactsValidator from 'App/Validators/UpdateContactsValidator';
|
||||
import ChangePasswordValidator from 'App/Validators/Password/ChangePasswordValidator';
|
||||
import UpdateContactsValidator from 'App/Validators/Contacts/UpdateContactsValidator';
|
||||
|
||||
export default class MeController {
|
||||
private readonly guard: 'api';
|
||||
|
||||
@ -10,17 +10,20 @@ import User from 'App/Models/User';
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
import AddRoleToUserValidator from 'App/Validators/AddRoleToUserValidator';
|
||||
import CreateUserValidator from 'App/Validators/CreateUserValidator';
|
||||
import DelRoleFromUserValidator from 'App/Validators/DelRoleFromUserValidator';
|
||||
import UpdateUserValidator from 'App/Validators/UpdateUserValidator';
|
||||
import AddRoleToUserValidator from 'App/Validators/User/AddRoleToUserValidator';
|
||||
import CreateUserValidator from 'App/Validators/User/CreateUserValidator';
|
||||
import DelRoleFromUserValidator from 'App/Validators/User/DelRoleFromUserValidator';
|
||||
import UpdateUserValidator from 'App/Validators/User/UpdateUserValidator';
|
||||
|
||||
export default class UsersController {
|
||||
import BaseController from '../../BaseController';
|
||||
|
||||
export default class UsersController extends BaseController {
|
||||
private readonly User: typeof User;
|
||||
|
||||
private readonly Role: typeof Role;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.User = User;
|
||||
this.Role = Role;
|
||||
}
|
||||
|
||||
14
app/Controllers/Http/BaseController.ts
Normal file
14
app/Controllers/Http/BaseController.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
export default class BaseController {
|
||||
public sendResponse(ctx: HttpContextContract, data: object, message: string, status = 200): void {
|
||||
const response = {
|
||||
status,
|
||||
message,
|
||||
data,
|
||||
};
|
||||
|
||||
return ctx.response.status(response.status).send(response);
|
||||
}
|
||||
}
|
||||
7
app/Datatype/Role.ts
Normal file
7
app/Datatype/Role.ts
Normal file
@ -0,0 +1,7 @@
|
||||
enum Role {
|
||||
ADMIN = 'admin',
|
||||
TEACHER = 'teacher',
|
||||
STUDENT = 'student',
|
||||
}
|
||||
|
||||
export default Role;
|
||||
@ -5,14 +5,14 @@ export default class Role {
|
||||
error: {
|
||||
message: string;
|
||||
status: number;
|
||||
errorCode: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.error = {
|
||||
message: 'You dont have permission to perform that action.',
|
||||
status: 403,
|
||||
errorCode: 'E_ACCESS_DENIED',
|
||||
code: 'E_ACCESS_DENIED',
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export default class Role {
|
||||
/**
|
||||
* Failed to verify user rights
|
||||
*/
|
||||
throw new Exception(this.error.message, this.error.status, this.error.errorCode);
|
||||
throw new Exception(this.error.message, this.error.status, this.error.code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { DateTime } from 'luxon';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { BaseModel, beforeCreate, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
import { BaseModel, beforeCreate, BelongsTo, belongsTo, column, HasOne, hasOne } from '@ioc:Adonis/Lucid/Orm';
|
||||
import Course from './Course';
|
||||
import LessonContent from './LessonContent';
|
||||
|
||||
export default class Lesson extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
@ -16,6 +18,16 @@ export default class Lesson extends BaseModel {
|
||||
@column()
|
||||
public description: string;
|
||||
|
||||
@belongsTo(() => Course, {
|
||||
foreignKey: 'course_id',
|
||||
})
|
||||
public course: BelongsTo<typeof Course>;
|
||||
|
||||
@hasOne(() => LessonContent, {
|
||||
foreignKey: 'lesson_id',
|
||||
})
|
||||
public content: HasOne<typeof LessonContent>;
|
||||
|
||||
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||
public createdAt: DateTime;
|
||||
|
||||
|
||||
19
app/Models/LessonContent.ts
Normal file
19
app/Models/LessonContent.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
export default class LessonContent extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
public id: string;
|
||||
|
||||
@column()
|
||||
public lesson_id: string;
|
||||
|
||||
@column()
|
||||
public video_url: string;
|
||||
|
||||
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||
public createdAt: DateTime;
|
||||
|
||||
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
|
||||
public updatedAt: DateTime;
|
||||
}
|
||||
@ -73,7 +73,7 @@ export default class User extends BaseModel {
|
||||
});
|
||||
|
||||
await user.related('roles').attach(inputRoles.map(r => r.id));
|
||||
await user.load('roles'); // load updated roles
|
||||
await user.load('roles');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
15
app/Validators/Lesson/CreateLessonValidator.ts
Normal file
15
app/Validators/Lesson/CreateLessonValidator.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { schema, rules } from '@ioc:Adonis/Core/Validator';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
export default class CreateLessonValidator {
|
||||
constructor(protected ctx: HttpContextContract) {}
|
||||
|
||||
public schema = schema.create({
|
||||
course_id: schema.string({}, [rules.exists({ table: 'courses', column: 'id' })]),
|
||||
title: schema.string({}, [rules.maxLength(255)]),
|
||||
description: schema.string(),
|
||||
video_url: schema.string(),
|
||||
});
|
||||
|
||||
public messages = {};
|
||||
}
|
||||
14
app/Validators/Lesson/UpdateLessonValidator.ts
Normal file
14
app/Validators/Lesson/UpdateLessonValidator.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { schema, rules } from '@ioc:Adonis/Core/Validator';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
export default class UpdateLessonValidator {
|
||||
constructor(protected ctx: HttpContextContract) {}
|
||||
|
||||
public schema = schema.create({
|
||||
course_id: schema.string({}, [rules.exists({ table: 'courses', column: 'id' })]),
|
||||
title: schema.string({}, [rules.maxLength(255)]),
|
||||
description: schema.string(),
|
||||
});
|
||||
|
||||
public messages = {};
|
||||
}
|
||||
19
database/migrations/1628017319337_lesson_contents.ts
Normal file
19
database/migrations/1628017319337_lesson_contents.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
|
||||
|
||||
export default class LessonContents extends BaseSchema {
|
||||
protected tableName = 'lesson_contents';
|
||||
|
||||
public async up() {
|
||||
this.schema.createTable(this.tableName, table => {
|
||||
table.increments('id');
|
||||
table.string('lesson_id', 21).unsigned().references('lessons.id').unique().onDelete('CASCADE');
|
||||
table.string('video_url').notNullable();
|
||||
table.timestamp('created_at', { useTz: true });
|
||||
table.timestamp('updated_at', { useTz: true });
|
||||
});
|
||||
}
|
||||
|
||||
public async down() {
|
||||
this.schema.dropTable(this.tableName);
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,6 @@ Route.group(() => {
|
||||
* Me module
|
||||
*
|
||||
*/
|
||||
|
||||
require('./me');
|
||||
|
||||
/**
|
||||
@ -32,8 +31,14 @@ Route.group(() => {
|
||||
* Courses module
|
||||
*
|
||||
*/
|
||||
|
||||
require('./courses');
|
||||
|
||||
/**
|
||||
*
|
||||
* Lessons module
|
||||
*
|
||||
*/
|
||||
require('./lessons');
|
||||
}).middleware('auth');
|
||||
})
|
||||
.prefix('v1')
|
||||
|
||||
12
routes/apis/v1/lessons.ts
Normal file
12
routes/apis/v1/lessons.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import Route from '@ioc:Adonis/Core/Route';
|
||||
|
||||
Route.group(() => {
|
||||
Route.get('/', 'Api/v1/LessonsController.list').middleware('role:admin,teacher,student').as('lessons.list');
|
||||
Route.post('/', 'Api/v1/LessonsController.create').middleware('role:admin,teacher').as('lessons.create');
|
||||
Route.get('/:id', 'Api/v1/LessonsController.show').middleware('role:admin,teacher,student').as('lessons.show');
|
||||
Route.delete('/:id', 'Api/v1/LessonsController.delete').middleware('role:admin,teacher').as('lessons.delete');
|
||||
Route.put('/:id', 'Api/v1/LessonsController.update').middleware('role:admin,teacher').as('lessons.update');
|
||||
Route.get('/:id/content', 'Api/v1/LessonsController.getContent')
|
||||
.middleware('role:admin,teacher,student')
|
||||
.as('lessons.get-content');
|
||||
}).prefix('lessons');
|
||||
Loading…
x
Reference in New Issue
Block a user