refactor: aseets route & database seeders

This commit is contained in:
Sergey Yarkov 2021-10-23 21:08:08 +03:00
parent ee25d495e1
commit 98a67f1744
16 changed files with 56 additions and 55 deletions

View File

@ -19,7 +19,7 @@ export default class AssetsController extends BaseController {
/** /**
* Send background image of course * Send background image of course
* GET /media/images/courses/:fileName * GET /assets/images/courses/:fileName
*/ */
public async serveCourseImage(ctx: HttpContextContract) { public async serveCourseImage(ctx: HttpContextContract) {
const result = await this.assetsService.getCourseImagePath(ctx.params.fileName); const result = await this.assetsService.getCourseImagePath(ctx.params.fileName);

View File

@ -17,14 +17,14 @@ import Category from 'App/Models/Category';
import Lesson from 'App/Models/Lesson'; import Lesson from 'App/Models/Lesson';
import User from 'App/Models/User'; import User from 'App/Models/User';
import CourseStatusEnum from 'App/Datatypes/Enums/CourseStatusEnum'; import CourseStatusEnum from 'App/Datatypes/Enums/CourseStatusEnum';
import Image from './Image'; // import Image from './Image';
export default class Course extends BaseModel { export default class Course extends BaseModel {
@column({ isPrimary: true }) @column({ isPrimary: true })
public id: string; public id: string;
@column({ serializeAs: null }) @column()
public bg_image_id: number; public bg_image_path: string;
@column() @column()
public title: string; public title: string;
@ -46,10 +46,10 @@ export default class Course extends BaseModel {
}) })
public category: BelongsTo<typeof Category>; public category: BelongsTo<typeof Category>;
@belongsTo(() => Image, { // @belongsTo(() => Image, {
foreignKey: 'bg_image_id', // foreignKey: 'bg_image_id',
}) // })
public image: BelongsTo<typeof Image>; // public image: BelongsTo<typeof Image>;
@belongsTo(() => User, { @belongsTo(() => User, {
foreignKey: 'teacher_id', foreignKey: 'teacher_id',

View File

@ -5,7 +5,7 @@ export default class Image extends BaseModel {
@column({ isPrimary: true }) @column({ isPrimary: true })
public id: number; public id: number;
@column({ serializeAs: null }) @column()
public path: string; public path: string;
@column() @column()

View File

@ -59,12 +59,7 @@ export default class CourseRepository {
query.andWhere('status', status); query.andWhere('status', status);
} }
const courses = await query const courses = await query.preload('category').withCount('students').withCount('likes').withCount('lessons');
.preload('category')
.preload('image')
.withCount('students')
.withCount('likes')
.withCount('lessons');
return courses; return courses;
} }
@ -81,7 +76,6 @@ export default class CourseRepository {
.preload('category') .preload('category')
.preload('lessons') .preload('lessons')
.preload('students') .preload('students')
.preload('image')
.where('id', id) .where('id', id)
.where('status', CourseStatusEnum.PUBLISHED) .where('status', CourseStatusEnum.PUBLISHED)
.first(); .first();
@ -190,7 +184,7 @@ export default class CourseRepository {
* Create new course * Create new course
*/ */
const course = await this.Course.create({ const course = await this.Course.create({
bg_image_id: data.bg_image_id, bg_image_path: data.image?.filePath,
title: data.title, title: data.title,
description: data.description, description: data.description,
status: data.status, status: data.status,
@ -210,7 +204,6 @@ export default class CourseRepository {
public async delete(id: string | number): Promise<Course | null> { public async delete(id: string | number): Promise<Course | null> {
const course = await this.Course.query() const course = await this.Course.query()
.preload('category') .preload('category')
.preload('image')
.withCount('students') .withCount('students')
.withCount('likes') .withCount('likes')
.withCount('lessons') .withCount('lessons')

View File

@ -25,11 +25,11 @@ export default class ImageRepository {
return images; return images;
} }
public async create(file: MultipartFileContract): Promise<Image> { public async create({ file, location }: { file: MultipartFileContract; location: string }): Promise<Image> {
/** /**
* Save image to disk * Save image to disk
*/ */
await file.moveToDisk('images/courses'); await file.moveToDisk(location);
/** /**
* Save image to database * Save image to database

View File

@ -312,11 +312,6 @@ export default class CourseService {
}; };
} }
/**
* Delete background image from course
*/
await this.imageRepository.delete(course.bg_image_id);
return { return {
success: true, success: true,
status: HttpStatusEnum.OK, status: HttpStatusEnum.OK,

View File

@ -47,7 +47,7 @@ export default class ImageService {
} }
public async createImage(data: CreateImageValidator['schema']['props']): Promise<IResponse> { public async createImage(data: CreateImageValidator['schema']['props']): Promise<IResponse> {
const image = await this.imageRepository.create(data.image); const image = await this.imageRepository.create({ file: data.image, location: 'images' });
return { return {
success: true, success: true,

View File

@ -10,11 +10,14 @@ export default class UpdateContactsValidator {
locales: ['ru-RU', 'en-US'], locales: ['ru-RU', 'en-US'],
strict: true, strict: true,
}), }),
rules.unique({ table: 'contacts', column: 'phone_number' }),
]), ]),
vk_id: schema.string.optional({}, [rules.regex(/^([a-zA-Z0-9_]){1,64}$/)]), vk_id: schema.string.optional({}, [rules.regex(/^([a-zA-Z0-9_]){1,64}$/)]),
twitter_id: schema.string.optional({}, [rules.regex(/(^|[^@\w])@(\w{1,15})\b/)]), twitter_id: schema.string.optional({}, [rules.regex(/(^|[^@\w])@(\w{1,15})\b/)]),
telegram_id: schema.string.optional({}, [rules.regex(/(^|[^@\w])@(\w{1,64})\b/)]), telegram_id: schema.string.optional({}, [rules.regex(/(^|[^@\w])@(\w{1,64})\b/)]),
}); });
public messages = {}; public messages = {
'phone_number.unique': 'This phone number is not available',
};
} }

View File

@ -8,7 +8,10 @@ export default class CreateCourseValidator {
public schema = schema.create({ public schema = schema.create({
title: schema.string(), title: schema.string(),
description: schema.string(), description: schema.string(),
bg_image_id: schema.number.optional([rules.exists({ table: 'images', column: 'id' })]), image: schema.file.optional({
size: '10mb',
extnames: ['jpg', 'jpeg', 'png'],
}),
teacher_id: schema.string({}, [rules.exists({ table: 'users', column: 'id' })]), teacher_id: schema.string({}, [rules.exists({ table: 'users', column: 'id' })]),
category_id: schema.string({}, [rules.exists({ table: 'categories', column: 'id' })]), category_id: schema.string({}, [rules.exists({ table: 'categories', column: 'id' })]),
status: schema.enum(Object.values(CourseStatusEnum)), status: schema.enum(Object.values(CourseStatusEnum)),

View File

@ -13,6 +13,7 @@ import Contact from 'App/Models/Contact';
import Course from 'App/Models/Course'; import Course from 'App/Models/Course';
import Lesson from 'App/Models/Lesson'; import Lesson from 'App/Models/Lesson';
import LessonContent from 'App/Models/LessonContent'; import LessonContent from 'App/Models/LessonContent';
import Role from 'App/Models/Role';
import User from 'App/Models/User'; import User from 'App/Models/User';
export const ContactFactory = Factory.define(Contact, ({ faker }) => { export const ContactFactory = Factory.define(Contact, ({ faker }) => {
@ -25,7 +26,7 @@ export const UserFactory = Factory.define(User, ({ faker }) => {
return { return {
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(12)}${faker.datatype.number(100)}`,
email: faker.internet.exampleEmail(), email: faker.internet.exampleEmail(),
password: '123456', password: '123456',
}; };
@ -33,6 +34,25 @@ export const UserFactory = Factory.define(User, ({ faker }) => {
.relation('contacts', () => ContactFactory) .relation('contacts', () => ContactFactory)
.build(); .build();
export const TeacherFactory = Factory.define(User, ({ faker }) => {
return {
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
login: `${faker.lorem.word(12)}${faker.datatype.number(100)}`,
email: faker.internet.exampleEmail(),
password: '123456',
};
})
.relation('contacts', () => ContactFactory)
.after('create', async (_, user: User) => {
/**
* Get teacher role to attach after create new user
*/
const role = await Role.findByOrFail('slug', 'teacher');
user.related('roles').attach([role.id]);
})
.build();
export const LessonContentFactory = Factory.define(LessonContent, ({ faker }) => { export const LessonContentFactory = Factory.define(LessonContent, ({ faker }) => {
return { return {
video_url: `https://www.youtube.com/embed/${faker.datatype.string(10)}`, video_url: `https://www.youtube.com/embed/${faker.datatype.string(10)}`,
@ -64,5 +84,5 @@ export const CourseFactory = Factory.define(Course, ({ faker }) => {
}) })
.relation('category', () => CategoryFactory) .relation('category', () => CategoryFactory)
.relation('lessons', () => LessonFactory) .relation('lessons', () => LessonFactory)
// .relation('teacher', () => UserFactory) .relation('teacher', () => TeacherFactory)
.build(); .build();

View File

@ -11,7 +11,7 @@ export default class Courses extends BaseSchema {
public async up() { public async up() {
this.schema.createTable(this.tableName, table => { this.schema.createTable(this.tableName, table => {
table.string('id', 21).primary(); table.string('id', 21).primary();
table.integer('bg_image_id').unsigned().references('images.id').onDelete('SET NULL'); table.string('bg_image_path');
table.string('title').notNullable(); table.string('title').notNullable();
table.string('description').notNullable(); table.string('description').notNullable();
table.string('teacher_id', 21).unsigned().references('users.id').onDelete('CASCADE'); table.string('teacher_id', 21).unsigned().references('users.id').onDelete('CASCADE');

View File

@ -1,21 +1,15 @@
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'; import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
import Course from 'App/Models/Course';
import User from 'App/Models/User';
import { CourseFactory } from 'Database/factories'; import { CourseFactory } from 'Database/factories';
export default class CourseSeeder extends BaseSeeder { export default class CourseSeeder extends BaseSeeder {
private User: typeof User; private CourseFactory: typeof CourseFactory;
public async run() { public async run() {
this.User = User; this.CourseFactory = CourseFactory;
const teacher = await this.User.query() await this.CourseFactory.with('lessons', 2, lessonFactory => lessonFactory.with('content'))
.preload('roles') .with('category')
.whereHas('roles', q => q.where('slug', 'teacher')) .with('teacher')
.firstOrFail(); .createMany(3);
await CourseFactory.with('category')
.with('lessons', 2, lessonFactory => lessonFactory.with('content'))
.createMany(3, (course: Course) => course.related('teacher').associate(teacher));
} }
} }

View File

@ -11,7 +11,6 @@ export default class UserSeeder extends BaseSeeder {
*/ */
const roles = { const roles = {
admin: await Role.findByOrFail('slug', 'admin'), admin: await Role.findByOrFail('slug', 'admin'),
teacher: await Role.findByOrFail('slug', 'teacher'),
student: await Role.findByOrFail('slug', 'student'), student: await Role.findByOrFail('slug', 'student'),
}; };
@ -27,12 +26,6 @@ export default class UserSeeder extends BaseSeeder {
}); });
await userAdmin.related('roles').attach([roles.admin.id]); await userAdmin.related('roles').attach([roles.admin.id]);
/**
* Teacher
*/
const userTeacher = await UserFactory.with('contacts').create();
await userTeacher.related('roles').attach([roles.teacher.id]);
/** /**
* Student * Student
*/ */

View File

@ -6,11 +6,6 @@ Route.group(() => {
* v1 * v1
*/ */
require('./apis/v1/index'); require('./apis/v1/index');
/**
* Assets routes
*/
require('./apis/assets/index');
}) })
.prefix('api') .prefix('api')
.as('api'); .as('api');

View File

@ -26,4 +26,9 @@ Route.group(() => {
* Api routes * Api routes
*/ */
require('./api'); require('./api');
/**
* Serve assets
*/
require('./assets');
}); });