mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
feat: image repository & images table
This commit is contained in:
parent
fb92e0e4e4
commit
18819024b2
@ -17,13 +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';
|
||||
|
||||
export default class Course extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
public id: string;
|
||||
|
||||
@column()
|
||||
public bg_image: string;
|
||||
@column({ serializeAs: null })
|
||||
public bg_image_id: number;
|
||||
|
||||
@column()
|
||||
public title: string;
|
||||
@ -45,6 +46,11 @@ export default class Course extends BaseModel {
|
||||
})
|
||||
public category: BelongsTo<typeof Category>;
|
||||
|
||||
@belongsTo(() => Image, {
|
||||
foreignKey: 'bg_image_id',
|
||||
})
|
||||
public image: BelongsTo<typeof Image>;
|
||||
|
||||
@belongsTo(() => User, {
|
||||
foreignKey: 'teacher_id',
|
||||
})
|
||||
|
||||
22
app/Models/Image.ts
Normal file
22
app/Models/Image.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
|
||||
export default class Image extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
public id: number;
|
||||
|
||||
@column()
|
||||
public path: string;
|
||||
|
||||
@column()
|
||||
public name: string;
|
||||
|
||||
@column()
|
||||
public ext: string;
|
||||
|
||||
@column.dateTime({ autoCreate: true })
|
||||
public createdAt: DateTime;
|
||||
|
||||
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
||||
public updatedAt: DateTime;
|
||||
}
|
||||
@ -1,13 +1,3 @@
|
||||
/**
|
||||
* Services
|
||||
*/
|
||||
import CloudinaryService from '@ioc:Adonis/Addons/Cloudinary';
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
import { cuid } from '@ioc:Adonis/Core/Helpers';
|
||||
|
||||
/**
|
||||
* Datatypes
|
||||
*/
|
||||
@ -160,16 +150,10 @@ export default class CourseRepository {
|
||||
* @returns Created course
|
||||
*/
|
||||
public async create(data: CreateCourseValidator['schema']['props']): Promise<Course> {
|
||||
/**
|
||||
* Load background image
|
||||
*/
|
||||
const imageUploadResponse = await CloudinaryService.upload(data.background_image, cuid());
|
||||
|
||||
/**
|
||||
* Create new course
|
||||
*/
|
||||
const course = await this.Course.create({
|
||||
bg_image: imageUploadResponse.secure_url,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
status: CourseStatusEnum.DRAFT, // make course DRAFT before adding lessons
|
||||
|
||||
42
app/Repositories/ImageRepository.ts
Normal file
42
app/Repositories/ImageRepository.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { MultipartFileContract } from '@ioc:Adonis/Core/BodyParser';
|
||||
import CloudinaryService from '@ioc:Adonis/Addons/Cloudinary';
|
||||
import Image from 'App/Models/Image';
|
||||
|
||||
export default class ImageRepository {
|
||||
private Image: typeof Image;
|
||||
|
||||
constructor() {
|
||||
this.Image = Image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create image in cloudinary cloud
|
||||
*
|
||||
* @param file File
|
||||
* @param publicId Image public id
|
||||
* @returns Created image
|
||||
*/
|
||||
public async createInCloudinaryCloud(file: MultipartFileContract, publicId: string | undefined): Promise<Image> {
|
||||
const response = await CloudinaryService.upload(file, publicId);
|
||||
const image = await this.Image.create({ name: publicId, path: response.secure_url, ext: response.format });
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete image from cloudinary cloud
|
||||
*
|
||||
* @param id Image id
|
||||
* @returns Result or null
|
||||
*/
|
||||
public async deleteFormCloudinaryCloud(id: number): Promise<any> {
|
||||
const image = await this.Image.query().where('id', id).first();
|
||||
|
||||
if (image) {
|
||||
const result = await CloudinaryService.destroy(image.name);
|
||||
await image.delete();
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import * as cloudinary from 'cloudinary';
|
||||
import { UploadApiResponse, UploadApiOptions, ResponseCallback } from 'cloudinary';
|
||||
import { UploadApiResponse, UploadApiOptions, ResponseCallback, ResourceType, DeliveryType } from 'cloudinary';
|
||||
import { MultipartFileContract } from '@ioc:Adonis/Core/BodyParser';
|
||||
import { CloudinaryConfig, CloudinaryInterface } from '@ioc:Adonis/Addons/Cloudinary';
|
||||
|
||||
@ -39,6 +39,18 @@ export default class CloudinaryService implements CloudinaryInterface {
|
||||
return response;
|
||||
}
|
||||
|
||||
public async destroy(
|
||||
publicId: string,
|
||||
options?: {
|
||||
resource_type?: ResourceType;
|
||||
type?: DeliveryType;
|
||||
invalidate?: boolean;
|
||||
}
|
||||
): Promise<any> {
|
||||
const response = await this.cloudinary.v2.uploader.destroy(publicId, options);
|
||||
return response;
|
||||
}
|
||||
|
||||
public getConfig(): CloudinaryConfig {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { cuid } from '@ioc:Adonis/Core/Helpers';
|
||||
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||
|
||||
@ -16,6 +17,7 @@ import RoleHelper from 'App/Helpers/RoleHelper';
|
||||
import CourseRepository from 'App/Repositories/CourseRepository';
|
||||
import UserRepository from 'App/Repositories/UserRepository';
|
||||
import CategoryRepository from 'App/Repositories/CategoryRepository';
|
||||
import ImageRepository from 'App/Repositories/ImageRepository';
|
||||
|
||||
/**
|
||||
* Validators
|
||||
@ -31,14 +33,18 @@ export default class CourseService {
|
||||
|
||||
private categoryRepository: CategoryRepository;
|
||||
|
||||
private imageRepository: ImageRepository;
|
||||
|
||||
constructor(
|
||||
courseRepository: CourseRepository,
|
||||
userRepository: UserRepository,
|
||||
categoryRepository: CategoryRepository
|
||||
categoryRepository: CategoryRepository,
|
||||
imageRepository: ImageRepository
|
||||
) {
|
||||
this.courseRepository = courseRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.categoryRepository = categoryRepository;
|
||||
this.imageRepository = imageRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -274,11 +280,15 @@ export default class CourseService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating new course
|
||||
*/
|
||||
/* Create course */
|
||||
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 {
|
||||
success: true,
|
||||
status: StatusCodeEnum.CREATED,
|
||||
@ -308,6 +318,11 @@ export default class CourseService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete background image from course
|
||||
*/
|
||||
await this.imageRepository.deleteFormCloudinaryCloud(course.bg_image_id);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status: StatusCodeEnum.OK,
|
||||
|
||||
@ -7,7 +7,7 @@ export default class CreateCourseValidator {
|
||||
public schema = schema.create({
|
||||
title: schema.string(),
|
||||
description: schema.string(),
|
||||
background_image: schema.file({
|
||||
background_image: schema.file.optional({
|
||||
size: '2mb',
|
||||
extnames: ['jpg', 'jpeg', 'png'],
|
||||
}),
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
declare module '@ioc:Adonis/Addons/Cloudinary' {
|
||||
import { ResponseCallback, TransformationOptions, UploadApiOptions, UploadApiResponse } from 'cloudinary';
|
||||
import {
|
||||
ResponseCallback,
|
||||
TransformationOptions,
|
||||
UploadApiOptions,
|
||||
UploadApiResponse,
|
||||
ResourceType,
|
||||
DeliveryType,
|
||||
} from 'cloudinary';
|
||||
import { MultipartFileContract } from '@ioc:Adonis/Core/BodyParser';
|
||||
|
||||
export interface CloudinaryConfig {
|
||||
@ -18,6 +25,15 @@ declare module '@ioc:Adonis/Addons/Cloudinary' {
|
||||
callback?: ResponseCallback
|
||||
): Promise<UploadApiResponse>;
|
||||
|
||||
destroy(
|
||||
publicId: string,
|
||||
options?: {
|
||||
resource_type?: ResourceType;
|
||||
type?: DeliveryType;
|
||||
invalidate?: boolean;
|
||||
}
|
||||
): Promise<any>;
|
||||
|
||||
getConfig(): CloudinaryConfig;
|
||||
}
|
||||
|
||||
|
||||
@ -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.string('bg_image');
|
||||
table.string('bg_image_id');
|
||||
table.string('title').notNullable();
|
||||
table.string('description').notNullable();
|
||||
table.string('teacher_id', 21).unsigned().references('users.id').onDelete('CASCADE');
|
||||
|
||||
20
database/migrations/1629037559962_images.ts
Normal file
20
database/migrations/1629037559962_images.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
|
||||
|
||||
export default class Images extends BaseSchema {
|
||||
protected tableName = 'images';
|
||||
|
||||
public async up() {
|
||||
this.schema.createTable(this.tableName, table => {
|
||||
table.increments('id');
|
||||
table.string('path').notNullable();
|
||||
table.string('name').notNullable();
|
||||
table.string('ext').notNullable();
|
||||
table.timestamp('created_at', { useTz: true });
|
||||
table.timestamp('updated_at', { useTz: true });
|
||||
});
|
||||
}
|
||||
|
||||
public async down() {
|
||||
this.schema.dropTable(this.tableName);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user