From c8a5f7be16510baee191acae5d50a7959c384ac0 Mon Sep 17 00:00:00 2001 From: Bobby Wibowo Date: Sun, 27 Sep 2020 04:33:42 +0700 Subject: [PATCH] Added setContentDisposition option Resolves #192 This added 2 new dependencies: content-disposition BobbyWibowo/serve-static content-disposition: This has fallback generation for file names that are outside ISO-8859-1. Plus it was already a sub-dependency due to express to begin with. BobbyWibowo/serve-static: A fork of express/serve-static to allow specifying an async setHeaders function by the name preSetHeaders, that will be awaited before creating send stream to clients. --- config.sample.js | 6 ++++++ lolisafe.js | 36 ++++++++++++++++++++++++++++++++---- package.json | 2 ++ yarn.lock | 11 ++++++++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/config.sample.js b/config.sample.js index 808a609..375404b 100644 --- a/config.sample.js +++ b/config.sample.js @@ -30,6 +30,12 @@ module.exports = { serveFilesWithNode: false, domain: 'https://lolisafe.moe', + /* + If you serve files with node, you can optionally choose to set Content-Disposition header + into their original file names. This allows users to save files into their original file names. + */ + setContentDisposition: false, + /* If you are serving your files with a different domain than your lolisafe homepage, then fill this option with your lolisafe homepage, otherwise any falsy value. diff --git a/lolisafe.js b/lolisafe.js index 5680834..dd8f167 100644 --- a/lolisafe.js +++ b/lolisafe.js @@ -1,11 +1,13 @@ const bodyParser = require('body-parser') const clamd = require('clamdjs') +const contentDisposition = require('content-disposition') const express = require('express') const helmet = require('helmet') const nunjucks = require('nunjucks') const path = require('path') const RateLimit = require('express-rate-limit') const readline = require('readline') +const serveStatic = require('serve-static') const config = require('./config') const logger = require('./logger') const versions = require('./src/versions') @@ -65,6 +67,33 @@ let setHeaders = res => { res.set('Access-Control-Allow-Origin', '*') } +const initServeStaticUploads = (opts = {}) => { + if (config.setContentDisposition) { + opts.preSetHeaders = async (res, path) => { + // Do only if accessing files from uploads' root directory (i.e. not thumbs, etc.) + // and only if they're GET requests + if (path.indexOf('/', 1) === -1 && res.req.method === 'GET') { + const name = path.substring(1) + try { + const file = await db.table('files') + .where('name', name) + .select('original') + .first() + res.set('Content-Disposition', contentDisposition(file.original, { type: 'inline' })) + } catch (error) { + logger.error(error) + } + } + } + // serveStatic is just a modified express/serve-static module that allows specifying + // an async setHeaders function by the name preSetHeaders. + // The module will wait for the said function before creating send stream to client. + safe.use('/', serveStatic(paths.uploads, opts)) + } else { + safe.use('/', express.static(paths.uploads, opts)) + } +} + // Cache control (safe.fiery.me) if (config.cacheControl) { const cacheControls = { @@ -96,7 +125,7 @@ if (config.cacheControl) { // If serving uploads with node if (config.serveFilesWithNode) - safe.use('/', express.static(paths.uploads, { + initServeStaticUploads({ setHeaders: res => { res.set('Access-Control-Allow-Origin', '*') // If using CDN, cache uploads in CDN as well @@ -104,7 +133,7 @@ if (config.cacheControl) { if (config.cacheControl !== 2) res.set('Cache-Control', cacheControls.cdn) } - })) + }) // Function for static assets. // This requires the assets to use version in their query string, @@ -125,8 +154,7 @@ if (config.cacheControl) { next() }) } else if (config.serveFilesWithNode) { - // If serving uploads with node - safe.use('/', express.static(paths.uploads)) + initServeStaticUploads() } // Static assets diff --git a/package.json b/package.json index 297b09b..bb30ceb 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "blake3": "~2.1.4", "body-parser": "~1.19.0", "clamdjs": "~1.0.2", + "content-disposition": "~0.5.3", "express": "~4.17.1", "express-rate-limit": "~5.1.3", "fluent-ffmpeg": "~2.1.2", @@ -48,6 +49,7 @@ "randomstring": "~1.1.5", "readline": "~1.3.0", "search-query-parser": "~1.5.5", + "serve-static": "git+https://git@github.com/BobbyWibowo/serve-static#60049dec396615ab738d29576a65432e4f9d7d4a", "sharp": "~0.26.0", "sqlite3": "~5.0.0", "systeminformation": "~4.27.3" diff --git a/yarn.lock b/yarn.lock index 0ffeb13..7cce582 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1440,7 +1440,7 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= -content-disposition@0.5.3: +content-disposition@0.5.3, content-disposition@~0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== @@ -6788,6 +6788,15 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" +"serve-static@git+https://git@github.com/BobbyWibowo/serve-static#60049dec396615ab738d29576a65432e4f9d7d4a": + version "1.14.2" + resolved "git+https://git@github.com/BobbyWibowo/serve-static#60049dec396615ab738d29576a65432e4f9d7d4a" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"