Files
bigcapital/packages/server/src/api/controllers/Media.ts
2024-08-08 16:10:42 +02:00

212 lines
5.7 KiB
TypeScript

import { Router, Request, Response, NextFunction } from 'express';
import {
param,
query,
check,
} from 'express-validator';
import { camelCase, upperFirst } from 'lodash';
import { Inject, Service } from 'typedi';
import { IMediaLinkDTO } from '@/interfaces';
import fs from 'fs';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from './BaseController';
import MediaService from '@/services/Media/MediaService';
import { ServiceError } from '@/exceptions';
const fsPromises = fs.promises;
@Service()
export default class MediaController extends BaseController {
@Inject()
mediaService: MediaService;
/**
* Router constructor.
*/
router() {
const router = Router();
router.post('/upload', [
...this.uploadValidationSchema,
],
this.validationResult,
asyncMiddleware(this.uploadMedia.bind(this)),
this.handlerServiceErrors,
);
router.post('/:id/link', [
...this.mediaIdParamSchema,
...this.linkValidationSchema,
],
this.validationResult,
asyncMiddleware(this.linkMedia.bind(this)),
this.handlerServiceErrors,
);
router.delete('/', [
...this.deleteValidationSchema,
],
this.validationResult,
asyncMiddleware(this.deleteMedia.bind(this)),
this.handlerServiceErrors,
);
router.get('/:id', [
...this.mediaIdParamSchema,
],
this.validationResult,
asyncMiddleware(this.getMedia.bind(this)),
this.handlerServiceErrors,
);
return router;
}
get uploadValidationSchema() {
return [
check('model_name').optional().trim(),
check('model_id').optional().isNumeric(),
];
}
get linkValidationSchema() {
return [
check('model_name').exists().trim(),
check('model_id').exists().isNumeric().toInt(),
]
}
get deleteValidationSchema() {
return [
query('ids').exists().isArray(),
query('ids.*').exists().isNumeric().toInt(),
];
}
get mediaIdParamSchema() {
return [
param('id').exists().isNumeric().toInt(),
];
}
/**
* Retrieve all or the given attachment ids.
* @param {Request} req -
* @param {Response} req -
* @param {NextFunction} req -
*/
async getMedia(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: mediaId } = req.params;
try {
const media = await this.mediaService.getMedia(tenantId, mediaId);
return res.status(200).send({ media });
} catch (error) {
next(error);
}
}
/**
* Uploads media.
* @param {Request} req -
* @param {Response} req -
* @param {NextFunction} req -
*/
async uploadMedia(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { attachment } = req.files
const linkMediaDTO: IMediaLinkDTO = this.matchedBodyData(req);
const modelName = linkMediaDTO.modelName
? upperFirst(camelCase(linkMediaDTO.modelName)) : '';
try {
const media = await this.mediaService.upload(tenantId, attachment, modelName, linkMediaDTO.modelId);
return res.status(200).send({ media_id: media.id });
} catch (error) {
next(error);
}
}
/**
* Deletes the given attachment ids from file system and database.
* @param {Request} req -
* @param {Response} req -
* @param {NextFunction} req -
*/
async deleteMedia(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { ids: mediaIds } = req.query;
try {
await this.mediaService.deleteMedia(tenantId, mediaIds);
return res.status(200).send({
media_ids: mediaIds
});
} catch (error) {
next(error);
}
}
/**
* Links the given media to the specific resource model.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async linkMedia(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: mediaId } = req.params;
const linkMediaDTO: IMediaLinkDTO = this.matchedBodyData(req);
const modelName = upperFirst(camelCase(linkMediaDTO.modelName));
try {
await this.mediaService.linkMedia(tenantId, mediaId, linkMediaDTO.modelId, modelName);
return res.status(200).send({ media_id: mediaId });
} catch (error) {
next(error);
}
}
/**
* Handler service errors.
* @param {Error} error
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
handlerServiceErrors(error, req: Request, res: Response, next: NextFunction) {
if (error instanceof ServiceError) {
if (error.errorType === 'MINETYPE_NOT_SUPPORTED') {
return res.boom.badRequest(null, {
errors: [{ type: 'MINETYPE_NOT_SUPPORTED', code: 100, }]
});
}
if (error.errorType === 'MEDIA_NOT_FOUND') {
return res.boom.badRequest(null, {
errors: [{ type: 'MEDIA_NOT_FOUND', code: 200 }]
});
}
if (error.errorType === 'MODEL_NAME_HAS_NO_MEDIA') {
return res.boom.badRequest(null, {
errors: [{ type: 'MODEL_NAME_HAS_NO_MEDIA', code: 300 }]
});
}
if (error.errorType === 'MODEL_ID_NOT_FOUND') {
return res.boom.badRequest(null, {
errors: [{ type: 'MODEL_ID_NOT_FOUND', code: 400 }]
});
}
if (error.errorType === 'MEDIA_IDS_NOT_FOUND') {
return res.boom.badRequest(null, {
errors: [{ type: 'MEDIA_IDS_NOT_FOUND', code: 500 }],
});
}
if (error.errorType === 'MEDIA_LINK_EXISTS') {
return res.boom.badRequest(null, {
errors: [{ type: 'MEDIA_LINK_EXISTS', code: 600 }],
});
}
}
next(error);
}
};