mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-19 01:31:34 +00:00
Updates
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:
parent
6a25eaac05
commit
5bb960756f
@ -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,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user