mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 18:29:15 +03:00
feat: images controller
This commit is contained in:
parent
18819024b2
commit
4cd2140e17
58
app/Controllers/Http/Api/v1/ImagesController.ts
Normal file
58
app/Controllers/Http/Api/v1/ImagesController.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Exception, inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
import ImageService from 'App/Services/ImageService';
|
||||||
|
import CreateImageValidator from 'App/Validators/Image/CreateImageValidator';
|
||||||
|
import BaseController from '../../BaseController';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class ImagesController extends BaseController {
|
||||||
|
private imageService: ImageService;
|
||||||
|
|
||||||
|
constructor(imageService: ImageService) {
|
||||||
|
super();
|
||||||
|
this.imageService = imageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async list(ctx: HttpContextContract) {
|
||||||
|
const result = await this.imageService.fetchImages();
|
||||||
|
|
||||||
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async show(ctx: HttpContextContract) {
|
||||||
|
const result = await this.imageService.fetchImage(ctx.params.id);
|
||||||
|
|
||||||
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async create(ctx: HttpContextContract) {
|
||||||
|
const payload = await ctx.request.validate(CreateImageValidator);
|
||||||
|
const result = await this.imageService.createImage(payload);
|
||||||
|
|
||||||
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete(ctx: HttpContextContract) {
|
||||||
|
const result = await this.imageService.deleteImage(ctx.params.id);
|
||||||
|
|
||||||
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Ioc().make(ImagesController);
|
||||||
@ -49,7 +49,8 @@ export default class CourseRepository {
|
|||||||
.preload('teacher')
|
.preload('teacher')
|
||||||
.preload('category')
|
.preload('category')
|
||||||
.preload('lessons')
|
.preload('lessons')
|
||||||
.where('status', CourseStatusEnum.PUBLISHED);
|
.preload('image');
|
||||||
|
// .where('status', CourseStatusEnum.PUBLISHED);
|
||||||
return courses;
|
return courses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,8 +66,9 @@ 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();
|
||||||
|
|
||||||
return course;
|
return course;
|
||||||
@ -154,6 +156,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,
|
||||||
title: data.title,
|
title: data.title,
|
||||||
description: data.description,
|
description: data.description,
|
||||||
status: CourseStatusEnum.DRAFT, // make course DRAFT before adding lessons
|
status: CourseStatusEnum.DRAFT, // make course DRAFT before adding lessons
|
||||||
|
|||||||
@ -9,6 +9,16 @@ export default class ImageRepository {
|
|||||||
this.Image = Image;
|
this.Image = Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getById(id: number): Promise<Image | null> {
|
||||||
|
const image = await this.Image.query().where('id', id).first();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAll(): Promise<Image[]> {
|
||||||
|
const images = await this.Image.query();
|
||||||
|
return images;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create image in cloudinary cloud
|
* Create image in cloudinary cloud
|
||||||
*
|
*
|
||||||
@ -28,13 +38,13 @@ export default class ImageRepository {
|
|||||||
* @param id Image id
|
* @param id Image id
|
||||||
* @returns Result or null
|
* @returns Result or null
|
||||||
*/
|
*/
|
||||||
public async deleteFormCloudinaryCloud(id: number): Promise<any> {
|
public async deleteFormCloudinaryCloud(id: number): Promise<Image | null> {
|
||||||
const image = await this.Image.query().where('id', id).first();
|
const image = await this.Image.query().where('id', id).first();
|
||||||
|
|
||||||
if (image) {
|
if (image) {
|
||||||
const result = await CloudinaryService.destroy(image.name);
|
await CloudinaryService.destroy(image.name);
|
||||||
await image.delete();
|
await image.delete();
|
||||||
return result;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { cuid } from '@ioc:Adonis/Core/Helpers';
|
|
||||||
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||||
|
|
||||||
@ -283,12 +282,6 @@ export default class CourseService {
|
|||||||
/* Create course */
|
/* Create course */
|
||||||
const course = await this.courseRepository.create(data);
|
const course = await this.courseRepository.create(data);
|
||||||
|
|
||||||
/* Create image if provided */
|
|
||||||
if (data.background_image) {
|
|
||||||
const bgImage = await this.imageRepository.createInCloudinaryCloud(data.background_image, cuid());
|
|
||||||
await course.related('image').associate(bgImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: StatusCodeEnum.CREATED,
|
status: StatusCodeEnum.CREATED,
|
||||||
|
|||||||
85
app/Services/ImageService.ts
Normal file
85
app/Services/ImageService.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
|
import { cuid } from '@ioc:Adonis/Core/Helpers';
|
||||||
|
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
||||||
|
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||||
|
import ImageRepository from 'App/Repositories/ImageRepository';
|
||||||
|
import CreateImageValidator from 'App/Validators/Image/CreateImageValidator';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class ImageService {
|
||||||
|
private imageRepository: ImageRepository;
|
||||||
|
|
||||||
|
constructor(imageRepository: ImageRepository) {
|
||||||
|
this.imageRepository = imageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchImages(): Promise<IResponse> {
|
||||||
|
const images = await this.imageRepository.getAll();
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Fetched all images.',
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
data: images,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchImage(id: number): Promise<IResponse> {
|
||||||
|
const image = await this.imageRepository.getById(id);
|
||||||
|
|
||||||
|
if (!image) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Image not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Fetched image.',
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
data: image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createImage(data: CreateImageValidator['schema']['props']): Promise<IResponse> {
|
||||||
|
const image = await this.imageRepository.createInCloudinaryCloud(data.image, cuid());
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Image created.',
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
data: image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteImage(id: number): Promise<IResponse> {
|
||||||
|
const image = await this.imageRepository.deleteFormCloudinaryCloud(id);
|
||||||
|
|
||||||
|
if (!image) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Image not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Image deleted.',
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
data: image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Ioc().make(ImageService);
|
||||||
@ -7,10 +7,7 @@ export default class CreateCourseValidator {
|
|||||||
public schema = schema.create({
|
public schema = schema.create({
|
||||||
title: schema.string(),
|
title: schema.string(),
|
||||||
description: schema.string(),
|
description: schema.string(),
|
||||||
background_image: schema.file.optional({
|
bg_image_id: schema.number.optional([rules.exists({ table: 'images', column: 'id' })]),
|
||||||
size: '2mb',
|
|
||||||
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' })]),
|
||||||
});
|
});
|
||||||
|
|||||||
15
app/Validators/Image/CreateImageValidator.ts
Normal file
15
app/Validators/Image/CreateImageValidator.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { schema } from '@ioc:Adonis/Core/Validator';
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
|
||||||
|
export default class CreateImageValidator {
|
||||||
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
image: schema.file({
|
||||||
|
size: '10mb',
|
||||||
|
extnames: ['jpg', 'jpeg', 'png'],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
public messages = {};
|
||||||
|
}
|
||||||
@ -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.string('bg_image_id');
|
table.integer('bg_image_id').unsigned().references('images.id').onDelete('SET NULL');
|
||||||
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');
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
||||||
import Role from 'App/Models/Role';
|
import Role from 'App/Models/Role';
|
||||||
|
import User from 'App/Models/User';
|
||||||
import { UserFactory } from 'Database/factories';
|
import { UserFactory } from 'Database/factories';
|
||||||
|
|
||||||
export default class UserSeeder extends BaseSeeder {
|
export default class UserSeeder extends BaseSeeder {
|
||||||
@ -17,7 +18,13 @@ export default class UserSeeder extends BaseSeeder {
|
|||||||
/**
|
/**
|
||||||
* Administrator
|
* Administrator
|
||||||
*/
|
*/
|
||||||
const userAdmin = await UserFactory.with('contacts').create();
|
const userAdmin = await User.create({
|
||||||
|
first_name: 'John',
|
||||||
|
last_name: 'Doe',
|
||||||
|
login: 'admin',
|
||||||
|
password: '12345',
|
||||||
|
email: 'example.email@mail.com',
|
||||||
|
});
|
||||||
await userAdmin.related('roles').attach([roles.admin.id]);
|
await userAdmin.related('roles').attach([roles.admin.id]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
8
routes/apis/v1/images.ts
Normal file
8
routes/apis/v1/images.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Route from '@ioc:Adonis/Core/Route';
|
||||||
|
|
||||||
|
Route.group(() => {
|
||||||
|
Route.post('/', 'Api/v1/ImagesController.create').as('images.create');
|
||||||
|
Route.delete('/:id', 'Api/v1/ImagesController.delete').as('images.delete');
|
||||||
|
Route.get('/', 'Api/v1/ImagesController.list').as('images.list');
|
||||||
|
Route.get('/:id', 'Api/v1/ImagesController.show').as('images.show');
|
||||||
|
}).prefix('images');
|
||||||
@ -48,6 +48,13 @@ Route.group(() => {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
require('./lessons');
|
require('./lessons');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Images module
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
require('./images');
|
||||||
}).middleware('auth');
|
}).middleware('auth');
|
||||||
})
|
})
|
||||||
.prefix('v1')
|
.prefix('v1')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user