Last active
March 16, 2023 01:01
-
-
Save mkhizeryounas/51d7d0221129e3f7055b7d88d4c5e6bc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import express from 'express'; | |
import Joi from './joi'; | |
import validate from 'express-validation'; | |
import HttpStatus from 'http-status-codes'; | |
import logger from './logger'; | |
const validators = { | |
list: { | |
query: { | |
limit: Joi.number().min(0).max(100).default(25), | |
page: Joi.number().min(1).default(1), | |
filter: Joi.object().default({}), | |
sort: Joi.object().default({}), | |
}, | |
}, | |
single: { | |
params: { | |
id: Joi.objectId().required(), | |
}, | |
}, | |
}; | |
class Mixin { | |
constructor({ | |
middlewares = [], | |
model, | |
prefix = null, | |
validator = {}, | |
methods = [], | |
populate = [], | |
queryset = () => ({}), | |
transform = (req) => req, | |
}) { | |
this.availableMethods = ['list', 'create', 'read', 'update', 'delete']; | |
this.middlewares = Array.isArray(middlewares) ? middlewares : [middlewares]; | |
this.populate = populate; | |
this.querysetFn = queryset; | |
this.middlewares.push(async (req, _, next) => { | |
try { | |
req.queryset = await queryset(req); | |
return next(); | |
} catch (err) { | |
return next(err); | |
} | |
}); | |
this.middlewares.push(async (req, _, next) => { | |
req = await transform(req); | |
return next(); | |
}); | |
this.model = model; | |
this.listRoute = prefix ? `/${prefix}` : '/'; | |
this.singleRoute = | |
this.listRoute !== '/' ? `${this.listRoute}/:id` : '/:id'; | |
this.validator = validator; | |
this.router = express.Router(); | |
this.router.use(async (req, _, next) => { | |
try { | |
req.self = async () => { | |
const queryset = await this.querysetFn(req); | |
const { id } = req.params; | |
if (!id) { | |
return null; | |
} | |
return this.getById({ id, queryset }); | |
}; | |
return next(); | |
} catch (err) { | |
return next(err); | |
} | |
}); | |
this.register(methods); | |
} | |
async getById({ id, queryset }) { | |
return this.model.findOne({ _id: id, ...queryset }).populate(this.populate); | |
} | |
list() { | |
this.router.get( | |
this.listRoute, | |
[...this.middlewares, validate(validators.list)], | |
async (req, res, next) => { | |
try { | |
const { queryset } = req; | |
const { filter, page, limit, sort } = req.query; | |
const data = await this.model.paginate( | |
{ ...filter, ...queryset }, | |
{ page, limit, sort, populate: this.populate } | |
); | |
res.reply({ data, status: HttpStatus.OK }); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
} | |
read() { | |
this.router.get( | |
this.singleRoute, | |
[...this.middlewares, validate(validators.single)], | |
async (req, res, next) => { | |
try { | |
const { queryset } = req; | |
const { id } = req.params; | |
const data = await this.getById({ id, queryset }); | |
if (!data) { | |
throw { | |
status: HttpStatus.NOT_FOUND, | |
}; | |
} | |
res.reply({ data, status: HttpStatus.OK }); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
} | |
create() { | |
this.router.post( | |
this.listRoute, | |
[...this.middlewares, validate({ body: this.validator })], | |
async (req, res, next) => { | |
try { | |
const { queryset } = req; | |
let data = new this.model({ | |
...req.body, | |
...queryset, | |
}); | |
await data.save(); | |
data = await this.model.populate(data, this.populate); | |
res.reply({ data, status: HttpStatus.CREATED }); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
} | |
update() { | |
this.router.put( | |
this.singleRoute, | |
[ | |
...this.middlewares, | |
validate(validators.single), | |
validate({ body: this.validator }), | |
], | |
async (req, res, next) => { | |
try { | |
const { queryset, body } = req; | |
const { id } = req.params; | |
const data = await this.model | |
.findOneAndUpdate( | |
{ | |
_id: id, | |
...queryset, | |
}, | |
{ $set: body }, | |
{ new: true } | |
) | |
.populate(this.populate); | |
if (!data) { | |
throw { | |
status: HttpStatus.NOT_FOUND, | |
}; | |
} | |
res.reply({ data, status: HttpStatus.OK }); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
} | |
delete() { | |
this.router.delete( | |
this.singleRoute, | |
[...this.middlewares, validate(validators.single)], | |
async (req, res, next) => { | |
try { | |
const { queryset } = req; | |
const { id } = req.params; | |
const data = await this.model.remove({ _id: id, ...queryset }); | |
if (!data || !data.deletedCount) { | |
throw { | |
status: HttpStatus.NOT_FOUND, | |
}; | |
} | |
res.reply({ data: '', status: HttpStatus.NO_CONTENT }); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
} | |
register(methods = this.availableMethods) { | |
methods.map((e) => { | |
if (!this.availableMethods.includes(e)) { | |
throw Error('Unknown method::', e); | |
} | |
this[e](); | |
}); | |
} | |
} | |
export const METHOD_TYPES = { | |
LIST: 'list', | |
CREATE: 'create', | |
READ: 'read', | |
UPDATE: 'update', | |
DELETE: 'delete', | |
}; | |
export default Mixin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import multer from '../middlewares/multer'; | |
import validate from 'express-validation'; | |
import * as fileController from '../controllers/files/files.controller'; | |
import * as fileValidator from '../controllers/files/files.validator'; | |
import uuid4 from 'uuid4'; | |
import { unlock } from '../utils/locker'; | |
import logger from '../utils/logger'; | |
import { File } from '../models'; | |
import Mixin from '../utils/mixin'; | |
import Joi from '../utils/joi'; | |
const validator = { | |
filename: Joi.string().required(), | |
mime: Joi.string().required(), | |
size: Joi.number().required(), | |
url: Joi.string().required(), | |
isImage: Joi.boolean().default(false), | |
uuid: Joi.string().required(), | |
key: Joi.string().required(), | |
workspace: Joi.objectId().required(), | |
}; | |
const { router } = new Mixin({ | |
model: File, | |
middlewares: [unlock], | |
validator: validator, | |
populate: ['workspace'], | |
methods: [ | |
METHOD_TYPES.LIST, | |
METHOD_TYPES.CREATE, | |
METHOD_TYPES.READ, | |
METHOD_TYPES.UPDATE, | |
METHOD_TYPES.DELETE, | |
], | |
queryset: async (req) => { | |
return { workspace: req.user.workspace._id }; | |
}, | |
transform: async (req) => { | |
return req; | |
}, | |
}); | |
// const router = mixin.router; | |
// mixin.register(['list', 'read', 'delete', 'create']); | |
router.get('/:id/details', [unlock], async (req, res) => { | |
const data = await req.self(); | |
return res.json({ data }); | |
}); | |
router.post( | |
'/upload', | |
[unlock, multer.array('files', 200)], | |
async (req, res, next) => { | |
try { | |
const { files, file } = req; | |
logger.debug({ file, files }); | |
logger.debug('Files uploaded::', files.length); | |
return res.reply({ | |
data: await fileController.saveUploadedFiles({ | |
files, | |
workspace: req.user.workspace, | |
}), | |
}); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
router.post( | |
'/upload/pre-signed', | |
[unlock, validate(fileValidator.preSigned)], | |
async (req, res, next) => { | |
try { | |
const fileId = uuid4(); | |
const subpath = `${req?.user?.workspace?.identifier}/` || ''; | |
const fileName = `uploads/${subpath}${fileId}`; | |
const presignedUrl = fileController.presignedUrl({ | |
...req.body, | |
fileName, | |
}); | |
return res.reply({ | |
data: { | |
...presignedUrl, | |
fileId: fileId, | |
}, | |
}); | |
} catch (err) { | |
return next(err); | |
} | |
} | |
); | |
module.exports = router; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment