|
|
'use strict'
const express = require('express') const router = express.Router()
const readChunk = require('read-chunk') const fileType = require('file-type') const Promise = require('bluebird') const fs = Promise.promisifyAll(require('fs-extra')) const path = require('path') const _ = require('lodash')
const validPathRe = new RegExp('^([a-z0-9\\/-]+\\.[a-z0-9]+)$') const validPathThumbsRe = new RegExp('^([a-z0-9]+\\.png)$')
// ==========================================
// SERVE UPLOADS FILES
// ==========================================
router.get('/t/*', (req, res, next) => { let fileName = req.params[0] if (!validPathThumbsRe.test(fileName)) { return res.sendStatus(404).end() }
// todo: Authentication-based access
res.sendFile(fileName, { root: lcdata.getThumbsPath(), dotfiles: 'deny' }, (err) => { if (err) { res.status(err.status).end() } }) })
router.post('/img', lcdata.uploadImgHandler, (req, res, next) => { let destFolder = _.chain(req.body.folder).trim().toLower().value()
upl.validateUploadsFolder(destFolder).then((destFolderPath) => { if (!destFolderPath) { res.json({ ok: false, msg: 'Invalid Folder' }) return true }
Promise.map(req.files, (f) => { let destFilename = '' let destFilePath = ''
return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => { destFilename = fname destFilePath = path.resolve(destFolderPath, destFilename)
return readChunk(f.path, 0, 262) }).then((buf) => { // -> Check MIME type by magic number
let mimeInfo = fileType(buf) if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) { return Promise.reject(new Error('Invalid file type.')) } return true }).then(() => { // -> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false }) }).then(() => { return { ok: true, filename: destFilename, filesize: f.size } }).reflect() }, {concurrency: 3}).then((results) => { let uplResults = _.map(results, (r) => { if (r.isFulfilled()) { return r.value() } else { return { ok: false, msg: r.reason().message } } }) res.json({ ok: true, results: uplResults }) return true }).catch((err) => { res.json({ ok: false, msg: err.message }) return true }) }) })
router.post('/file', lcdata.uploadFileHandler, (req, res, next) => { let destFolder = _.chain(req.body.folder).trim().toLower().value()
upl.validateUploadsFolder(destFolder).then((destFolderPath) => { if (!destFolderPath) { res.json({ ok: false, msg: 'Invalid Folder' }) return true }
Promise.map(req.files, (f) => { let destFilename = '' let destFilePath = ''
return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => { destFilename = fname destFilePath = path.resolve(destFolderPath, destFilename)
// -> Move file to final destination
return fs.moveAsync(f.path, destFilePath, { clobber: false }) }).then(() => { return { ok: true, filename: destFilename, filesize: f.size } }).reflect() }, {concurrency: 3}).then((results) => { let uplResults = _.map(results, (r) => { if (r.isFulfilled()) { return r.value() } else { return { ok: false, msg: r.reason().message } } }) res.json({ ok: true, results: uplResults }) return true }).catch((err) => { res.json({ ok: false, msg: err.message }) return true }) }) })
router.get('/*', (req, res, next) => { let fileName = req.params[0] if (!validPathRe.test(fileName)) { return res.sendStatus(404).end() }
// todo: Authentication-based access
res.sendFile(fileName, { root: git.getRepoPath() + '/uploads/', dotfiles: 'deny' }, (err) => { if (err) { res.status(err.status).end() } }) })
module.exports = router
|