mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 18:29:15 +03:00
feat: download material of lesson
This commit is contained in:
parent
5b635ed164
commit
be2f05a780
@ -1,3 +1,4 @@
|
||||
import Application from '@ioc:Adonis/Core/Application';
|
||||
import { Exception, inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
@ -6,6 +7,11 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
*/
|
||||
import LessonService from 'App/Services/LessonService';
|
||||
|
||||
/**
|
||||
* Models
|
||||
*/
|
||||
import LessonMaterial from 'App/Models/LessonMaterial';
|
||||
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
@ -96,7 +102,7 @@ export default class LessonsController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lesson content by id
|
||||
* Get lesson content by lesson id
|
||||
* GET /lessons/:id/content
|
||||
*/
|
||||
public async getContent(ctx: HttpContextContract) {
|
||||
@ -108,6 +114,29 @@ export default class LessonsController extends BaseController {
|
||||
|
||||
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lesson materials by file name
|
||||
* GET /lessons/materials/:file
|
||||
*/
|
||||
public async getMaterial(ctx: HttpContextContract) {
|
||||
const { file } = ctx.request.params();
|
||||
const { data, message, status, success, error } = await this.lessonService.fetchMaterialFile(ctx, file);
|
||||
|
||||
if (!success && error) {
|
||||
throw new Exception(message, status, error.code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send file to user
|
||||
*/
|
||||
if (data instanceof LessonMaterial) {
|
||||
const path = Application.makePath(data.url);
|
||||
return this.sendFile(ctx, path, data.clientName);
|
||||
}
|
||||
|
||||
return this.sendResponse(ctx, data, message, status);
|
||||
}
|
||||
}
|
||||
|
||||
new Ioc().make(LessonsController);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
import { ResponseContract } from '@ioc:Adonis/Core/Response';
|
||||
|
||||
export default class BaseController {
|
||||
public sendResponse(ctx: HttpContextContract, data: object, message: string, status = 200, meta?: any): void {
|
||||
@ -13,7 +14,7 @@ export default class BaseController {
|
||||
return ctx.response.status(response.status).send(response);
|
||||
}
|
||||
|
||||
public sendFile(ctx: HttpContextContract, path: string) {
|
||||
return ctx.response.download(path);
|
||||
public sendFile(ctx: HttpContextContract, ...args: Parameters<ResponseContract['attachment']>) {
|
||||
return ctx.response.attachment(...args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
export default interface IResponse {
|
||||
export default interface IResponse<T = {}> {
|
||||
success: boolean;
|
||||
status: number;
|
||||
message: string;
|
||||
data: object;
|
||||
data: T | {};
|
||||
meta?: {
|
||||
pagination?: any;
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@ import { DateTime } from 'luxon';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { BaseModel, beforeCreate, BelongsTo, belongsTo, column, HasOne, hasOne } from '@ioc:Adonis/Lucid/Orm';
|
||||
import Course from './Course';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import LessonContent from './LessonContent';
|
||||
|
||||
export default class Lesson extends BaseModel {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { BaseModel, column, HasMany, hasMany } from '@ioc:Adonis/Lucid/Orm';
|
||||
/* eslint-disable import/no-cycle */
|
||||
import { BaseModel, BelongsTo, belongsTo, column, HasMany, hasMany } from '@ioc:Adonis/Lucid/Orm';
|
||||
import { DateTime } from 'luxon';
|
||||
import Lesson from './Lesson';
|
||||
import LessonMaterial from './LessonMaterial';
|
||||
|
||||
export default class LessonContent extends BaseModel {
|
||||
@ -15,6 +17,9 @@ export default class LessonContent extends BaseModel {
|
||||
@hasMany(() => LessonMaterial, { foreignKey: 'lesson_content_id' })
|
||||
public materials: HasMany<typeof LessonMaterial>;
|
||||
|
||||
@belongsTo(() => Lesson, { foreignKey: 'lesson_id' })
|
||||
public lesson: BelongsTo<typeof Lesson>;
|
||||
|
||||
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||
public createdAt: DateTime;
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm';
|
||||
import { DateTime } from 'luxon';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import LessonContent from './LessonContent';
|
||||
|
||||
export default class LessonMaterial extends BaseModel {
|
||||
@column({ isPrimary: true })
|
||||
@ -11,12 +13,20 @@ export default class LessonMaterial extends BaseModel {
|
||||
@column()
|
||||
public name: string;
|
||||
|
||||
@column()
|
||||
public clientName: string;
|
||||
|
||||
@column()
|
||||
public ext: string;
|
||||
|
||||
@column()
|
||||
public lesson_content_id: number;
|
||||
|
||||
@belongsTo(() => LessonContent, {
|
||||
foreignKey: 'lesson_content_id',
|
||||
})
|
||||
public content: BelongsTo<typeof LessonContent>;
|
||||
|
||||
@column.dateTime({ autoCreate: true })
|
||||
public createdAt: DateTime;
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
/**
|
||||
* Models
|
||||
*/
|
||||
import Drive from '@ioc:Adonis/Core/Drive';
|
||||
import Course from 'App/Models/Course';
|
||||
import Lesson from 'App/Models/Lesson';
|
||||
import LessonMaterial from 'App/Models/LessonMaterial';
|
||||
|
||||
/**
|
||||
* Validators
|
||||
@ -13,8 +15,11 @@ import UpdateLessonValidator from 'App/Validators/Lesson/UpdateLessonValidator';
|
||||
export default class LessonRepository {
|
||||
private Lesson: typeof Lesson;
|
||||
|
||||
private LessonMaterial: typeof LessonMaterial;
|
||||
|
||||
constructor() {
|
||||
this.Lesson = Lesson;
|
||||
this.LessonMaterial = LessonMaterial;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,6 +54,17 @@ export default class LessonRepository {
|
||||
return lesson && lesson.content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lesson material
|
||||
*
|
||||
* @param fileName Name of file
|
||||
* @returns Lesson material file or null
|
||||
*/
|
||||
public async getMaterialFileByName(fileName: string): Promise<LessonMaterial | null> {
|
||||
const material = await this.LessonMaterial.query().where('name', fileName).first();
|
||||
return material;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create lesson
|
||||
*
|
||||
@ -79,10 +95,12 @@ export default class LessonRepository {
|
||||
*/
|
||||
await file.moveToDisk('materials');
|
||||
if (file.state === 'moved') {
|
||||
const url = await Drive.getUrl(`materials/${file.fileName}`);
|
||||
await content.related('materials').create({
|
||||
name: file.clientName,
|
||||
name: file.fileName,
|
||||
clientName: file.clientName,
|
||||
ext: file.extname,
|
||||
url: file.filePath,
|
||||
url,
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||
// import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
|
||||
/**
|
||||
* Datatypes
|
||||
*/
|
||||
import HttpStatusEnum from 'App/Datatypes/Enums/HttpStatusEnum';
|
||||
// import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
|
||||
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||
// import RoleHelper from 'App/Helpers/RoleHelper';
|
||||
import LessonMaterial from 'App/Models/LessonMaterial';
|
||||
|
||||
/**
|
||||
* Repositories
|
||||
*/
|
||||
@ -77,6 +76,55 @@ export default class LessonService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch lesson material by file name
|
||||
*
|
||||
* @param fileName Name of file
|
||||
* @returns Response
|
||||
*/
|
||||
public async fetchMaterialFile(ctx: HttpContextContract, fileName: string): Promise<IResponse<LessonMaterial>> {
|
||||
const material = await this.lessonRepository.getMaterialFileByName(fileName);
|
||||
|
||||
if (!material) {
|
||||
return {
|
||||
success: false,
|
||||
status: HttpStatusEnum.NOT_FOUND,
|
||||
data: {},
|
||||
message: 'Material not found.',
|
||||
error: {
|
||||
code: 'E_NOT_FOUND',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow user to view lesson material
|
||||
*/
|
||||
await material.load('content', q => q.preload('lesson'));
|
||||
|
||||
const {
|
||||
content: { lesson },
|
||||
} = material;
|
||||
if (await ctx.bouncer.denies('viewLessonContent', lesson)) {
|
||||
return {
|
||||
success: false,
|
||||
status: HttpStatusEnum.FORBIDDEN,
|
||||
message: 'You are not able to view this file.',
|
||||
data: {},
|
||||
error: {
|
||||
code: 'E_FORBIDDEN',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status: HttpStatusEnum.OK,
|
||||
message: 'Fetched lesson material.',
|
||||
data: material,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create lesson
|
||||
*
|
||||
|
||||
@ -8,6 +8,7 @@ export default class LessonMaterials extends BaseSchema {
|
||||
table.increments('id').primary();
|
||||
table.string('url').notNullable();
|
||||
table.string('name').notNullable();
|
||||
table.string('client_name').notNullable();
|
||||
table.string('ext').notNullable();
|
||||
table.integer('lesson_content_id').unsigned().references('lesson_contents.id').onDelete('CASCADE');
|
||||
table.timestamp('created_at', { useTz: true });
|
||||
|
||||
@ -9,4 +9,7 @@ Route.group(() => {
|
||||
Route.get('/:id/content', 'Api/v1/LessonsController.getContent')
|
||||
.middleware('role:admin,teacher,student')
|
||||
.as('lessons.get-content');
|
||||
Route.get('/materials/:file', 'Api/v1/LessonsController.getMaterial')
|
||||
.middleware('role:admin,teacher,student')
|
||||
.as('lessons.get-material');
|
||||
}).prefix('lessons');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user