From 98a67f1744db72482cb6e6d9bcb47fb0f0b44b9c Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Sat, 23 Oct 2021 21:08:08 +0300 Subject: [PATCH] refactor: aseets route & database seeders --- app/Controllers/Http/Api/AssetsController.ts | 2 +- app/Models/Course.ts | 14 +++++------ app/Models/Image.ts | 2 +- app/Repositories/CourseRepository.ts | 11 ++------- app/Repositories/ImageRepository.ts | 4 ++-- app/Services/CourseService.ts | 5 ---- app/Services/ImageService.ts | 2 +- .../Contacts/UpdateContactsValidator.ts | 5 +++- .../Course/CreateCourseValidator.ts | 5 +++- database/factories/index.ts | 24 +++++++++++++++++-- database/migrations/1627300211260_courses.ts | 2 +- database/seeders/Course.ts | 18 +++++--------- database/seeders/User.ts | 7 ------ routes/api.ts | 5 ---- routes/{apis/assets/index.ts => assets.ts} | 0 routes/index.ts | 5 ++++ 16 files changed, 56 insertions(+), 55 deletions(-) rename routes/{apis/assets/index.ts => assets.ts} (100%) diff --git a/app/Controllers/Http/Api/AssetsController.ts b/app/Controllers/Http/Api/AssetsController.ts index c5914b7..6d2efcb 100644 --- a/app/Controllers/Http/Api/AssetsController.ts +++ b/app/Controllers/Http/Api/AssetsController.ts @@ -19,7 +19,7 @@ export default class AssetsController extends BaseController { /** * Send background image of course - * GET /media/images/courses/:fileName + * GET /assets/images/courses/:fileName */ public async serveCourseImage(ctx: HttpContextContract) { const result = await this.assetsService.getCourseImagePath(ctx.params.fileName); diff --git a/app/Models/Course.ts b/app/Models/Course.ts index e45a175..a4d961a 100644 --- a/app/Models/Course.ts +++ b/app/Models/Course.ts @@ -17,14 +17,14 @@ import Category from 'App/Models/Category'; import Lesson from 'App/Models/Lesson'; import User from 'App/Models/User'; import CourseStatusEnum from 'App/Datatypes/Enums/CourseStatusEnum'; -import Image from './Image'; +// import Image from './Image'; export default class Course extends BaseModel { @column({ isPrimary: true }) public id: string; - @column({ serializeAs: null }) - public bg_image_id: number; + @column() + public bg_image_path: string; @column() public title: string; @@ -46,10 +46,10 @@ export default class Course extends BaseModel { }) public category: BelongsTo; - @belongsTo(() => Image, { - foreignKey: 'bg_image_id', - }) - public image: BelongsTo; + // @belongsTo(() => Image, { + // foreignKey: 'bg_image_id', + // }) + // public image: BelongsTo; @belongsTo(() => User, { foreignKey: 'teacher_id', diff --git a/app/Models/Image.ts b/app/Models/Image.ts index 3cebc7a..680deb1 100644 --- a/app/Models/Image.ts +++ b/app/Models/Image.ts @@ -5,7 +5,7 @@ export default class Image extends BaseModel { @column({ isPrimary: true }) public id: number; - @column({ serializeAs: null }) + @column() public path: string; @column() diff --git a/app/Repositories/CourseRepository.ts b/app/Repositories/CourseRepository.ts index be52301..0563820 100644 --- a/app/Repositories/CourseRepository.ts +++ b/app/Repositories/CourseRepository.ts @@ -59,12 +59,7 @@ export default class CourseRepository { query.andWhere('status', status); } - const courses = await query - .preload('category') - .preload('image') - .withCount('students') - .withCount('likes') - .withCount('lessons'); + const courses = await query.preload('category').withCount('students').withCount('likes').withCount('lessons'); return courses; } @@ -81,7 +76,6 @@ export default class CourseRepository { .preload('category') .preload('lessons') .preload('students') - .preload('image') .where('id', id) .where('status', CourseStatusEnum.PUBLISHED) .first(); @@ -190,7 +184,7 @@ export default class CourseRepository { * Create new course */ const course = await this.Course.create({ - bg_image_id: data.bg_image_id, + bg_image_path: data.image?.filePath, title: data.title, description: data.description, status: data.status, @@ -210,7 +204,6 @@ export default class CourseRepository { public async delete(id: string | number): Promise { const course = await this.Course.query() .preload('category') - .preload('image') .withCount('students') .withCount('likes') .withCount('lessons') diff --git a/app/Repositories/ImageRepository.ts b/app/Repositories/ImageRepository.ts index 82f0c78..281bd1e 100644 --- a/app/Repositories/ImageRepository.ts +++ b/app/Repositories/ImageRepository.ts @@ -25,11 +25,11 @@ export default class ImageRepository { return images; } - public async create(file: MultipartFileContract): Promise { + public async create({ file, location }: { file: MultipartFileContract; location: string }): Promise { /** * Save image to disk */ - await file.moveToDisk('images/courses'); + await file.moveToDisk(location); /** * Save image to database diff --git a/app/Services/CourseService.ts b/app/Services/CourseService.ts index d5ac807..745db37 100644 --- a/app/Services/CourseService.ts +++ b/app/Services/CourseService.ts @@ -312,11 +312,6 @@ export default class CourseService { }; } - /** - * Delete background image from course - */ - await this.imageRepository.delete(course.bg_image_id); - return { success: true, status: HttpStatusEnum.OK, diff --git a/app/Services/ImageService.ts b/app/Services/ImageService.ts index bcffd15..11466ff 100644 --- a/app/Services/ImageService.ts +++ b/app/Services/ImageService.ts @@ -47,7 +47,7 @@ export default class ImageService { } public async createImage(data: CreateImageValidator['schema']['props']): Promise { - const image = await this.imageRepository.create(data.image); + const image = await this.imageRepository.create({ file: data.image, location: 'images' }); return { success: true, diff --git a/app/Validators/Contacts/UpdateContactsValidator.ts b/app/Validators/Contacts/UpdateContactsValidator.ts index d2bd234..ff3c965 100644 --- a/app/Validators/Contacts/UpdateContactsValidator.ts +++ b/app/Validators/Contacts/UpdateContactsValidator.ts @@ -10,11 +10,14 @@ export default class UpdateContactsValidator { locales: ['ru-RU', 'en-US'], strict: true, }), + rules.unique({ table: 'contacts', column: 'phone_number' }), ]), 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/)]), 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', + }; } diff --git a/app/Validators/Course/CreateCourseValidator.ts b/app/Validators/Course/CreateCourseValidator.ts index f90cc56..23d8ce1 100644 --- a/app/Validators/Course/CreateCourseValidator.ts +++ b/app/Validators/Course/CreateCourseValidator.ts @@ -8,7 +8,10 @@ export default class CreateCourseValidator { public schema = schema.create({ title: 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' })]), category_id: schema.string({}, [rules.exists({ table: 'categories', column: 'id' })]), status: schema.enum(Object.values(CourseStatusEnum)), diff --git a/database/factories/index.ts b/database/factories/index.ts index fb205db..759e150 100644 --- a/database/factories/index.ts +++ b/database/factories/index.ts @@ -13,6 +13,7 @@ import Contact from 'App/Models/Contact'; import Course from 'App/Models/Course'; import Lesson from 'App/Models/Lesson'; import LessonContent from 'App/Models/LessonContent'; +import Role from 'App/Models/Role'; import User from 'App/Models/User'; export const ContactFactory = Factory.define(Contact, ({ faker }) => { @@ -25,7 +26,7 @@ export const UserFactory = Factory.define(User, ({ faker }) => { return { first_name: faker.name.firstName(), 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(), password: '123456', }; @@ -33,6 +34,25 @@ export const UserFactory = Factory.define(User, ({ faker }) => { .relation('contacts', () => ContactFactory) .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 }) => { return { 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('lessons', () => LessonFactory) - // .relation('teacher', () => UserFactory) + .relation('teacher', () => TeacherFactory) .build(); diff --git a/database/migrations/1627300211260_courses.ts b/database/migrations/1627300211260_courses.ts index 5682713..7b69724 100644 --- a/database/migrations/1627300211260_courses.ts +++ b/database/migrations/1627300211260_courses.ts @@ -11,7 +11,7 @@ export default class Courses extends BaseSchema { public async up() { this.schema.createTable(this.tableName, table => { 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('description').notNullable(); table.string('teacher_id', 21).unsigned().references('users.id').onDelete('CASCADE'); diff --git a/database/seeders/Course.ts b/database/seeders/Course.ts index bab8f71..4a21c77 100644 --- a/database/seeders/Course.ts +++ b/database/seeders/Course.ts @@ -1,21 +1,15 @@ import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'; -import Course from 'App/Models/Course'; -import User from 'App/Models/User'; import { CourseFactory } from 'Database/factories'; export default class CourseSeeder extends BaseSeeder { - private User: typeof User; + private CourseFactory: typeof CourseFactory; public async run() { - this.User = User; + this.CourseFactory = CourseFactory; - const teacher = await this.User.query() - .preload('roles') - .whereHas('roles', q => q.where('slug', 'teacher')) - .firstOrFail(); - - await CourseFactory.with('category') - .with('lessons', 2, lessonFactory => lessonFactory.with('content')) - .createMany(3, (course: Course) => course.related('teacher').associate(teacher)); + await this.CourseFactory.with('lessons', 2, lessonFactory => lessonFactory.with('content')) + .with('category') + .with('teacher') + .createMany(3); } } diff --git a/database/seeders/User.ts b/database/seeders/User.ts index 06e066a..478c001 100644 --- a/database/seeders/User.ts +++ b/database/seeders/User.ts @@ -11,7 +11,6 @@ export default class UserSeeder extends BaseSeeder { */ const roles = { admin: await Role.findByOrFail('slug', 'admin'), - teacher: await Role.findByOrFail('slug', 'teacher'), student: await Role.findByOrFail('slug', 'student'), }; @@ -27,12 +26,6 @@ export default class UserSeeder extends BaseSeeder { }); 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 */ diff --git a/routes/api.ts b/routes/api.ts index a496f70..e78f64a 100644 --- a/routes/api.ts +++ b/routes/api.ts @@ -6,11 +6,6 @@ Route.group(() => { * v1 */ require('./apis/v1/index'); - - /** - * Assets routes - */ - require('./apis/assets/index'); }) .prefix('api') .as('api'); diff --git a/routes/apis/assets/index.ts b/routes/assets.ts similarity index 100% rename from routes/apis/assets/index.ts rename to routes/assets.ts diff --git a/routes/index.ts b/routes/index.ts index d614d40..7149843 100644 --- a/routes/index.ts +++ b/routes/index.ts @@ -26,4 +26,9 @@ Route.group(() => { * Api routes */ require('./api'); + + /** + * Serve assets + */ + require('./assets'); });