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: ['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',
'.exe',
'.msi',
@ -67,15 +72,19 @@ module.exports = {
/*
Chunked uploads.
If this is enabled, every files uploaded from the home page will forcibly be chunked
by the size specified in uploads.chunkedUploads.maxSize. People will still be able to
upload bigger files through the API as long as they don't surpass the limit specified
in uploads.maxSize though.
Total size of the whole chunks will still be checked against uploads.maxSize too.
If this is enabled, every files uploaded from the homepage uploader will forcibly be chunked
by the size specified in "chunkSize". People will still be able to upload bigger files with
the API as long as they don't surpass the limit specified in the "maxSize" option above.
Total size of the whole chunks will also later be checked against the "maxSize" option.
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: {
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
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,
but please not. Once a user has changed to a number within the range, the user will
no longer be able to use the default value.
but please not. Otherwise, once a user has changed to a value within the range,
the user will no longer be able to use the default value.
*/
fileLength: {
min: 4,
@ -95,10 +104,10 @@ module.exports = {
},
/*
This option will limit how many times it will try to generate random names
for uploaded files. If this value is higher than 1, it will help in cases
where files with the same name already exists (higher chance with shorter file name length).
*/
This option will limit how many times it will
try to generate a new random name when a collision occurrs.
The shorter the file name length is, the higher the chance for a collision to occur.
*/
maxTries: 1,
/*

View File

@ -61,17 +61,15 @@ const upload = multer({
fileSize: config.uploads.maxSize
},
fileFilter (req, file, cb) {
// If there are no blocked extensions
if (config.blockedExtensions === undefined) {
return cb(null, true)
}
// If there are extensions that have to be filtered
if (config.extensionsFilter && config.extensionsFilter.length) {
const extname = path.extname(file.originalname).toLowerCase()
const match = config.extensionsFilter.some(extension => extname === extension.toLowerCase())
// If the extension is blocked
if (config.blockedExtensions.some(extension => {
return path.extname(file.originalname).toLowerCase() === extension.toLowerCase()
})) {
// eslint-disable-next-line standard/no-callback-literal
return cb('This file extension is not allowed.')
if ((config.filterBlacklist && match) || (!config.filterBlacklist && !match)) {
// eslint-disable-next-line standard/no-callback-literal
return cb(`Sorry, ${extname.substr(1).toUpperCase()} files are not permitted for security reasons.`)
}
}
if (chunkedUploads) {
@ -83,13 +81,12 @@ const upload = multer({
}
const totalFileSize = parseInt(req.body.totalfilesize)
if (!isNaN(totalFileSize) && totalFileSize > maxSizeBytes) {
if (totalFileSize > maxSizeBytes) {
// 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)
}
}).array('files[]')
@ -115,7 +112,7 @@ uploadsController.getUniqueRandomName = (length, extension = '', cb) => {
// If it still haven't reached allowed maximum tries, then try again
if (i < maxTries) { return access(i) }
// 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
@ -149,17 +146,18 @@ uploadsController.upload = async (req, res, next) => {
uploadsController.actuallyUpload = async (req, res, user, albumid) => {
const erred = error => {
console.log(error)
const isError = error instanceof Error
if (isError) { console.log(error) }
res.json({
success: false,
description: error.toString()
description: isError ? error.toString() : `Error: ${error}`
})
}
upload(req, res, async 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 (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) => {
const erred = error => {
console.log(error)
const isError = error instanceof Error
if (isError) { console.log(error) }
res.json({
success: false,
description: error.toString()
description: isError ? error.toString() : `Error: ${error}`
})
}
const files = req.body.files
if (!files) { return erred(new Error('Missing files array.')) }
if (!files) { return erred('Missing files array.') }
let iteration = 0
const infoMap = []
for (const file of files) {
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)
fs.readdir(uuidDir, async (error, chunkNames) => {
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 length = uploadsController.getFileNameLength(req)

View File

@ -157,7 +157,7 @@ upload.prepareDropzone = () => {
autoProcessQueue: true,
headers: { token: upload.token },
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
chunksUploaded: async (file, done) => {
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 }
return res.render('album', {
layout: false,
title: album.name,
count: files.length,
thumb,

View File

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

View File

@ -10,7 +10,7 @@
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.
#}
{% set v = "8HSPG0eoRX" %}
{% set v = "aSRDjST7cs" %}
{#
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>
<article class="message">
<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
upload service.
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.
</div>
</article>
@ -39,7 +38,8 @@
<h2 class='subtitle'>How can I keep track of my uploads?</h2>
<article class="message">
<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!
</div>
</article>
@ -47,16 +47,16 @@
<h2 class='subtitle'>Do you have any No-JS uploader?</h2>
<article class="message">
<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
that you will not use the Dashboard anyways.
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 that you will not use the Dashboard anyways.
</div>
</article>
<h2 class='subtitle'>What are albums?</h2>
<article class="message">
<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"
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.
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.
</div>
</article>

View File

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