diff --git a/routers/resource.js b/routers/resource.js index 29c957e..7ff5b95 100644 --- a/routers/resource.js +++ b/routers/resource.js @@ -3,7 +3,7 @@ const escape = require('escape-html'); const fetch = require('node-fetch'); const { deleteS3 } = require('../storage'); const { diskFilePath, s3enabled } = require('../config.json'); -const { path, log, getTrueHttp, getTrueDomain, formatBytes, formatTimestamp, getS3url, getDirectUrl, getSafeExt, getResourceColor, replaceholder } = require('../utils'); +const { path, log, getTrueHttp, getTrueDomain, formatBytes, formatTimestamp, getS3url, getDirectUrl, getResourceColor, replaceholder } = require('../utils'); const { CODE_UNAUTHORIZED, CODE_NOT_FOUND, } = require('../MagicNumbers.json'); const data = require('../data'); const users = require('../auth'); @@ -25,7 +25,6 @@ router.use((req, res, next) => { // View file router.get('/', (req, res, next) => data.get(req.ass.resourceId).then((fileData) => { const { resourceId } = req.ass; - const isVideo = fileData.mimetype.includes('video'); // Build OpenGraph meta tags const og = fileData.opengraph, ogs = ['']; @@ -33,21 +32,21 @@ router.get('/', (req, res, next) => data.get(req.ass.resourceId).then((fileData) og.description && (ogs.push(``)); // skipcq: JS-0093 og.author && (ogs.push(``)); // skipcq: JS-0093 og.color && (ogs.push(``)); // skipcq: JS-0093 - !isVideo && (ogs.push(``)); // skipcq: JS-0093 + !fileData.is.video && (ogs.push(``)); // skipcq: JS-0093 // Send the view to the client res.render('view', { - isVideo, + fileIs: fileData.is, title: escape(fileData.originalname), uploader: users[fileData.token].username, timestamp: formatTimestamp(fileData.timestamp), size: formatBytes(fileData.size), color: getResourceColor(fileData.opengraph.color || null, fileData.vibrant), resourceAttr: { src: getDirectUrl(resourceId) }, - discordUrl: `${getDirectUrl(resourceId)}${getSafeExt(fileData.mimetype)}`, + discordUrl: `${getDirectUrl(resourceId)}${fileData.ext}`, oembedUrl: `${getTrueHttp()}${getTrueDomain()}/${resourceId}/oembed`, - ogtype: isVideo ? 'video.other' : 'image', - urlType: `og:${isVideo ? 'video' : 'image'}`, + ogtype: fileData.is.video ? 'video.other' : fileData.is.image ? 'image' : 'website', + urlType: `og:${fileData.is.video ? 'video' : fileData.is.audio ? 'audio' : 'image'}`, opengraph: replaceholder(ogs.join('\n'), fileData.size, fileData.timestamp, fileData.originalname) }); }).catch(next)); @@ -60,7 +59,7 @@ router.get('/direct*', (req, res, next) => data.get(req.ass.resourceId).then((fi // Return the file differently depending on what storage option was used const uploaders = { - s3: () => fetch(getS3url(fileData.randomId, fileData.mimetype)).then((file) => { + s3: () => fetch(getS3url(fileData.randomId, fileData.ext)).then((file) => { file.headers.forEach((value, header) => res.setHeader(header, value)); file.body.pipe(res); }), @@ -76,7 +75,7 @@ router.get('/direct*', (req, res, next) => data.get(req.ass.resourceId).then((fi // Thumbnail response router.get('/thumbnail', (req, res, next) => data.get(req.ass.resourceId) - .then(({ thumbnail }) => fs.readFile(path(diskFilePath, 'thumbnails/', thumbnail))) + .then(({ is, thumbnail }) => fs.readFile((!is || (is.image || is.video)) ? path(diskFilePath, 'thumbnails/', thumbnail) : 'views/ass-audio-icon.png')) .then((fileData) => res.type('jpg').send(fileData)) .catch(next)); @@ -85,10 +84,10 @@ router.get('/thumbnail', (req, res, next) => // https://old.reddit.com/r/discordapp/comments/82p8i6/a_basic_tutorial_on_how_to_get_the_most_out_of/ router.get('/oembed', (req, res, next) => data.get(req.ass.resourceId) - .then(({ opengraph, mimetype, size, timestamp, originalname }) => + .then(({ opengraph, is, size, timestamp, originalname }) => res.type('json').send({ version: '1.0', - type: mimetype.includes('video') ? 'video' : 'photo', + type: is.video ? 'video' : is.image ? 'photo' : 'link', author_url: opengraph.authorUrl, provider_url: opengraph.providerUrl, author_name: replaceholder(opengraph.author || '', size, timestamp, originalname), @@ -112,7 +111,7 @@ router.get('/delete/:deleteId', (req, res, next) => { if (deleteId !== fileData.deleteId) return res.sendStatus(CODE_UNAUTHORIZED); // Save the file information - return Promise.all([s3enabled ? deleteS3(fileData) : fs.rmSync(path(fileData.path)), fs.rmSync(path(diskFilePath, 'thumbnails/', fileData.thumbnail))]); + return Promise.all([s3enabled ? deleteS3(fileData) : fs.rmSync(path(fileData.path)), (!fileData.is || (fileData.is.image || fileData.is.video)) ? fs.rmSync(path(diskFilePath, 'thumbnails/', fileData.thumbnail)) : () => Promise.resolve()]); }) .then(() => data.del(req.ass.resourceId)) .then(() => (log(`Deleted: ${oldName} (${oldType})`), res.type('text').send('File has been deleted!'))) // skipcq: JS-0090 diff --git a/storage.js b/storage.js index 7886156..b0fb9f0 100644 --- a/storage.js +++ b/storage.js @@ -7,7 +7,7 @@ const multer = require('multer'); const Thumbnail = require('./thumbnails'); const Vibrant = require('./vibrant'); const Hash = require('./hash'); -const { getSafeExt, getDatedDirname, sanitize, generateId } = require('./utils'); +const { getDatedDirname, sanitize, generateId } = require('./utils'); const { s3enabled, s3endpoint, s3bucket, s3accessKey, s3secretKey, saveAsOriginal, maxUploadSize, mediaStrict } = require('./config.json'); const { CODE_UNSUPPORTED_MEDIA_TYPE } = require('./MagicNumbers.json'); @@ -34,10 +34,23 @@ function getLocalFilename(req) { function processUploaded(req, res, next) { // Fixes req.file.mimetype = req.file.detectedMimeType; + req.file.ext = req.file.detectedFileExtension; req.file.originalname = sanitize(req.file.originalName); req.file.randomId = generateId('random', ID_GEN_LENGTH, null, null); req.file.deleteId = generateId('random', ID_GEN_LENGTH, null, null); + // Set up types + req.file.is = { + image: false, + video: false, + audio: false, + other: false + }; + + // Specify correct type + const isType = req.file.mimetype.includes('image') ? 'image' : req.file.mimetype.includes('video') ? 'video' : req.file.mimetype.includes('audio') ? 'audio' : 'other'; + req.file.is[isType] = true; + // Block the resource if the mimetype is not an image or video if (mediaStrict && !ALLOWED_MIMETYPES.test(req.file.mimetype)) { fs.remove(req.file.path.concat('.temp')); @@ -70,7 +83,7 @@ function processUploaded(req, res, next) { // Upload to Amazon S3 ? s3.putObject({ Bucket: s3bucket, - Key: req.file.randomId.concat(getSafeExt(req.file.mimetype)), + Key: req.file.randomId.concat(req.file.ext), ACL: 'public-read', ContentType: req.file.mimetype, Body: fs.createReadStream(req.file.path) @@ -91,7 +104,7 @@ function processUploaded(req, res, next) { function deleteS3(file) { return new Promise((resolve, reject) => s3 - .deleteObject({ Bucket: s3bucket, Key: file.randomId.concat(getSafeExt(file.mimetype)) }) + .deleteObject({ Bucket: s3bucket, Key: file.randomId.concat(file.ext) }) .promise() .then(resolve) .catch(reject)); diff --git a/thumbnails.js b/thumbnails.js index 6c3ecd6..e11f10f 100644 --- a/thumbnails.js +++ b/thumbnails.js @@ -81,6 +81,6 @@ function getImageThumbnail(file) { */ module.exports = (file) => new Promise((resolve, reject) => - (file.mimetype.includes('video') ? getVideoThumbnail : getImageThumbnail)(file) - .then(() => resolve(getNewName(file.originalname))) + (file.is.video ? getVideoThumbnail : file.is.image ? getImageThumbnail : () => Promise.resolve())(file) + .then(() => resolve((file.is.video || file.is.image) ? getNewName(file.originalname) : 'views/ass-audio-icon.png')) .catch(reject)); diff --git a/utils.js b/utils.js index ae5c805..f801488 100755 --- a/utils.js +++ b/utils.js @@ -26,12 +26,8 @@ function getTrueDomain(d = domain) { return d.concat((port === HTTP || port === HTTPS || isProxied) ? '' : `:${port}`); } -function getSafeExt(type) { - return type.includes('video') ? '.mp4' : type.includes('gif') ? '.gif' : ''; -} - -function getS3url(s3key, type) { - return `https://${s3bucket}.${s3endpoint}/${s3key}${getSafeExt(type)}`; +function getS3url(s3key, ext) { + return `https://${s3bucket}.${s3endpoint}/${s3key}${ext}`; } function getDirectUrl(resourceId) { @@ -101,7 +97,6 @@ module.exports = { formatBytes, replaceholder, getDatedDirname, - getSafeExt, randomHexColour, sanitize, log: console.log, @@ -120,7 +115,7 @@ module.exports = { generateId: (mode, length, gfyLength, originalName) => (GENERATORS.has(mode) ? GENERATORS.get(mode)({ length, gfyLength }) : originalName), arrayEquals: (arr1, arr2) => arr1.length === arr2.length && arr1.slice().sort().every((value, index) => value === arr2.slice().sort()[index]), downloadTempS3: (file) => new Promise((resolve, reject) => - fetch(getS3url(file.randomId, file.mimetype)) + fetch(getS3url(file.randomId, file.ext)) .then((f2) => f2.body.pipe(fs.createWriteStream(Path.join(__dirname, diskFilePath, sanitize(file.originalname))).on('close', () => resolve()))) .catch(reject)), } diff --git a/vibrant.js b/vibrant.js index 3165b82..6079786 100644 --- a/vibrant.js +++ b/vibrant.js @@ -25,4 +25,4 @@ function getVibrant(file, resolve, reject) { * @param {*} file The file to get a colour from * @returns The Vibrant colour as a Hex value (or random Hex value for videos) */ -module.exports = (file) => new Promise((resolve, reject) => file.mimetype.includes('video') ? resolve(randomHexColour()) : getVibrant(file, resolve, reject)); // skipcq: JS-0229 +module.exports = (file) => new Promise((resolve, reject) => !file.is.image ? resolve(randomHexColour()) : getVibrant(file, resolve, reject)); // skipcq: JS-0229 diff --git a/views/view.pug b/views/view.pug index 489b9a2..5cc0784 100644 --- a/views/view.pug +++ b/views/view.pug @@ -25,10 +25,12 @@ html #content h4!=title figure - if isVideo + if fileIs.video video#media(controls loop muted playsinline preload='metadata')&attributes(resourceAttr) - else + else if fileIs.image img#media(decoding='async')&attributes(resourceAttr) + else + code!=title figcaption br span Uploaded by #[strong!=uploader]