Updates (WARNING!)

WARNING: Please turn off lolisafe before upgrading, then run "node database/migration.js" once after upgrading. Ignore all errors/warnings about duplicate column name. Afterwards make sure your config.js follows the new format in config.sample.js (specifically fileLength and generateThumbnails options).

* generateImageThumbnails and generateVideoThumbnails options in config.js is now renamed to an object named generateThumbnails, with image and video as its properties.

* fileLength option is now an object with min, max, default and userChangeable as its properties.

* User may now change their preferred file length (following the previous option, of course).

* Updated a bunch of responses messages. Mainly appending a dot to the messages.

* New APIs:
/fileLength/config to get an object of the current fileLength config (exactly what is in the config.js file).
/fileLength/change to change user's preferred file length.

* And maybe some others ...?
This commit is contained in:
Bobby Wibowo 2018-03-24 20:52:47 +07:00
parent 763d06ff95
commit 616124446f
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
17 changed files with 179 additions and 70 deletions

View File

@ -64,8 +64,21 @@ module.exports = {
*/ */
maxSize: '512MB', maxSize: '512MB',
// The length of the random generated name for the uploaded files /*
fileLength: 32, The length of the random generated name for the uploaded files.
If "userChangeable" is set to true, registered users will be able to change
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.
*/
fileLength: {
min: 4,
max: 32,
default: 32,
userChangeable: false
},
/* /*
This option will limit how many times it will try to generate random names This option will limit how many times it will try to generate random names
@ -79,8 +92,10 @@ module.exports = {
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org) to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
for images and ffmpeg (https://ffmpeg.org/) for video files for images and ffmpeg (https://ffmpeg.org/) for video files
*/ */
generateImageThumbnails: true, generateThumbnails: {
generateVideoThumbnails: false, image: true,
video: false
},
/* /*
Allows users to download a .zip file of all files in an album. Allows users to download a .zip file of all files in an album.

View File

@ -46,7 +46,7 @@ albumsController.create = async (req, res, next) => {
const name = req.body.name const name = req.body.name
if (name === undefined || name === '') { if (name === undefined || name === '') {
return res.json({ success: false, description: 'No album name specified' }) return res.json({ success: false, description: 'No album name specified.' })
} }
const album = await db.table('albums').where({ const album = await db.table('albums').where({
@ -56,7 +56,7 @@ albumsController.create = async (req, res, next) => {
}).first() }).first()
if (album) { if (album) {
return res.json({ success: false, description: 'There\'s already an album with that name' }) return res.json({ success: false, description: 'There\'s already an album with that name.' })
} }
await db.table('albums').insert({ await db.table('albums').insert({
@ -77,7 +77,7 @@ albumsController.delete = async (req, res, next) => {
const id = req.body.id const id = req.body.id
if (id === undefined || id === '') { if (id === undefined || id === '') {
return res.json({ success: false, description: 'No album specified' }) return res.json({ success: false, description: 'No album specified.' })
} }
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 }) await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 })
@ -89,17 +89,17 @@ albumsController.rename = async (req, res, next) => {
const id = req.body.id const id = req.body.id
if (id === undefined || id === '') { if (id === undefined || id === '') {
return res.json({ success: false, description: 'No album specified' }) return res.json({ success: false, description: 'No album specified.' })
} }
const name = req.body.name const name = req.body.name
if (name === undefined || name === '') { if (name === undefined || name === '') {
return res.json({ success: false, description: 'No name specified' }) return res.json({ success: false, description: 'No name specified.' })
} }
const album = await db.table('albums').where({ name: name, userid: user.id }).first() const album = await db.table('albums').where({ name: name, userid: user.id }).first()
if (album) { if (album) {
return res.json({ success: false, description: 'Name already in use' }) return res.json({ success: false, description: 'Name already in use.' })
} }
await db.table('albums').where({ id: id, userid: user.id }).update({ name: name }) await db.table('albums').where({ id: id, userid: user.id }).update({ name: name })
@ -108,10 +108,10 @@ albumsController.rename = async (req, res, next) => {
albumsController.get = async (req, res, next) => { albumsController.get = async (req, res, next) => {
const identifier = req.params.identifier const identifier = req.params.identifier
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' }) if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided.' })
const album = await db.table('albums').where({ identifier, enabled: 1 }).first() const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
if (!album) return res.json({ success: false, description: 'Album not found' }) if (!album) return res.json({ success: false, description: 'Album not found.' })
const title = album.name const title = album.name
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC') const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
@ -120,7 +120,7 @@ albumsController.get = async (req, res, next) => {
file.file = `${config.domain}/${file.name}` file.file = `${config.domain}/${file.name}`
const ext = path.extname(file.name).toLowerCase() const ext = path.extname(file.name).toLowerCase()
if ((config.uploads.generateImageThumbnails && utils.imageExtensions.includes(ext)) || (config.uploads.generateVideoThumbnails && utils.videoExtensions.includes(ext))) { if ((config.uploads.generateThumbnails.image && utils.imageExtensions.includes(ext)) || (config.uploads.generateThumbnails.video && utils.videoExtensions.includes(ext))) {
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png` file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`
} }
} }
@ -135,11 +135,11 @@ albumsController.get = async (req, res, next) => {
albumsController.generateZip = async (req, res, next) => { albumsController.generateZip = async (req, res, next) => {
const identifier = req.params.identifier const identifier = req.params.identifier
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' }) if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided.' })
if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled' }) if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled.' })
const album = await db.table('albums').where({ identifier, enabled: 1 }).first() const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
if (!album) return res.json({ success: false, description: 'Album not found' }) if (!album) return res.json({ success: false, description: 'Album not found.' })
if (album.zipGeneratedAt > album.editedAt) { if (album.zipGeneratedAt > album.editedAt) {
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`) const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
@ -148,7 +148,7 @@ albumsController.generateZip = async (req, res, next) => {
} else { } else {
console.log(`Generating zip for album identifier: ${identifier}`) console.log(`Generating zip for album identifier: ${identifier}`)
const files = await db.table('files').select('name').where('albumid', album.id) const files = await db.table('files').select('name').where('albumid', album.id)
if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album' }) if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album.' })
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`) const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`)
let archive = new Zip() let archive = new Zip()

View File

@ -10,53 +10,53 @@ authController.verify = async (req, res, next) => {
const username = req.body.username const username = req.body.username
const password = req.body.password const password = req.body.password
if (username === undefined) return res.json({ success: false, description: 'No username provided' }) if (username === undefined) return res.json({ success: false, description: 'No username provided.' })
if (password === undefined) return res.json({ success: false, description: 'No password provided' }) if (password === undefined) return res.json({ success: false, description: 'No password provided.' })
const user = await db.table('users').where('username', username).first() const user = await db.table('users').where('username', username).first()
if (!user) return res.json({ success: false, description: 'Username doesn\'t exist' }) if (!user) return res.json({ success: false, description: 'Username doesn\'t exist.' })
if (user.enabled === false || user.enabled === 0) { if (user.enabled === false || user.enabled === 0) {
return res.json({ return res.json({
success: false, success: false,
description: 'This account has been disabled' description: 'This account has been disabled.'
}) })
} }
bcrypt.compare(password, user.password, (err, result) => { bcrypt.compare(password, user.password, (err, result) => {
if (err) { if (err) {
console.log(err) console.log(err)
return res.json({ success: false, description: 'There was an error' }) return res.json({ success: false, description: 'There was an error.' })
} }
if (result === false) return res.json({ success: false, description: 'Wrong password' }) if (result === false) return res.json({ success: false, description: 'Wrong password.' })
return res.json({ success: true, token: user.token }) return res.json({ success: true, token: user.token })
}) })
} }
authController.register = async (req, res, next) => { authController.register = async (req, res, next) => {
if (config.enableUserAccounts === false) { if (config.enableUserAccounts === false) {
return res.json({ success: false, description: 'Register is disabled at the moment' }) return res.json({ success: false, description: 'Register is disabled at the moment.' })
} }
const username = req.body.username const username = req.body.username
const password = req.body.password const password = req.body.password
if (username === undefined) return res.json({ success: false, description: 'No username provided' }) if (username === undefined) return res.json({ success: false, description: 'No username provided.' })
if (password === undefined) return res.json({ success: false, description: 'No password provided' }) if (password === undefined) return res.json({ success: false, description: 'No password provided.' })
if (username.length < 4 || username.length > 32) { if (username.length < 4 || username.length > 32) {
return res.json({ success: false, description: 'Username must have 4-32 characters' }) return res.json({ success: false, description: 'Username must have 4-32 characters.' })
} }
if (password.length < 6 || password.length > 64) { if (password.length < 6 || password.length > 64) {
return res.json({ success: false, description: 'Password must have 6-64 characters' }) return res.json({ success: false, description: 'Password must have 6-64 characters.' })
} }
const user = await db.table('users').where('username', username).first() const user = await db.table('users').where('username', username).first()
if (user) return res.json({ success: false, description: 'Username already exists' }) if (user) return res.json({ success: false, description: 'Username already exists.' })
bcrypt.hash(password, 10, async (err, hash) => { bcrypt.hash(password, 10, async (err, hash) => {
if (err) { if (err) {
console.log(err) console.log(err)
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' }) return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻.' })
} }
const token = randomstring.generate(64) const token = randomstring.generate(64)
await db.table('users').insert({ await db.table('users').insert({
@ -73,16 +73,16 @@ authController.changePassword = async (req, res, next) => {
const user = await utils.authorize(req, res) const user = await utils.authorize(req, res)
let password = req.body.password let password = req.body.password
if (password === undefined) return res.json({ success: false, description: 'No password provided' }) if (password === undefined) return res.json({ success: false, description: 'No password provided.' })
if (password.length < 6 || password.length > 64) { if (password.length < 6 || password.length > 64) {
return res.json({ success: false, description: 'Password must have 6-64 characters' }) return res.json({ success: false, description: 'Password must have 6-64 characters.' })
} }
bcrypt.hash(password, 10, async (err, hash) => { bcrypt.hash(password, 10, async (err, hash) => {
if (err) { if (err) {
console.log(err) console.log(err)
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' }) return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻.' })
} }
await db.table('users').where('id', user.id).update({ password: hash }) await db.table('users').where('id', user.id).update({ password: hash })
@ -90,4 +90,32 @@ authController.changePassword = async (req, res, next) => {
}) })
} }
authController.getFileLengthConfig = async (req, res, next) => {
const user = await utils.authorize(req, res)
return res.json({ success: true, fileLength: user.fileLength, config: config.uploads.fileLength })
}
authController.changeFileLength = async (req, res, next) => {
if (config.uploads.fileLength.userChangeable === false) {
return res.json({ success: false, description: 'Changing file length is disabled at the moment.' })
}
const user = await utils.authorize(req, res)
let fileLength = parseInt(req.body.fileLength)
if (fileLength === undefined) return res.json({ success: false, description: 'No file length provided.' })
if (isNaN(fileLength)) return res.json({ success: false, description: 'File length is not a valid number.' })
if (fileLength < config.uploads.fileLength.min || fileLength > config.uploads.fileLength.max) {
return res.json({ success: false, description: `File length must be ${config.uploads.fileLength.min} to ${config.uploads.fileLength.max} characters` })
}
if (fileLength === user.fileLength) {
return res.json({ success: true })
}
await db.table('users').where('id', user.id).update({ fileLength })
return res.json({ success: true })
}
module.exports = authController module.exports = authController

View File

@ -7,10 +7,10 @@ const tokenController = {}
tokenController.verify = async (req, res, next) => { tokenController.verify = async (req, res, next) => {
const token = req.body.token const token = req.body.token
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' }) if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided.' })
const user = await db.table('users').where('token', token).first() const user = await db.table('users').where('token', token).first()
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' }) if (!user) return res.status(401).json({ success: false, description: 'Invalid token.' })
return res.json({ success: true, username: user.username }) return res.json({ success: true, username: user.username })
} }

View File

@ -18,8 +18,10 @@ const storage = multer.diskStorage({
cb(null, uploadDir) cb(null, uploadDir)
}, },
filename: function (req, file, cb) { filename: function (req, file, cb) {
// If the user has a preferred file length, make sure it follows the allowed range
const fileLength = req.params.fileLength ? Math.min(Math.max(req.params.fileLength, config.uploads.fileLength.min), config.uploads.fileLength.max) : config.uploads.fileLength.default
const access = i => { const access = i => {
const name = randomstring.generate(config.uploads.fileLength) + path.extname(file.originalname) const name = randomstring.generate(fileLength) + path.extname(file.originalname)
fs.access(path.join(uploadDir, name), err => { fs.access(path.join(uploadDir, name), err => {
if (err) return cb(null, name) if (err) return cb(null, name)
console.log(`A file named "${name}" already exists (${++i}/${maxTries}).`) console.log(`A file named "${name}" already exists (${++i}/${maxTries}).`)
@ -36,12 +38,10 @@ const upload = multer({
storage: storage, storage: storage,
limits: { fileSize: config.uploads.maxSize }, limits: { fileSize: config.uploads.maxSize },
fileFilter: function (req, file, cb) { fileFilter: function (req, file, cb) {
if (config.blockedExtensions !== undefined) { if (config.blockedExtensions === undefined) return cb(null, true)
if (config.blockedExtensions.some(extension => path.extname(file.originalname).toLowerCase() === extension)) { if (config.blockedExtensions.some(extension => path.extname(file.originalname).toLowerCase() === extension)) {
// eslint-disable-next-line standard/no-callback-literal // eslint-disable-next-line standard/no-callback-literal
return cb('This file extension is not allowed') return cb('This file extension is not allowed.')
}
return cb(null, true)
} }
return cb(null, true) return cb(null, true)
} }
@ -57,9 +57,12 @@ uploadsController.upload = async (req, res, next) => {
if (user && (user.enabled === false || user.enabled === 0)) { if (user && (user.enabled === false || user.enabled === 0)) {
return res.json({ return res.json({
success: false, success: false,
description: 'This account has been disabled' description: 'This account has been disabled.'
}) })
} }
if (user && user.fileLength) {
req.params.fileLength = user.fileLength
}
const albumid = req.headers.albumid || req.params.albumid const albumid = req.headers.albumid || req.params.albumid
if (albumid && user) { if (albumid && user) {
@ -67,7 +70,7 @@ uploadsController.upload = async (req, res, next) => {
if (!album) { if (!album) {
return res.json({ return res.json({
success: false, success: false,
description: 'Album doesn\'t exist or it doesn\'t belong to the user' description: 'Album doesn\'t exist or it doesn\'t belong to the user.'
}) })
} }
return uploadsController.actuallyUpload(req, res, user, albumid) return uploadsController.actuallyUpload(req, res, user, albumid)
@ -167,14 +170,14 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
for (let file of files) { for (let file of files) {
let ext = path.extname(file.name).toLowerCase() let ext = path.extname(file.name).toLowerCase()
if ((config.uploads.generateImageThumbnails && utils.imageExtensions.includes(ext)) || (config.uploads.generateVideoThumbnails && utils.videoExtensions.includes(ext))) { if ((config.uploads.generateThumbnails.image && utils.imageExtensions.includes(ext)) || (config.uploads.generateThumbnails.video && utils.videoExtensions.includes(ext))) {
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png` file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
utils.generateThumbs(file) utils.generateThumbs(file)
} }
if (file.albumid) { if (file.albumid) {
db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {}) db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {})
.catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album' }) }) .catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album.' }) })
} }
} }
} }
@ -183,7 +186,7 @@ uploadsController.delete = async (req, res) => {
const user = await utils.authorize(req, res) const user = await utils.authorize(req, res)
const id = req.body.id const id = req.body.id
if (id === undefined || id === '') { if (id === undefined || id === '') {
return res.json({ success: false, description: 'No file specified' }) return res.json({ success: false, description: 'No file specified.' })
} }
const file = await db.table('files') const file = await db.table('files')
@ -285,7 +288,7 @@ uploadsController.list = async (req, res) => {
} }
let ext = path.extname(file.name).toLowerCase() let ext = path.extname(file.name).toLowerCase()
if ((config.uploads.generateImageThumbnails && utils.imageExtensions.includes(ext)) || (config.uploads.generateVideoThumbnails && utils.videoExtensions.includes(ext))) { if ((config.uploads.generateThumbnails.image && utils.imageExtensions.includes(ext)) || (config.uploads.generateThumbnails.video && utils.videoExtensions.includes(ext))) {
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png` file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
} }
} }

View File

@ -23,10 +23,10 @@ utilsController.getPrettyDate = function (date) {
utilsController.authorize = async (req, res) => { utilsController.authorize = async (req, res) => {
const token = req.headers.token const token = req.headers.token
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' }) if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided.' })
const user = await db.table('users').where('token', token).first() const user = await db.table('users').where('token', token).first()
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' }) if (!user) return res.status(401).json({ success: false, description: 'Invalid token.' })
return user return user
} }
@ -36,8 +36,8 @@ utilsController.generateThumbs = function (file, basedomain) {
const isImageExt = utilsController.imageExtensions.includes(ext) const isImageExt = utilsController.imageExtensions.includes(ext)
if (!isVideoExt && !isImageExt) return if (!isVideoExt && !isImageExt) return
if (isVideoExt && config.uploads.generateVideoThumbnails !== true) return if (isVideoExt && config.uploads.generateThumbnails.video !== true) return
if (isImageExt && config.uploads.generateImageThumbnails !== true) return if (isImageExt && config.uploads.generateThumbnails.image !== true) return
let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png') let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png')
fs.access(thumbname, err => { fs.access(thumbname, err => {

View File

@ -3,13 +3,10 @@ const db = require('knex')(config.database)
const migration = {} const migration = {}
migration.start = async () => { migration.start = async () => {
await db.schema.table('albums', table => { await db.schema.table('albums', t => t.dateTime('editedAt')).catch(err => console.warn(err.message))
table.dateTime('editedAt') await db.schema.table('albums', t => t.dateTime('zipGeneratedAt')).catch(err => console.warn(err.message))
table.dateTime('zipGeneratedAt') await db.schema.table('users', t => t.dateTime('enabled')).catch(err => console.warn(err.message))
}).catch(() => {}) await db.schema.table('users', t => t.dateTime('fileLength')).catch(err => console.warn(err.message))
await db.schema.table('users', table => {
table.integer('enabled')
}).catch(() => {})
console.log('Migration finished! Now start lolisafe normally') console.log('Migration finished! Now start lolisafe normally')
process.exit(0) process.exit(0)
} }

View File

@ -11,7 +11,7 @@
<!-- Stylesheets and scripts --> <!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="css/style.css?v=wtTfETVFNy"> <link rel="stylesheet" type="text/css" href="css/style.css?v=ugMWEbHDxs">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="js/album.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="js/album.js?v=V2RnA3Mwhh"></script>

View File

@ -12,7 +12,7 @@
<!-- Stylesheets and scripts --> <!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="css/style.css?v=wtTfETVFNy"> <link rel="stylesheet" type="text/css" href="css/style.css?v=ugMWEbHDxs">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="js/auth.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="js/auth.js?v=V2RnA3Mwhh"></script>

View File

@ -12,8 +12,8 @@
<!-- Stylesheets and scripts --> <!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="css/style.css?v=wtTfETVFNy"> <link rel="stylesheet" type="text/css" href="css/style.css?v=ugMWEbHDxs">
<link rel="stylesheet" type="text/css" href="css/dashboard.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="css/dashboard.css?v=ugMWEbHDxs">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="js/dashboard.js?v=29qSkTNt9U"></script> <script type="text/javascript" src="js/dashboard.js?v=29qSkTNt9U"></script>
@ -108,7 +108,10 @@
<p class="menu-label">Administration</p> <p class="menu-label">Administration</p>
<ul class="menu-list"> <ul class="menu-list">
<li> <li>
<a id="itemTokens" onclick="panel.changeToken()">Change your token</a> <a id="itemFileLength" onclick="panel.changeFileLength()">Preferred file length</a>
</li>
<li>
<a id="itemTokens" onclick="panel.changeToken()">Manage your token</a>
</li> </li>
<li> <li>
<a id="itemPassword" onclick="panel.changePassword()">Change your password</a> <a id="itemPassword" onclick="panel.changePassword()">Change your password</a>

View File

@ -11,7 +11,7 @@
<!-- Stylesheets and scripts --> <!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="css/style.css?v=wtTfETVFNy"> <link rel="stylesheet" type="text/css" href="css/style.css?v=ugMWEbHDxs">
<!-- Open Graph tags --> <!-- Open Graph tags -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />

View File

@ -11,7 +11,7 @@
<!-- Stylesheets and scripts --> <!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="css/style.css?v=wtTfETVFNy"> <link rel="stylesheet" type="text/css" href="css/style.css?v=ugMWEbHDxs">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="libs/dropzone/dropzone.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/dropzone/dropzone.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="libs/axios/axios.min.js?v=V2RnA3Mwhh"></script>

View File

@ -207,3 +207,7 @@ section#dashboard div#table div.column .title {
line-height: 200px; line-height: 200px;
font-weight: normal; font-weight: normal;
} }
.help {
color: #7f8c8d;
}

View File

@ -462,6 +462,63 @@ panel.getAlbum = function (item) {
panel.getUploads(item.id) panel.getUploads(item.id)
} }
panel.changeFileLength = function () {
axios.get('api/fileLength/config')
.then(function (response) {
if (response.data.success === false) {
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error occurred', response.data.description, 'error')
}
panel.page.innerHTML = `
<h2 class="subtitle">Preferred file length</h2>
<div class="field">
<label class="label">Your current file length:</label>
<div class="field has-addons">
<div class="control is-expanded">
<input id="fileLength" class="input" type="text" placeholder="Your file length" value="${response.data.fileLength ? Math.min(Math.max(response.data.fileLength, response.data.config.min), response.data.config.max) : response.data.config.default}">
</div>
<div class="control">
<a id="setFileLength" class="button is-primary">Set file length</a>
</div>
</div>
<p class="help">Default file length is <b>${response.data.config.default}</b> characters. ${response.data.config.userChangeable ? `Range allowed for user is <b>${response.data.config.min}</b> to <b>${response.data.config.max}</b> characters.` : 'Changing file length is disabled at the moment.'}</p>
</div>
`
document.getElementById('setFileLength').addEventListener('click', function () {
panel.setFileLength(document.getElementById('fileLength').value)
})
})
.catch(function (error) {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.setFileLength = function (fileLength) {
axios.post('api/fileLength/change', { fileLength })
.then(function (response) {
if (response.data.success === false) {
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error occurred', response.data.description, 'error')
}
swal({
title: 'Woohoo!',
text: 'Your file length was successfully changed.',
icon: 'success'
}).then(() => {
location.reload()
})
})
.catch(function (error) {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.changeToken = function () { panel.changeToken = function () {
axios.get('api/tokens') axios.get('api/tokens')
.then(function (response) { .then(function (response) {
@ -506,7 +563,7 @@ panel.getNewToken = function () {
swal({ swal({
title: 'Woohoo!', title: 'Woohoo!',
text: 'Your token was changed successfully.', text: 'Your token was successfully changed.',
icon: 'success' icon: 'success'
}).then(() => { }).then(() => {
localStorage.token = response.data.token localStorage.token = response.data.token
@ -567,7 +624,7 @@ panel.sendNewPassword = function (pass) {
swal({ swal({
title: 'Woohoo!', title: 'Woohoo!',
text: 'Your password was changed successfully.', text: 'Your password was successfully changed.',
icon: 'success' icon: 'success'
}).then(() => { }).then(() => {
location.reload() location.reload()

View File

@ -19,7 +19,7 @@ routes.get('/a/:identifier', async (req, res, next) => {
file.file = `${basedomain}/${file.name}` file.file = `${basedomain}/${file.name}`
let ext = path.extname(file.name).toLowerCase() let ext = path.extname(file.name).toLowerCase()
if ((config.uploads.generateImageThumbnails && utils.imageExtensions.includes(ext)) || (config.uploads.generateVideoThumbnails && utils.videoExtensions.includes(ext))) { if ((config.uploads.generateThumbnails.image && utils.imageExtensions.includes(ext)) || (config.uploads.generateThumbnails.video && utils.videoExtensions.includes(ext))) {
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png` file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
/* /*

View File

@ -33,5 +33,7 @@ routes.get('/albums/test', (req, res, next) => albumsController.test(req, res, n
routes.get('/tokens', (req, res, next) => tokenController.list(req, res, next)) routes.get('/tokens', (req, res, next) => tokenController.list(req, res, next))
routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next)) routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next))
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next)) routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next))
routes.get('/fileLength/config', (req, res, next) => authController.getFileLengthConfig(req, res, next))
routes.post('/fileLength/change', (req, res, next) => authController.changeFileLength(req, res, next))
module.exports = routes module.exports = routes

View File

@ -11,7 +11,7 @@
<!-- Stylesheets and scripts --> <!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="../libs/bulma/bulma.min.css?v=V2RnA3Mwhh"> <link rel="stylesheet" type="text/css" href="../libs/bulma/bulma.min.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="../css/style.css?v=wtTfETVFNy"> <link rel="stylesheet" type="text/css" href="../css/style.css?v=ugMWEbHDxs">
<script type="text/javascript" src="../libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="../libs/sweetalert/sweetalert.min.js?v=V2RnA3Mwhh"></script>
<script type="text/javascript" src="../libs/axios/axios.min.js?v=V2RnA3Mwhh"></script> <script type="text/javascript" src="../libs/axios/axios.min.js?v=V2RnA3Mwhh"></script>