implented multer to offer endpoints for upload and retrieval of images

This commit is contained in:
Pau 2025-05-09 02:13:29 +02:00
parent 31540d4d4b
commit 24f1ca4350
3 changed files with 118 additions and 5 deletions

80
src/middlewares/upload.js Normal file
View File

@ -0,0 +1,80 @@
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import crypto from 'crypto';
import config from '../config/index.js';
const uploadDir = path.resolve(process.cwd(), config.uploadDir);
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
let userPath = req.body.path || '';
userPath = path.normalize(userPath).replace(/^(\.\.[\/\\])+/, '');
const targetPath = path.join(uploadDir, userPath);
if (!targetPath.startsWith(uploadDir)) {
return cb(new Error('Invalid path.'));
}
if (!fs.existsSync(targetPath)) {
fs.mkdirSync(targetPath, { recursive: true });
}
cb(null, targetPath);
},
filename: (req, file, cb) => {
const randomName = crypto.randomBytes(16).toString('hex');
const ext = path.extname(file.originalname);
cb(null, randomName + ext);
}
});
const fileFilter = (req, file, cb) => {
const allowedExtensions = /jpeg|jpg|png|gif/;
const allowedMimeTypes = /image\/jpeg|image\/jpg|image\/png|image\/gif/;
const extname = allowedExtensions.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedMimeTypes.test(file.mimetype);
if (extname && mimetype) {
cb(null, true);
} else {
cb(new multer.MulterError('LIMIT_UNEXPECTED_FILE', file.fieldname));
}
};
const multerUpload = multer({
storage,
fileFilter,
limits: { fileSize: 1024 * 1024 * 1024 } // 1GB
});
const uploadMiddleware = (fieldName, multiple = false) => (req, res, next) => {
const uploader = multiple ? multerUpload.array(fieldName) : multerUpload.single(fieldName);
uploader(req, res, (err) => {
if (err instanceof multer.MulterError) {
return res.status(400).json({ error: err.message });
} else if (err) {
return res.status(500).json({ error: 'Internal server error during file upload.' });
}
if (!multiple && !req.file) {
return res.status(400).json({ error: `No file uploaded under field "${fieldName}"` });
}
const result = req.file
next(result);
});
};
export {
fileFilter,
uploadMiddleware
};

View File

@ -15,7 +15,7 @@ import {
sendOkResponse,
sendResponseNoContent,
} from '../utils/responses.js'
import { latitudeRange, longitudeRange, uuid, varChar } from '../validators/expressValidator/customValidators.js'
import { json, latitudeRange, longitudeRange, uuid, varChar } from '../validators/expressValidator/customValidators.js'
import { integerRange } from '../validators/expressValidator/customValidators.js'
import {payloadExpressValidator} from '../validators/expressValidator/payloadExpressValidator.js'
import { authorizePermission, setToken, authenticateToken, refreshAuthenticate} from '../middlewares/auth.js'
@ -86,7 +86,7 @@ export default(config) => {
(req, res, next) => indexController(req, res, next),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendOkResponse(result, req, res)
)
);
// User Routes
/**
@ -974,7 +974,8 @@ export default(config) => {
varChar('address').optional({ nullable: true, values: 'falsy' }),
varChar('description').optional({ nullable: true, values: 'falsy' }),
latitudeRange('latitude'),
longitudeRange('longitude')
longitudeRange('longitude'),
json('images').optional({ nullable: true, values: 'falsy' }),
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => insertPlaceController(req, res, next, config),
@ -1012,7 +1013,8 @@ export default(config) => {
varChar('address').optional({ nullable: true, values: 'falsy' }),
varChar('description').optional({ nullable: true, values: 'falsy' }),
latitudeRange('latitude').optional({ nullable: false, values: 'falsy' }),
longitudeRange('longitude').optional({nullable: false, values: 'falsy'})
longitudeRange('longitude').optional({nullable: false, values: 'falsy'}),
json('images').optional({ nullable: true, values: 'falsy' }),
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => modifyPlaceController(req, res, next, config),
@ -1221,6 +1223,7 @@ export default(config) => {
uuid('user_uuid'),
uuid('report_type_uuid').optional({ nullable: false, values: 'falsy' }),
integerRange('rating', {min: 1, max: 3}),
json('images').optional({ nullable: true, values: 'falsy' }),
varChar('description').optional({ nullable: true, values: 'falsy' }),
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
@ -1258,6 +1261,7 @@ export default(config) => {
uuid('user_uuid').optional({ nullable: false, values: 'falsy' }),
uuid('report_type_uuid').optional({ nullable: false, values: 'falsy' }),
integerRange('rating', {min: 1, max: 3}).optional({ nullable: false, values: 'falsy' }),
json('images').optional({ nullable: true, values: 'falsy' }),
varChar('description').optional({ nullable: true, values: 'falsy' }),
],
(req, res, next) => payloadExpressValidator(req, res, next, config),

View File

@ -0,0 +1,29 @@
import { uploadMiddleware } from '../middlewares/upload.js'; // tu middleware multer
import { Router } from 'express'
export default(config) => {
/**
* Express router to mount user related functions on.
* @type {Object}
* @const
* @namespace placeRouter
*/
const router = Router()
const hasAddLinks = config.environment !== 'production'
// Single file upload route
router.post(
'/upload',
uploadMiddleware('file'),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendOkResponse(result, req, res));
// Multiple file upload route
router.post(
'/upload-multiple',
uploadMiddleware('file', true),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendOkResponse(result, req, res)
)
}