uploadController.js:

 * Fixed chunk uploads failing when "blockedExtensions" is missing from the config file.

config.sample.js:

* Renamed "blockedExtensions" to "extensionsFilter", and added a new option named "filterBlacklist". When "filterBlacklist" is set to 'true', all extensions in "extensionsFilter" array will be blacklisted, otherwise it will be a whitelist, so only files with those extensions that can be uploaded.

* Renamed "uploads.chunkedUploads.maxSize" to "uploads.chunkedUploads.chunkSize".

* Added "uploads.chunkedUploads.noJsMaxSize" which can be used to change the 'displayed' file size on the No-JS uploader page.

* Some other phrases updates.

_globals.njk:

* Updated static files' version string since there is a small update to home.js.

other files:

* Regular code improvements/tweaks.
This commit is contained in:
Bobby Wibowo 2018-04-25 20:16:34 +07:00
parent 6a25eaac05
commit 5bb960756f
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
8 changed files with 63 additions and 57 deletions

View File

@ -40,8 +40,13 @@ module.exports = {
// Pages to process for the frontend // Pages to process for the frontend
pages: ['home', 'auth', 'dashboard', 'faq'], pages: ['home', 'auth', 'dashboard', 'faq'],
// Add file extensions here which should be blocked /*
blockedExtensions: [ If set to true, all extensions in "extensionsFilter" array will be blacklisted,
otherwise only files with those extensions that can be uploaded.
*/
filterBlacklist: true,
extensionsFilter: [
'.jar', '.jar',
'.exe', '.exe',
'.msi', '.msi',
@ -67,15 +72,19 @@ module.exports = {
/* /*
Chunked uploads. Chunked uploads.
If this is enabled, every files uploaded from the home page will forcibly be chunked If this is enabled, every files uploaded from the homepage uploader will forcibly be chunked
by the size specified in uploads.chunkedUploads.maxSize. People will still be able to by the size specified in "chunkSize". People will still be able to upload bigger files with
upload bigger files through the API as long as they don't surpass the limit specified the API as long as they don't surpass the limit specified in the "maxSize" option above.
in uploads.maxSize though. Total size of the whole chunks will also later be checked against the "maxSize" option.
Total size of the whole chunks will still be checked against uploads.maxSize too. No-JS uploader page will not have chunked uploads support, if you want to change the maximum
file size 'displayed' on it, you can change the value of "noJsMaxSize".
You can also set it to null (or other falsy values) to inherit the value of "maxSize" option.
"chunkSize", and "noJsMaxSize" if set, need to be in MB.
*/ */
chunkedUploads: { chunkedUploads: {
enabled: true, enabled: true,
maxSize: '10MB' chunkSize: '10MB',
noJsMaxSize: null
}, },
/* /*
@ -84,8 +93,8 @@ module.exports = {
their preferred file name length from the dashboard. The allowed range will their preferred file name length from the dashboard. The allowed range will
be set by "min" and "max". Otherwise it will use "default". be set by "min" and "max". Otherwise it will use "default".
Technically it's possible to have "default" outside of the "min" and "max" range, Technically it's possible to have "default" outside of the "min" and "max" range,
but please not. Once a user has changed to a number within the range, the user will but please not. Otherwise, once a user has changed to a value within the range,
no longer be able to use the default value. the user will no longer be able to use the default value.
*/ */
fileLength: { fileLength: {
min: 4, min: 4,
@ -95,10 +104,10 @@ module.exports = {
}, },
/* /*
This option will limit how many times it will try to generate random names This option will limit how many times it will
for uploaded files. If this value is higher than 1, it will help in cases try to generate a new random name when a collision occurrs.
where files with the same name already exists (higher chance with shorter file name length). The shorter the file name length is, the higher the chance for a collision to occur.
*/ */
maxTries: 1, maxTries: 1,
/* /*

View File

@ -61,17 +61,15 @@ const upload = multer({
fileSize: config.uploads.maxSize fileSize: config.uploads.maxSize
}, },
fileFilter (req, file, cb) { fileFilter (req, file, cb) {
// If there are no blocked extensions // If there are extensions that have to be filtered
if (config.blockedExtensions === undefined) { if (config.extensionsFilter && config.extensionsFilter.length) {
return cb(null, true) const extname = path.extname(file.originalname).toLowerCase()
} const match = config.extensionsFilter.some(extension => extname === extension.toLowerCase())
// If the extension is blocked if ((config.filterBlacklist && match) || (!config.filterBlacklist && !match)) {
if (config.blockedExtensions.some(extension => { // eslint-disable-next-line standard/no-callback-literal
return path.extname(file.originalname).toLowerCase() === extension.toLowerCase() return cb(`Sorry, ${extname.substr(1).toUpperCase()} files are not permitted for security reasons.`)
})) { }
// eslint-disable-next-line standard/no-callback-literal
return cb('This file extension is not allowed.')
} }
if (chunkedUploads) { if (chunkedUploads) {
@ -83,13 +81,12 @@ const upload = multer({
} }
const totalFileSize = parseInt(req.body.totalfilesize) const totalFileSize = parseInt(req.body.totalfilesize)
if (!isNaN(totalFileSize) && totalFileSize > maxSizeBytes) { if (totalFileSize > maxSizeBytes) {
// eslint-disable-next-line standard/no-callback-literal // eslint-disable-next-line standard/no-callback-literal
return cb('Chunked upload error. Total file size is larger than maximum file size.') return cb('Chunk error occurred. Total file size is larger than the maximum file size.')
} }
} }
// If the extension is not blocked
return cb(null, true) return cb(null, true)
} }
}).array('files[]') }).array('files[]')
@ -115,7 +112,7 @@ uploadsController.getUniqueRandomName = (length, extension = '', cb) => {
// If it still haven't reached allowed maximum tries, then try again // If it still haven't reached allowed maximum tries, then try again
if (i < maxTries) { return access(i) } if (i < maxTries) { return access(i) }
// eslint-disable-next-line standard/no-callback-literal // eslint-disable-next-line standard/no-callback-literal
return cb('Could not allocate a unique random name. Try again?') return cb('Sorry, we could not allocate a unique random name. Try again?')
}) })
} }
// Get us a unique random name // Get us a unique random name
@ -149,17 +146,18 @@ uploadsController.upload = async (req, res, next) => {
uploadsController.actuallyUpload = async (req, res, user, albumid) => { uploadsController.actuallyUpload = async (req, res, user, albumid) => {
const erred = error => { const erred = error => {
console.log(error) const isError = error instanceof Error
if (isError) { console.log(error) }
res.json({ res.json({
success: false, success: false,
description: error.toString() description: isError ? error.toString() : `Error: ${error}`
}) })
} }
upload(req, res, async error => { upload(req, res, async error => {
if (error) { return erred(error) } if (error) { return erred(error) }
if (req.files.length === 0) { return erred(new Error('No files.')) } if (req.files.length === 0) { return erred('No files.') }
// If chunked uploads is enabled and the uploaded file is a chunk, then just say that it was a success // If chunked uploads is enabled and the uploaded file is a chunk, then just say that it was a success
if (chunkedUploads && req.body.uuid) { return res.json({ success: true }) } if (chunkedUploads && req.body.uuid) { return res.json({ success: true }) }
@ -215,26 +213,28 @@ uploadsController.finishChunks = async (req, res, next) => {
uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => { uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => {
const erred = error => { const erred = error => {
console.log(error) const isError = error instanceof Error
if (isError) { console.log(error) }
res.json({ res.json({
success: false, success: false,
description: error.toString() description: isError ? error.toString() : `Error: ${error}`
}) })
} }
const files = req.body.files const files = req.body.files
if (!files) { return erred(new Error('Missing files array.')) } if (!files) { return erred('Missing files array.') }
let iteration = 0 let iteration = 0
const infoMap = [] const infoMap = []
for (const file of files) { for (const file of files) {
const { uuid, original, count } = file const { uuid, original, count } = file
if (!uuid || !count) { return erred(new Error('Missing UUID and/or chunks count.')) } if (!uuid) { return erred('Missing UUID.') }
if (!count) { return erred('Missing chunks count.') }
const uuidDir = path.join(chunksDir, uuid) const uuidDir = path.join(chunksDir, uuid)
fs.readdir(uuidDir, async (error, chunkNames) => { fs.readdir(uuidDir, async (error, chunkNames) => {
if (error) { return erred(error) } if (error) { return erred(error) }
if (count < chunkNames.length) { return erred(new Error('Chunks count mismatch.')) } if (count < chunkNames.length) { return erred('Chunks count mismatch.') }
const extension = typeof original === 'string' ? path.extname(original) : '' const extension = typeof original === 'string' ? path.extname(original) : ''
const length = uploadsController.getFileNameLength(req) const length = uploadsController.getFileNameLength(req)

View File

@ -157,7 +157,7 @@ upload.prepareDropzone = () => {
autoProcessQueue: true, autoProcessQueue: true,
headers: { token: upload.token }, headers: { token: upload.token },
chunking: upload.chunkedUploads.enabled, chunking: upload.chunkedUploads.enabled,
chunkSize: parseInt(upload.chunkedUploads.maxSize) * 1000000, // 1000000 B = 1 MB, chunkSize: parseInt(upload.chunkedUploads.chunkSize) * 1000000, // 1000000 B = 1 MB,
parallelChunkUploads: false, // when set to true, sometimes it often hangs with hundreds of parallel uploads parallelChunkUploads: false, // when set to true, sometimes it often hangs with hundreds of parallel uploads
chunksUploaded: async (file, done) => { chunksUploaded: async (file, done) => {
file.previewElement.querySelector('.progress').setAttribute('value', 100) file.previewElement.querySelector('.progress').setAttribute('value', 100)

View File

@ -48,7 +48,6 @@ routes.get('/a/:identifier', async (req, res, next) => {
if (config.uploads.generateZips) { enableDownload = true } if (config.uploads.generateZips) { enableDownload = true }
return res.render('album', { return res.render('album', {
layout: false,
title: album.name, title: album.name,
count: files.length, count: files.length,
thumb, thumb,

View File

@ -3,9 +3,8 @@ const routes = require('express').Router()
const uploadController = require('./../controllers/uploadController') const uploadController = require('./../controllers/uploadController')
const renderOptions = { const renderOptions = {
layout: false,
uploadDisabled: false, uploadDisabled: false,
maxFileSize: config.uploads.maxSize maxFileSize: config.uploads.chunkedUploads.noJsMaxSize || config.uploads.maxSize
} }
if (config.private) { if (config.private) {
@ -17,7 +16,7 @@ if (config.private) {
} }
routes.get('/nojs', async (req, res, next) => { routes.get('/nojs', async (req, res, next) => {
return res.render('nojs', renderOptions) return res.render('nojs', { renderOptions })
}) })
routes.post('/nojs', (req, res, next) => { routes.post('/nojs', (req, res, next) => {
@ -25,13 +24,12 @@ routes.post('/nojs', (req, res, next) => {
res.json = (...args) => { res.json = (...args) => {
const result = args[0] const result = args[0]
const _renderOptions = {} const options = { renderOptions }
Object.assign(_renderOptions, renderOptions)
_renderOptions.errorMessage = result.success ? '' : (result.description || 'An unexpected error occurred.') options.errorMessage = result.success ? '' : (result.description || 'An unexpected error occurred.')
_renderOptions.files = result.files || [{}] options.files = result.files || [{}]
return res.render('nojs', _renderOptions) return res.render('nojs', options)
} }
return uploadController.upload(req, res, next) return uploadController.upload(req, res, next)

View File

@ -10,7 +10,7 @@
This will be appended to all CSS and JS files, This will be appended to all CSS and JS files,
and should be changed on every updates to make sure clients load the very latest version of them. and should be changed on every updates to make sure clients load the very latest version of them.
#} #}
{% set v = "8HSPG0eoRX" %} {% set v = "aSRDjST7cs" %}
{# {#
These will be the links in the homepage and the No-JS uploader. These will be the links in the homepage and the No-JS uploader.

View File

@ -24,8 +24,7 @@
<h2 class='subtitle'>What is safe.fiery.me?</h2> <h2 class='subtitle'>What is safe.fiery.me?</h2>
<article class="message"> <article class="message">
<div class="message-body"> <div class="message-body">
safe.fiery.me is merely another clone of lolisafe. We accept your files, photos, documents, anything, and give you back a shareable link for you to send to others.<br> lolisafe itself is an easy to use, open source and completely free file safe.fiery.me is merely another clone of <a href="https://github.com/WeebDev/lolisafe" target="_blank">lolisafe</a>. We accept your files, photos, documents, anything, and give you back a shareable link for you to send to others.
upload service.
</div> </div>
</article> </article>
@ -39,7 +38,8 @@
<h2 class='subtitle'>How can I keep track of my uploads?</h2> <h2 class='subtitle'>How can I keep track of my uploads?</h2>
<article class="message"> <article class="message">
<div class="message-body"> <div class="message-body">
Simply create a user on the site and every upload will be associated with your account, granting you access to your uploaded files through our dashboard.<br> By having an account, you will also be able to set file name length for your new Simply create a user on the site and every upload will be associated with your account, granting you access to your uploaded files through our dashboard.<br>
By having an account, you will also be able to set file name length for your new
uploads! uploads!
</div> </div>
</article> </article>
@ -47,16 +47,16 @@
<h2 class='subtitle'>Do you have any No-JS uploader?</h2> <h2 class='subtitle'>Do you have any No-JS uploader?</h2>
<article class="message"> <article class="message">
<div class="message-body"> <div class="message-body">
Yes, check out <a href="../nojs" target="_blank">this page</a>.<br> Unfortunately you will not be able to associate your uploads to your account, if you have any.<br> Then again, if you want to use the No-JS uploader, then it's very likely Yes, check out <a href="nojs" target="_blank">this page</a>.<br>
that you will not use the Dashboard anyways. Unfortunately you will not be able to associate your uploads to your account, if you have any.<br>
Then again, if you want to use the No-JS uploader, then it's very likely that you will not use the Dashboard anyways.
</div> </div>
</article> </article>
<h2 class='subtitle'>What are albums?</h2> <h2 class='subtitle'>What are albums?</h2>
<article class="message"> <article class="message">
<div class="message-body"> <div class="message-body">
Albums are a simple way of sorting uploads together. Right now you can create albums through the dashboard, then afterwards you can use them through the homepage uploader or with <a href="https://chrome.google.com/webstore/detail/loli-safe-uploader/enkkmplljfjppcdaancckgilmgoiofnj" Albums are a simple way of sorting uploads together. Right now you can create albums through the dashboard, then afterwards you can use them through the homepage uploader or with <a href="https://chrome.google.com/webstore/detail/loli-safe-uploader/enkkmplljfjppcdaancckgilmgoiofnj" target="_blank">our chrome extension</a>, which will enable you to <strong>right click -> send to lolisafe</strong> or to a desired album if you have any. You will probably have to change some things involving <b>https://safe.fiery.me/api/upload</b> if you want to use the extension though.
target="_blank">our chrome extension</a>, which will enable you to <strong>right click -> send to lolisafe</strong> or to a desired album if you have any. You will probably have to change some things involving <b>https://safe.fiery.me/api/upload</b> if you want to use the extension though.
</div> </div>
</article> </article>

View File

@ -17,7 +17,7 @@
<h2 class="subtitle">{{ globals.home_subtitle | safe }}</h2> <h2 class="subtitle">{{ globals.home_subtitle | safe }}</h2>
<h3 class="subtitle" id="maxFileSize"> <h3 class="subtitle" id="maxFileSize">
Maximum upload size per file is {{ maxFileSize }} Maximum upload size per file is {{ renderOptions.maxFileSize }}
</h3> </h3>
<div class="columns is-gapless"> <div class="columns is-gapless">
@ -29,8 +29,8 @@
</p> </p>
</div> </div>
<div class="content"> <div class="content">
{% if uploadDisabled -%} {% if renderOptions.uploadDisabled -%}
<a class="button is-danger" style="display: flex" href="auth">{{ uploadDisabled }}</a> <a class="button is-danger" style="display: flex" href="auth">{{ renderOptions.uploadDisabled }}</a>
{%- else -%} {%- else -%}
<form id="form" action="" method="post" enctype="multipart/form-data"> <form id="form" action="" method="post" enctype="multipart/form-data">
<div class="field"> <div class="field">