* Refactored all instances of "err" into "error".

* Added bulk delete feature (API route: /api/uploads/bulkdelete). It accepts an array of IDs (its key must be "ids" in the JSON POST request). Don't forget it still requires a token in the headers. (https://s.fiery.me/6rjMAYoC.mp4)

* Removed fontello.css from auth.html.

* Updated a bunch of styling.

* Added "copy link to clipboard" button to thumbs view.

* Added "view thumbnail" button to list view. Clicking the row will no longer trigger thumb view, instead you have to press that button.

* Updated icons.

* ... and perhaps some others that I can't remember?
This commit is contained in:
Bobby Wibowo 2018-03-30 06:22:08 +07:00
parent d2086ce4ba
commit 0067c8fe83
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
26 changed files with 455 additions and 207 deletions

View File

@ -161,8 +161,8 @@ albumsController.generateZip = async (req, res, next) => {
try {
// const exists = fs.statSync(path.join(__dirname, '..', config.uploads.folder, file.name))
archive.file(file.name, fs.readFileSync(path.join(__dirname, '..', config.uploads.folder, file.name)))
} catch (err) {
console.log(err)
} catch (error) {
console.log(error)
}
}

View File

@ -22,9 +22,9 @@ authController.verify = async (req, res, next) => {
})
}
bcrypt.compare(password, user.password, (err, result) => {
if (err) {
console.log(err)
bcrypt.compare(password, user.password, (error, result) => {
if (error) {
console.log(error)
return res.json({ success: false, description: 'There was an error.' })
}
if (result === false) { return res.json({ success: false, description: 'Wrong password.' }) }
@ -53,9 +53,9 @@ authController.register = async (req, res, next) => {
const user = await db.table('users').where('username', username).first()
if (user) { return res.json({ success: false, description: 'Username already exists.' }) }
bcrypt.hash(password, 10, async (err, hash) => {
if (err) {
console.log(err)
bcrypt.hash(password, 10, async (error, hash) => {
if (error) {
console.log(error)
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻.' })
}
const token = randomstring.generate(64)
@ -80,9 +80,9 @@ authController.changePassword = async (req, res, next) => {
return res.json({ success: false, description: 'Password must have 6-64 characters.' })
}
bcrypt.hash(password, 10, async (err, hash) => {
if (err) {
console.log(err)
bcrypt.hash(password, 10, async (error, hash) => {
if (error) {
console.log(error)
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻.' })
}

View File

@ -26,15 +26,15 @@ const storage = multer.diskStorage({
// Check for the existence of UUID dir in chunks dir
const uuidDir = path.join(chunksDir, req.body.uuid)
fs.access(uuidDir, err => {
fs.access(uuidDir, error => {
// If it exists, callback
if (!err) { return cb(null, uuidDir) }
if (!error) { return cb(null, uuidDir) }
// It it doesn't, then make it first
fs.mkdir(uuidDir, err => {
fs.mkdir(uuidDir, error => {
// If there was no error, callback
if (!err) { return cb(null, uuidDir) }
if (!error) { return cb(null, uuidDir) }
// Otherwise, log it
console.log(err)
console.log(error)
// eslint-disable-next-line standard/no-callback-literal
return cb('Could not process the chunked upload. Try again?')
})
@ -112,9 +112,9 @@ uploadsController.getFileNameLength = req => {
uploadsController.getUniqueRandomName = (length, extension, cb) => {
const access = i => {
const name = randomstring.generate(length) + extension
fs.access(path.join(uploadDir, name), err => {
fs.access(path.join(uploadDir, name), error => {
// If a file with the same name does not exist
if (err) { return cb(null, name) }
if (error) { return cb(null, name) }
// If a file with the same name already exists, log to console
console.log(`A file named ${name} already exists (${++i}/${maxTries}).`)
// If it still haven't reached allowed maximum tries, then try again
@ -163,16 +163,16 @@ uploadsController.upload = async (req, res, next) => {
}
uploadsController.actuallyUpload = async (req, res, user, albumid) => {
const erred = err => {
console.log(err)
const erred = error => {
console.log(error)
res.json({
success: false,
description: err.toString()
description: error.toString()
})
}
upload(req, res, async err => {
if (err) { return erred(err) }
upload(req, res, async error => {
if (error) { return erred(error) }
if (req.files.length === 0) { return erred(new Error('No files.')) }
@ -238,11 +238,11 @@ uploadsController.finishChunks = async (req, res, next) => {
}
uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => {
const erred = err => {
console.log(err)
const erred = error => {
console.log(error)
res.json({
success: false,
description: err.toString()
description: error.toString()
})
}
@ -257,15 +257,15 @@ uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => {
const chunksDirUuid = path.join(chunksDir, uuid)
fs.readdir(chunksDirUuid, async (err, chunks) => {
if (err) { return erred(err) }
fs.readdir(chunksDirUuid, async (error, chunks) => {
if (error) { return erred(error) }
if (count < chunks.length) { return erred(new Error('Chunks count mismatch.')) }
const extension = path.extname(chunks[0])
const length = uploadsController.getFileNameLength(req)
uploadsController.getUniqueRandomName(length, extension, async (err, name) => {
if (err) { return erred(err) }
uploadsController.getUniqueRandomName(length, extension, async (error, name) => {
if (error) { return erred(error) }
const destination = path.join(uploadDir, name)
const destFileStream = fs.createWriteStream(destination, { flags: 'a' })
@ -274,9 +274,9 @@ uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => {
const appended = await uploadsController.appendToStream(destFileStream, chunksDirUuid, chunks)
.catch(erred)
rimraf(chunksDirUuid, err => {
if (err) {
console.log(err)
rimraf(chunksDirUuid, error => {
if (error) {
console.log(error)
}
})
@ -314,10 +314,10 @@ uploadsController.appendToStream = async (destFileStream, chunksDirUuid, chunks)
.on('end', () => {
append(i + 1)
})
.on('error', err => {
console.log(err)
.on('error', error => {
console.log(error)
destFileStream.end()
return reject(err)
return reject(error)
})
.pipe(destFileStream, { end: false })
} else {
@ -373,7 +373,7 @@ uploadsController.writeFilesToDb = async (req, res, user, albumid, infoMap) => {
timestamp: Math.floor(Date.now() / 1000)
})
} else {
uploadsController.deleteFile(info.data.filename).then(() => {}).catch(err => console.log(err))
uploadsController.deleteFile(info.data.filename).then(() => {}).catch(error => console.log(error))
existingFiles.push(dbFile)
}
@ -452,41 +452,91 @@ uploadsController.delete = async (req, res) => {
.first()
try {
await uploadsController.deleteFile(file.name).catch(err => {
// ENOENT is missing file, for whatever reason, then just delete from db
if (err.code !== 'ENOENT') { throw err }
await uploadsController.deleteFile(file.name).catch(error => {
// ENOENT is missing file, for whatever reason, then just delete from db anyways
if (error.code !== 'ENOENT') { throw error }
})
await db.table('files').where('id', id).del()
if (file.albumid) {
await db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000))
}
} catch (err) {
console.log(err)
} catch (error) {
console.log(error)
}
return res.json({ success: true })
}
uploadsController.bulkDelete = async (req, res) => {
const user = await utils.authorize(req, res)
if (!user) { return }
const ids = req.body.ids
if (ids === undefined || !ids.length) {
return res.json({ success: false, description: 'No files specified.' })
}
const files = await db.table('files')
.whereIn('id', ids)
.where(function () {
if (user.username !== 'root') {
this.where('userid', user.id)
}
})
const failedIds = []
// First, we delete all the physical files
await Promise.all(files.map(file => {
return uploadsController.deleteFile(file.name).catch(error => {
// ENOENT is missing file, for whatever reason, then just delete from db anyways
if (error.code !== 'ENOENT') {
console.log(error)
failedIds.push(file.id)
}
})
}))
// Second, we filter out failed IDs
const successIds = files.filter(file => !failedIds.includes(file.id))
await Promise.all(successIds.map(file => {
return db.table('files').where('id', file.id).del()
.then(() => {
if (file.albumid) {
return db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000))
}
})
.catch(error => {
console.error(error)
failedIds.push(file.id)
})
}))
return res.json({
success: true,
failedIds
})
}
uploadsController.deleteFile = function (file) {
const ext = path.extname(file).toLowerCase()
return new Promise((resolve, reject) => {
fs.stat(path.join(__dirname, '..', config.uploads.folder, file), (err, stats) => {
if (err) { return reject(err) }
fs.unlink(path.join(__dirname, '..', config.uploads.folder, file), err => {
if (err) { return reject(err) }
fs.stat(path.join(__dirname, '..', config.uploads.folder, file), (error, stats) => {
if (error) { return reject(error) }
fs.unlink(path.join(__dirname, '..', config.uploads.folder, file), error => {
if (error) { return reject(error) }
if (!utils.imageExtensions.includes(ext) && !utils.videoExtensions.includes(ext)) {
return resolve()
}
file = file.substr(0, file.lastIndexOf('.')) + '.png'
fs.stat(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), (err, stats) => {
if (err) {
if (err.code !== 'ENOENT') {
console.log(err)
fs.stat(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), (error, stats) => {
if (error) {
if (error.code !== 'ENOENT') {
console.log(error)
}
return resolve()
}
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), err => {
if (err) { return reject(err) }
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), error => {
if (error) { return reject(error) }
return resolve()
})
})

View File

@ -61,8 +61,8 @@ utilsController.generateThumbs = function (file, basedomain) {
if (isImageExt && config.uploads.generateThumbnails.image !== true) { return }
let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png')
fs.access(thumbname, err => {
if (err && err.code === 'ENOENT') {
fs.access(thumbname, error => {
if (error && error.code === 'ENOENT') {
if (isVideoExt) {
ffmpeg(path.join(__dirname, '..', config.uploads.folder, file.name))
.thumbnail({

View File

@ -45,8 +45,8 @@ let init = function (db) {
db.table('users').where({username: 'root'}).then((user) => {
if (user.length > 0) { return }
require('bcrypt').hash('root', 10, function (err, hash) {
if (err) { console.error('Error generating password hash for root') }
require('bcrypt').hash('root', 10, function (error, hash) {
if (error) { console.error('Error generating password hash for root') }
db.table('users').insert({
username: 'root',

View File

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

View File

@ -63,17 +63,17 @@ for (let page of config.pages) {
safe.use((req, res, next) => {
res.status(404).sendFile('HTTP404.html', { root: '../HttpErrorPages/dist/' })
})
safe.use((err, req, res, next) => {
console.error(err)
safe.use((error, req, res, next) => {
console.error(error)
res.status(500).sendFile('HTTP505.html', { root: '../HttpErrorPages/dist/' })
})
safe.listen(config.port, () => console.log(`lolisafe started on port ${config.port}`))
process.on('uncaughtException', err => {
console.error(`Uncaught Exception:\n${err.stack}`)
process.on('uncaughtException', error => {
console.error(`Uncaught Exception:\n${error.stack}`)
})
process.on('unhandledRejection', err => {
console.error(`Unhandled Rejection (Promise):\n${err.stack}`)
process.on('unhandledRejection', error => {
console.error(`Unhandled Rejection (Promise):\n${error.stack}`)
})

View File

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

View File

@ -11,11 +11,10 @@
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=V2RnA3Mwhh">
<link rel="stylesheet" type="text/css" href="css/style.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="css/style.css?v=vZEyc9zyh6">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="js/auth.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="js/auth.js?v=vZEyc9zyh6"></script>
<!-- Open Graph tags -->
<meta property="og:type" content="website" />

View File

@ -11,13 +11,13 @@
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="css/style.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="css/dashboard.css?v=GBG98Tjuxh">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=vZEyc9zyh6">
<link rel="stylesheet" type="text/css" href="css/style.css?v=vZEyc9zyh6">
<link rel="stylesheet" type="text/css" href="css/dashboard.css?v=vZEyc9zyh6">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="libs/clipboard.js/clipboard.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="js/dashboard.js?v=BN63JuN6My"></script>
<script type="text/javascript" src="js/dashboard.js?v=vZEyc9zyh6"></script>
<!-- Open Graph tags -->
<meta property="og:type" content="website" />

View File

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

View File

@ -11,8 +11,8 @@
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="css/style.css?v=8FbubjpYRC">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=vZEyc9zyh6">
<link rel="stylesheet" type="text/css" href="css/style.css?v=vZEyc9zyh6">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="libs/dropzone/dropzone.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="libs/axios/axios.min.js?v=8FbubjpYRC"></script>
@ -110,7 +110,7 @@
<p class="clipboard-mobile is-hidden-desktop" style="display: none">
<a class="button is-info is-outlined clipboard-js" style="display: flex">
<span class="icon">
<i class="icon-attach"></i>
<i class="icon-clipboard"></i>
</span>
<span>Copy link to clipboard</span>
</a>

View File

@ -3,6 +3,32 @@
KDE BREEZE DARK
------------------ */
.button.is-breeze {
background-color: #2980b9;
border-color: transparent;
color: #fff;
}
.button.is-breeze.is-hovered,
.button.is-breeze:hover {
background-color: #3daee9;
border-color: transparent;
color: #fff;
}
.button.is-breeze.is-active,
.button.is-breeze:active {
background-color: #3daee9;
border-color: transparent;
color: #fff;
}
.button.is-breeze.is-focus,
.button.is-breeze:focus {
border-color: transparent;
color: #fff;
}
html {
background-color: #232629;
}
@ -25,15 +51,6 @@ html {
background-color: #2980b9;
}
.button.is-primary {
background-color: #2980b9;
}
.button.is-primary.is-hovered,
.button.is-primary:hover {
background-color: #2980b9;
}
.pagination a {
color: #eff0f1;
border-color: #4d4d4d;
@ -76,6 +93,7 @@ html {
.table th {
color: #eff0f1;
height: 2.25em;
}
.table td,
@ -88,7 +106,7 @@ html {
}
section#dashboard .table {
font-size: 12px
font-size: .75rem;
}
section#dashboard div#table div.column {
@ -113,21 +131,29 @@ section#dashboard div#table div.column.image-container {
position: relative;
}
section#dashboard div#table div.column a.button {
section#dashboard div#table div.column div.controls {
display: flex;
position: absolute;
top: 10px;
right: 10px;
top: .75rem;
right: .75rem;
}
section#dashboard div#table div.column div.controls a.button {
border-radius: 0;
}
section#dashboard div#table div.column div.controls a.button:not(:active):not(:hover) {
background-color: rgba(49, 54, 59, .75);
}
section#dashboard div#table div.column div.name {
position: absolute;
left: 0;
bottom: 0;
right: 0;
left: .75rem;
bottom: .75rem;
right: .75rem;
background-color: rgba(49, 54, 59, .75);
color: #eff0f1;
padding: 10px;
padding: .25rem;
font-size: .75rem;
}
@ -135,20 +161,35 @@ section#dashboard div#table div.column div.name span {
font-weight: 800;
}
section#dashboard div#table div.column a.button:hover,
/*
section#dashboard div#table div.column div.controls a.button:hover,
section#dashboard div#table div.column a.button:active {
background-color: #ff3860;
}
*/
/* Make extra info appear on hover only on non-touch devices */
.no-touch section#dashboard div#table div.column a.button,
.no-touch section#dashboard div#table div.column div.controls,
.no-touch section#dashboard div#table div.column div.name {
opacity: 0;
transition: opacity .25s;
}
.no-touch section#dashboard div#table div.column:hover a.button,
.no-touch section#dashboard div#table div.column:hover div.controls,
.no-touch section#dashboard div#table div.column:hover div.name {
opacity: 1;
}
#page {
/* fix overflow issue with flex */
min-width: 0;
}
.table-container {
overflow-x: auto;
}
.table-container > table {
min-width: 650px;
}

View File

@ -137,10 +137,6 @@ hr {
background-color: #898b8d;
}
.table-container {
overflow-x: auto;
}
#login .input {
border-top: 0;
border-right: 0;
@ -165,15 +161,6 @@ hr {
border-color: #2980b9;
}
.button.is-danger {
background-color: #da4453;
}
.button.is-danger.is-hovered,
.button.is-danger:hover {
background-color: #3daee9;
}
.table.is-hoverable tbody tr:not(.is-selected):hover {
background-color: #4d4d4d;
}

View File

@ -25,8 +25,8 @@ page.do = dest => {
localStorage.token = response.data.token
window.location = 'dashboard'
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -53,8 +53,8 @@ page.verify = () => {
window.location = 'dashboard'
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}

View File

@ -6,7 +6,8 @@ const panel = {
username: undefined,
token: localStorage.token,
filesView: localStorage.filesView,
clipboardJS: undefined
clipboardJS: undefined,
selectedFiles: []
}
panel.preparePage = () => {
@ -45,8 +46,8 @@ panel.verifyToken = (token, reloadOnError) => {
panel.username = response.data.username
return panel.prepareDashboard()
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -128,20 +129,29 @@ panel.getUploads = (album, page, element) => {
<a class="button pagination-next" onclick="panel.getUploads(${album}, ${nextPage}, this)">Next page</a>
</nav>
`
const listType = `
const controls = `
<div class="columns">
<div class="column">
<a class="button is-small is-outlined is-danger" title="List view" onclick="panel.setFilesView('list', ${album}, ${page}, this)">
<span class="icon is-small">
<i class="icon-th-list"></i>
<div class="column"></div>
<div class="column" style="text-align: center">
<a class="button is-small is-danger" title="List view" onclick="panel.setFilesView('list', ${album}, ${page}, this)">
<span class="icon">
<i class="icon-th-list-1"></i>
</span>
</a>
<a class="button is-small is-outlined is-danger" title="Thumbs view" onclick="panel.setFilesView('thumbs', ${album}, ${page}, this)">
<span class="icon is-small">
<i class="icon-th-large"></i>
<a class="button is-small is-danger" title="Thumbs view" onclick="panel.setFilesView('thumbs', ${album}, ${page}, this)">
<span class="icon">
<i class="icon-th-large-1"></i>
</span>
</a>
</div>
<div class="column" style="text-align: right">
<a class="button is-small is-danger" title="Delete selected files" onclick="panel.deleteSelectedFiles()">
<span class="icon">
<i class="icon-trash"></i>
</span>
<span class="is-mobile-hidden">Delete selected files</span>
</a>
</div>
</div>
`
@ -149,7 +159,7 @@ panel.getUploads = (album, page, element) => {
panel.page.innerHTML = `
${pagination}
<hr>
${listType}
${controls}
<div class="columns is-multiline is-mobile is-centered" id="table">
</div>
@ -174,11 +184,18 @@ panel.getUploads = (album, page, element) => {
div.innerHTML = `<a class="image" href="${item.file}" target="_blank"><h1 class="title">.${item.file.split('.').pop()}</h1></a>`
}
div.innerHTML += `
<a class="button is-small is-danger is-outlined" title="Delete album" onclick="panel.deleteFile(${item.id}, ${album}, ${page})">
<span class="icon is-small">
<i class="icon-trash-1"></i>
</span>
</a>
<div class="controls">
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.file}">
<span class="icon">
<i class="icon-clipboard"></i>
</span>
</a>
<a class="button is-small is-danger" title="Delete file" onclick="panel.deleteFile(${item.id}, ${album}, ${page})">
<span class="icon">
<i class="icon-trash"></i>
</span>
</a>
</div>
<div class="name">
<p><span>${item.name}</span></p>
<p>${displayAlbumOrUser ? `<span>${displayAlbumOrUser}</span> ` : ''}${item.size}</div>
@ -192,11 +209,12 @@ panel.getUploads = (album, page, element) => {
panel.page.innerHTML = `
${pagination}
<hr>
${listType}
${controls}
<div class="table-container">
<table class="table is-narrow is-fullwidth is-hoverable">
<thead>
<tr>
<th><input id="selectAll" type="checkbox" title="Select all files" onclick="panel.selectAllFiles(this)"></th>
<th>File</th>
<th>${albumOrUser}</th>
<th>Size</th>
@ -214,7 +232,11 @@ panel.getUploads = (album, page, element) => {
const table = document.getElementById('table')
let allFilesSelected = true
for (const item of response.data.files) {
const selected = panel.selectedFiles.includes(item.id)
if (!selected && allFilesSelected) { allFilesSelected = false }
const tr = document.createElement('tr')
let displayAlbumOrUser = item.album
@ -225,19 +247,25 @@ panel.getUploads = (album, page, element) => {
tr.innerHTML = `
<tr>
<th><input type="checkbox" class="file-checkbox" title="Select this file" onclick="panel.selectFile(${item.id}, this)"${selected ? ' checked' : ''}></th>
<th><a href="${item.file}" target="_blank">${item.file}</a></th>
<th>${displayAlbumOrUser}</th>
<td>${item.size}</td>
<td>${item.date}</td>
<td style="text-align: right">
<a class="button is-small is-info is-outlined clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.file}">
<span class="icon is-small">
<i class="icon-attach"></i>
<a class="button is-small is-primary" title="View thumbnail" onclick="panel.displayThumbnailModal('${item.thumb}')">
<span class="icon">
<i class="icon-picture-1"></i>
</span>
</a>
<a class="button is-small is-danger is-outlined" title="Delete album" onclick="panel.deleteFile(${item.id}, ${album}, ${page})">
<span class="icon is-small">
<i class="icon-trash-1"></i>
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.file}">
<span class="icon">
<i class="icon-clipboard"></i>
</span>
</a>
<a class="button is-small is-danger" title="Delete file" onclick="panel.deleteFile(${item.id}, ${album}, ${page})">
<span class="icon">
<i class="icon-trash"></i>
</span>
</a>
</td>
@ -245,22 +273,16 @@ panel.getUploads = (album, page, element) => {
`
table.appendChild(tr)
}
if (item.thumb) {
tr.addEventListener('click', function (event) {
if (event.target.tagName.toLowerCase() === 'a') { return }
if (event.target.className.includes('icon')) { return }
document.getElementById('modalImage').src = item.thumb
document.getElementById('modal').className += ' is-active'
})
}
if (allFilesSelected && response.data.files.length) {
document.getElementById('selectAll').checked = true
}
}
}).catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
.catch(err => {
console.log(err)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.setFilesView = (view, album, page, element) => {
@ -269,6 +291,32 @@ panel.setFilesView = (view, album, page, element) => {
panel.getUploads(album, page, element)
}
panel.displayThumbnailModal = thumb => {
document.getElementById('modalImage').src = thumb
document.getElementById('modal').className += ' is-active'
}
panel.selectAllFiles = element => {
const table = document.getElementById('table')
const checkboxes = table.getElementsByClassName('file-checkbox')
for (const checkbox of checkboxes) {
if (checkbox.checked !== element.checked) {
checkbox.click()
}
}
element.title = element.checked ? 'Unselect all files' : 'Select all files'
}
panel.selectFile = (id, element) => {
if (!panel.selectedFiles.includes(id) && element.checked) {
panel.selectedFiles.push(id)
localStorage.selectedFiles = JSON.stringify(panel.selectedFiles)
} else if (panel.selectedFiles.includes(id) && !element.checked) {
panel.selectedFiles.splice(panel.selectedFiles.indexOf(id), 1)
localStorage.selectedFiles = JSON.stringify(panel.selectedFiles)
}
}
panel.deleteFile = (id, album, page) => {
swal({
title: 'Are you sure?',
@ -299,8 +347,61 @@ panel.deleteFile = (id, album, page) => {
swal('Deleted!', 'The file has been deleted.', 'success')
panel.getUploads(album, page)
})
.catch(err => {
console.log(err)
.catch(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.deleteSelectedFiles = () => {
const count = panel.selectedFiles.length
if (!count) {
return swal('An error occurred', 'You have not selected any files.', 'error')
}
const suffix = `file${count === 1 ? '' : 's'}`
swal({
title: 'Are you sure?',
text: `You won't be able to recover ${count} ${suffix}!`,
icon: 'warning',
dangerMode: true,
buttons: {
cancel: true,
confirm: {
text: `Yes, nuke the ${suffix}!`,
closeModal: false
}
}
}).then(value => {
if (!value) { return }
axios.post('api/upload/bulkdelete', {
ids: panel.selectedFiles
})
.then(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')
}
}
let deletedCount = count
if (response.data.failedIds && response.data.failedIds.length) {
deletedCount -= response.data.failedIds.length
panel.selectedFiles = panel.selectedFiles.filter(id => response.data.failedIds.includes(id))
} else {
panel.selectedFiles = []
}
localStorage.selectedFiles = JSON.stringify(panel.selectedFiles)
swal('Deleted!', `${deletedCount} file${deletedCount === 1 ? ' has' : 's have'} been deleted.`, 'success')
panel.getUploads()
})
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
})
@ -324,14 +425,19 @@ panel.getAlbums = () => {
<input id="albumName" class="input" type="text" placeholder="Name">
</div>
<div class="control">
<a id="submitAlbum" class="button is-primary">Submit</a>
<a id="submitAlbum" class="button is-breeze">
<span class="icon">
<i class="icon-paper-plane-empty"></i>
</span>
<span>Submit</span>
</a>
</div>
</div>
<h2 class="subtitle">List of albums</h2>
<div class="table-container">
<table class="table is-narrow is-fullwidth is-hoverable">
<table class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Name</th>
@ -358,19 +464,19 @@ panel.getAlbums = () => {
<td>${item.date}</td>
<td><a href="${item.identifier}" target="_blank">${item.identifier}</a></td>
<td style="text-align: right">
<a class="button is-small is-primary is-outlined" title="Edit name" onclick="panel.renameAlbum(${item.id})">
<a class="button is-small is-primary" title="Edit name" onclick="panel.renameAlbum(${item.id})">
<span class="icon is-small">
<i class="icon-edit"></i>
<i class="icon-pencil-1"></i>
</span>
</a>
<a class="button is-small is-info is-outlined clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.identifier}">
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.identifier}">
<span class="icon is-small">
<i class="icon-attach"></i>
<i class="icon-clipboard"></i>
</span>
</a>
<a class="button is-small is-danger is-outlined" title="Delete album" onclick="panel.deleteAlbum(${item.id})">
<a class="button is-small is-danger" title="Delete album" onclick="panel.deleteAlbum(${item.id})">
<span class="icon is-small">
<i class="icon-trash-1"></i>
<i class="icon-trash"></i>
</span>
</a>
</td>
@ -384,8 +490,8 @@ panel.getAlbums = () => {
panel.submitAlbum(this)
})
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -423,8 +529,8 @@ panel.renameAlbum = id => {
panel.getAlbumsSidebar()
panel.getAlbums()
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
})
@ -461,8 +567,8 @@ panel.deleteAlbum = id => {
panel.getAlbumsSidebar()
panel.getAlbums()
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
})
@ -487,8 +593,8 @@ panel.submitAlbum = element => {
panel.getAlbumsSidebar()
panel.getAlbums()
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
panel.setLoading(element, false)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
@ -524,8 +630,8 @@ panel.getAlbumsSidebar = () => {
albumsContainer.appendChild(li)
}
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -556,7 +662,12 @@ panel.changeFileLength = () => {
<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 name length</a>
<a id="setFileLength" class="button is-breeze">
<span class="icon">
<i class="icon-paper-plane-empty"></i>
</span>
<span>Set file name length</span>
</a>
</div>
</div>
<p class="help">Default file name 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 name length is disabled at the moment.'}</p>
@ -567,8 +678,8 @@ panel.changeFileLength = () => {
panel.setFileLength(document.getElementById('fileLength').value, this)
})
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -594,8 +705,8 @@ panel.setFileLength = (fileLength, element) => {
location.reload()
})
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
panel.isLoading(element, false)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
@ -622,7 +733,12 @@ panel.changeToken = () => {
<input id="token" readonly class="input" type="text" placeholder="Your token" value="${response.data.token}">
</div>
<div class="control">
<a id="getNewToken" class="button is-primary">Request new token</a>
<a id="getNewToken" class="button is-breeze">
<span class="icon">
<i class="icon-arrows-cw"></i>
</span>
<span>Request new token</span>
</a>
</div>
</div>
</div>
@ -632,8 +748,8 @@ panel.changeToken = () => {
panel.getNewToken(this)
})
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -660,8 +776,8 @@ panel.getNewToken = element => {
location.reload()
})
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
panel.isLoading(element, false)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
@ -684,7 +800,12 @@ panel.changePassword = () => {
<input id="passwordConfirm" class="input is-expanded" type="password" placeholder="Verify your new password">
</div>
<div class="control">
<a id="sendChangePassword" class="button is-primary">Set new password</a>
<a id="sendChangePassword" class="button is-breeze">
<span class="icon">
<i class="icon-paper-plane-empty"></i>
</span>
<span>Set new password</span>
</a>
</div>
</div>
</div>
@ -726,8 +847,8 @@ panel.sendNewPassword = (pass, element) => {
location.reload()
})
})
.catch(err => {
console.log(err)
.catch(error => {
console.log(error)
panel.isLoading(element, false)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
})
@ -749,6 +870,12 @@ window.onload = () => {
document.documentElement.className += ' no-touch'
}
const selectedFiles = localStorage.selectedFiles
console.log(selectedFiles)
if (selectedFiles) {
panel.selectedFiles = JSON.parse(selectedFiles)
}
panel.preparePage()
panel.clipboardJS = new ClipboardJS('.clipboard-js')

View File

@ -1,6 +1,15 @@
Font license info
## Elusive
Copyright (C) 2013 by Aristeides Stathopoulos
Author: Aristeides Stathopoulos
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://aristeides.com/
## Typicons
(c) Stephen Hutchings 2012
@ -10,3 +19,12 @@ Font license info
Homepage: http://typicons.com/
## Font Awesome
Copyright (C) 2016 by Dave Gandy
Author: Dave Gandy
License: SIL ()
Homepage: http://fortawesome.github.com/Font-Awesome/

View File

@ -1,11 +1,11 @@
@font-face {
font-family: 'fontello';
src: url('fontello.eot?31646562');
src: url('fontello.eot?31646562#iefix') format('embedded-opentype'),
url('fontello.woff2?31646562') format('woff2'),
url('fontello.woff?31646562') format('woff'),
url('fontello.ttf?31646562') format('truetype'),
url('fontello.svg?31646562#fontello') format('svg');
src: url('fontello.eot?10332206');
src: url('fontello.eot?10332206#iefix') format('embedded-opentype'),
url('fontello.woff2?10332206') format('woff2'),
url('fontello.woff?10332206') format('woff'),
url('fontello.ttf?10332206') format('truetype'),
url('fontello.svg?10332206#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -28,20 +28,35 @@
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
/* line-height: 1em; */
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-edit:before { content: '\e800'; } /* '' */
.icon-th-list-1:before { content: '\e800'; } /* '' */
.icon-upload-cloud:before { content: '\e801'; } /* '' */
.icon-trash-1:before { content: '\e802'; } /* '' */
.icon-th-list:before { content: '\e803'; } /* '' */
.icon-attach:before { content: '\e804'; } /* '' */
.icon-th-large:before { content: '\e805'; } /* '' */
.icon-picture-1:before { content: '\e802'; } /* '' */
.icon-th-large-1:before { content: '\e803'; } /* '' */
.icon-trash:before { content: '\e804'; } /* '' */
.icon-pencil-1:before { content: '\e805'; } /* '' */
.icon-clipboard:before { content: '\e806'; } /* '' */
.icon-arrows-cw:before { content: '\e807'; } /* '' */
.icon-paper-plane-empty:before { content: '\f1d9'; } /* '' */

Binary file not shown.

View File

@ -6,17 +6,23 @@
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="edit" unicode="&#xe800;" d="M966 671q24-23 24-55t-24-55l-185-185 0-469q0-21-15-36t-37-16l-676 0q-22 0-37 16t-16 36l0 677q0 21 16 37t37 15l468 0 185 185q24 24 55 24t55-24z m-523-486l327 328-112 112-328-327z m-134 69l4-86 88-4z m369-295l0 312-167-160q-20-20-63-33t-80-14l-159 0 0 159q0 41 11 82t31 61l165 167-312 0 0-574 574 0z m130 590l67 67-114 114-67-67z" horiz-adv-x="990" />
<glyph glyph-name="th-list-1" unicode="&#xe800;" d="M0-150l0 250 250 0 0-250-250 0z m0 375l0 250 250 0 0-250-250 0z m0 375l0 250 250 0 0-250-250 0z m391-750l0 250 609 0 0-250-609 0z m0 375l0 250 609 0 0-250-609 0z m0 375l0 250 609 0 0-250-609 0z" horiz-adv-x="1000" />
<glyph glyph-name="upload-cloud" unicode="&#xe801;" d="M781 506q108 0 184-76t76-184-76-184-184-77l-208 0 0 239 67-66q16-17 38-17 20 0 36 17 15 15 15 36t-15 36l-156 156q-14 14-37 15t-37-15l-156-156q-15-15-15-36t15-36q16-17 37-17 20 0 36 17l68 66 0-239-260 0q-86 0-148 61t-61 148q0 72 44 128t112 73l0 8q0 130 92 221t221 91q100 0 180-58t114-152q2 0 8 1t10 0z" horiz-adv-x="1041" />
<glyph glyph-name="trash-1" unicode="&#xe802;" d="M729 636q21 0 37-15t15-36-15-37-37-15l0-417q0-86-61-147t-147-62l-261 0q-86 0-147 62t-60 147l0 417q-22 0-37 15t-16 37 16 36 37 15l51 0 0 53q0 42 30 73t75 31l364 0q43 0 74-31t31-73l0-53 51 0z m-520 53l0-53 364 0 0 53-364 0z m416-573l0 417-469 0 0-417q0-43 31-74t73-31l261 0q43 0 73 31t31 74z m-390 338q10 0 18-8t7-17l0-313q0-10-7-18t-18-8-18 8-8 18l0 313q0 10 8 17t18 8z m104 0q10 0 18-8t8-17l0-313q0-10-8-18t-18-8-18 8-8 18l0 313q0 10 8 17t18 8z m104 0q10 0 18-8t8-17l0-313q0-10-8-18t-18-8-19 8-8 18l0 313q0 10 8 17t19 8z m105 0q10 0 17-8t8-17l0-313q0-10-8-18t-17-8-19 8-8 18l0 313q0 10 8 17t19 8z" horiz-adv-x="781" />
<glyph glyph-name="picture-1" unicode="&#xe802;" d="M0-68l0 836 1000 0 0-836-1000 0z m76 78l848 0 0 680-848 0 0-680z m90 80l0 59 150 195 102-86 193 291 223-228 0-231-668 0z m0 416q0 37 24 62t62 24q33 0 58-24t24-62q0-33-24-57t-58-25q-37 0-62 25t-24 57z" horiz-adv-x="1000" />
<glyph glyph-name="th-list" unicode="&#xe803;" d="M860 90q43 0 73-31t31-74q0-43-31-73t-73-31l-365 0q-44 0-74 31t-31 73 31 74 74 31l365 0z m0 364q43 0 73-31t31-73-31-73-73-31l-365 0q-44 0-74 31t-31 73 31 73 74 31l365 0z m0 365q43 0 73-31t31-73q0-44-31-74t-73-31l-365 0q-42 0-74 31t-31 74 31 73 74 31l365 0z m-860-834q0 130 130 130t130-130-130-130-130 130z m0 365q0 130 130 130t130-130-130-130-130 130z m0 365q0 130 130 130t130-130-130-130-130 130z" horiz-adv-x="964" />
<glyph glyph-name="th-large-1" unicode="&#xe803;" d="M0-150l0 438 438 0 0-438-438 0z m0 563l0 437 438 0 0-437-438 0z m563-563l0 438 437 0 0-438-437 0z m0 563l0 437 437 0 0-437-437 0z" horiz-adv-x="1000" />
<glyph glyph-name="attach" unicode="&#xe804;" d="M285 223q-52 0-91 38t-39 92q0 55 38 93l236 236q61 61 147 61t148-61 61-148-61-147l-369-369q-61-61-146-61-86 0-148 61t-61 148 61 147l42 41q0-61 37-110l-5-5q-30-30-30-73t30-74 74-30 74 30q61 61 183 184t184 185q30 30 30 73t-30 74q-31 30-74 30t-73-30l-237-236q-7-8-7-19t7-19 19-7 19 7l184 184q31-31 31-74 0-41-31-72l-110-111q-38-38-93-38z" horiz-adv-x="785" />
<glyph glyph-name="trash" unicode="&#xe804;" d="M0 633l0 141 289 0 0 76 246 0 0-76 289 0 0-141-824 0z m43-783l0 676 738 0 0-676-738 0z" horiz-adv-x="824" />
<glyph glyph-name="th-large" unicode="&#xe805;" d="M0 663q0 65 46 110t110 46l104 0q65 0 111-46t45-110l0-104q0-65-45-111t-111-45l-104 0q-65 0-110 45t-46 111l0 104z m521 0q0 65 46 110t111 46l103 0q65 0 111-46t46-110l0-104q0-65-46-111t-111-45l-103 0q-65 0-111 45t-46 111l0 104z m-521-521q0 65 46 110t110 46l104 0q65 0 111-46t45-110l0-104q0-65-45-111t-111-46l-104 0q-65 0-110 46t-46 111l0 104z m521 0q0 65 46 110t111 46l103 0q65 0 111-46t46-110l0-104q0-65-46-111t-111-46l-103 0q-65 0-111 46t-46 111l0 104z" horiz-adv-x="938" />
<glyph glyph-name="pencil-1" unicode="&#xe805;" d="M0-143l68 343 274-273z m137 392l422 422 259-260-421-422z m531 494q2 39 31 69t69 31 66-25l131-131q25-26 24-66t-30-69-69-30-66 24l-131 131q-27 27-25 66z" horiz-adv-x="989" />
<glyph glyph-name="clipboard" unicode="&#xe806;" d="M0-150l0 904 225 0 0-64-161 0 0-774 579 0 0 774-161 0 0 64 225 0 0-904-707 0z m129 129l0 31 31 0 0-31-31 0z m0 121l0 31 31 0 0-31-31 0z m0 121l0 31 31 0 0-31-31 0z m0 121l0 32 31 0 0-32-31 0z m0 121l0 32 31 0 0-32-31 0z m0 96l0 94 129 0 0 97q0 41 27 71t69 29 69-30 28-70q0-56-2-97l129 0 0-94-449 0z m96-582l0 33 353 0 0-33-353 0z m0 121l0 33 353 0 0-33-353 0z m0 121l0 33 353 0 0-33-353 0z m0 121l0 34 353 0 0-34-353 0z m0 121l0 34 353 0 0-34-353 0z m97 260q0-14 9-22t23-9 22 9 9 22-9 24-22 9-23-9-9-24z" horiz-adv-x="707" />
<glyph glyph-name="arrows-cw" unicode="&#xe807;" d="M0-150l0 402 402 0-160-160q108-107 258-107 125 0 222 75t130 192l138 0q-35-173-173-288t-317-114q-207 0-353 146z m10 598q35 174 173 288t317 114q207 0 354-146l146 146 0-402-402 0 160 160q-108 107-258 107-125 0-222-75t-130-192l-138 0z" horiz-adv-x="1000" />
<glyph glyph-name="paper-plane-empty" unicode="&#xf1d9;" d="M984 844q19-13 15-36l-142-857q-3-16-18-25-8-5-18-5-6 0-13 3l-294 120-166-182q-10-12-27-12-7 0-12 2-11 4-17 13t-6 21v252l-264 108q-20 8-22 30-2 22 18 33l928 536q20 12 38-1z m-190-837l123 739-800-462 187-76 482 356-267-444z" horiz-adv-x="1000" />
</font>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,10 +6,14 @@ const utils = require('../controllers/utilsController.js')
routes.get('/a/:identifier', async (req, res, next) => {
let 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()
if (!album) return res.status(404).sendFile('404.html', { root: './pages/error/' })
if (!album) {
return res.status(404).sendFile('404.html', { root: './pages/error/' })
}
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
let thumb = ''
@ -39,7 +43,7 @@ routes.get('/a/:identifier', async (req, res, next) => {
}
let enableDownload = false
if (config.uploads.generateZips) enableDownload = true
if (config.uploads.generateZips) { enableDownload = true }
return res.render('album', {
layout: false,

View File

@ -20,6 +20,7 @@ routes.get('/uploads', (req, res, next) => uploadController.list(req, res, next)
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next))
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next))
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next))
routes.post('/upload/bulkdelete', (req, res, next) => uploadController.bulkDelete(req, res, next))
routes.post('/upload/finishchunks', (req, res, next) => uploadController.finishChunks(req, res, next))
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next))
routes.get('/album/get/:identifier', (req, res, next) => albumsController.get(req, res, next))

View File

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