* Refactored all instances of "An error occurred" by appending an exclamation mark.

* Added the ability to add/remove files to/from album (API route: /api/albums/addfiles - https://s.fiery.me/dCAqLEQ9.mp4).

* Added the ability to purge files associated with an album when deleting the said album (set "purge" key to true in the JSON POST request to /api/albums/delete).

* Updated icons.

* Some other refactors, probably.
This commit is contained in:
Bobby Wibowo 2018-03-30 09:39:53 +07:00
parent ac242fd9a9
commit 8724d45ce0
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
17 changed files with 466 additions and 182 deletions

View File

@ -18,15 +18,20 @@ albumsController.list = async (req, res, next) => {
fields.push('identifier')
}
const albums = await db.table('albums').select(fields).where({ enabled: 1, userid: user.id })
const albums = await db.table('albums')
.select(fields)
.where({
enabled: 1,
userid: user.id
})
if (req.params.sidebar !== undefined) {
return res.json({ success: true, albums })
}
let ids = []
for (let album of albums) {
album.date = new Date(album.timestamp * 1000)
album.date = utils.getPrettyDate(album.date)
album.date = utils.getPrettyDate(new Date(album.timestamp * 1000))
album.identifier = `${albumDomain}/a/${album.identifier}`
ids.push(album.id)
@ -51,18 +56,20 @@ albumsController.create = async (req, res, next) => {
return res.json({ success: false, description: 'No album name specified.' })
}
const album = await db.table('albums').where({
name: name,
enabled: 1,
userid: user.id
}).first()
const album = await db.table('albums')
.where({
name,
enabled: 1,
userid: user.id
})
.first()
if (album) {
return res.json({ success: false, description: 'There\'s already an album with that name.' })
}
await db.table('albums').insert({
name: name,
name,
enabled: 1,
userid: user.id,
identifier: randomstring.generate(8),
@ -79,12 +86,42 @@ albumsController.delete = async (req, res, next) => {
if (!user) { return }
const id = req.body.id
const purge = req.body.purge
if (id === undefined || id === '') {
return res.json({ success: false, description: 'No album specified.' })
}
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 })
return res.json({ success: true })
let ids = []
let failedIds = []
if (purge) {
const files = await db.table('files')
.where({
albumid: id,
userid: user.id
})
ids = files.map(file => file.id)
failedIds = await utils.bulkDeleteFilesByIds(ids, user)
if (failedIds.length === ids.length) {
return res.json({
success: false,
description: 'Could not delete any of the files associated with the album.'
})
}
}
await db.table('albums')
.where({
id,
userid: user.id
})
.update({ enabled: 0 })
return res.json({
success: true,
failedIds
})
}
albumsController.rename = async (req, res, next) => {
@ -101,12 +138,26 @@ albumsController.rename = async (req, res, next) => {
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,
userid: user.id
})
.first()
if (album) {
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,
userid: user.id
})
.update({
name
})
return res.json({ success: true })
}
@ -131,7 +182,7 @@ albumsController.get = async (req, res, next) => {
return res.json({
success: true,
title: title,
title,
count: files.length,
files
})
@ -151,7 +202,9 @@ albumsController.generateZip = async (req, res, next) => {
return res.download(filePath, fileName)
} else {
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.' }) }
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`)
@ -182,4 +235,71 @@ albumsController.generateZip = async (req, res, next) => {
}
}
albumsController.addFiles = async (req, res, next) => {
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.' })
}
let albumid = req.body.albumid
if (typeof albumid !== 'number') { albumid = parseInt(albumid) }
if (isNaN(albumid) || (albumid === undefined) || (albumid < 0)) { albumid = null }
if (albumid !== null) {
const album = await db.table('albums')
.where({
id: albumid,
userid: user.id
})
.first()
if (!album) {
return res.json({
success: false,
description: 'Album doesn\'t exist or it doesn\'t belong to the user.'
})
}
}
const files = await db.table('files')
.whereIn('id', ids)
.where(function () {
if (user.username !== 'root') {
this.where('userid', user.id)
}
})
const failedIds = ids.filter(id => !files.find(file => file.id === id))
await Promise.all(files.map(file => {
return db.table('files')
.where('id', file.id)
.update('albumid', albumid)
.catch(error => {
console.error(error)
failedIds.push(file.id)
})
}))
if (failedIds.length < ids.length) {
if (albumid !== null) {
await db.table('albums')
.where('id', albumid)
.update('editedAt', Math.floor(Date.now() / 1000))
}
return res.json({
success: true,
failedIds
})
}
return res.json({
success: false,
description: `Could not ${albumid === null ? 'add' : 'remove'} any of the selected files ${albumid === null ? 'to' : 'from'} the album.`
})
}
module.exports = albumsController

View File

@ -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(error => console.log(error))
utils.deleteFile(info.data.filename).then(() => {}).catch(error => console.log(error))
existingFiles.push(dbFile)
}
@ -428,7 +428,10 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
}
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.' }) })
}
}
@ -452,13 +455,17 @@ uploadsController.delete = async (req, res) => {
.first()
try {
await uploadsController.deleteFile(file.name).catch(error => {
await utils.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()
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))
await db.table('albums')
.where('id', file.albumid)
.update('editedAt', Math.floor(Date.now() / 1000))
}
} catch (error) {
console.log(error)
@ -475,73 +482,17 @@ uploadsController.bulkDelete = async (req, res) => {
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 = await utils.bulkDeleteFilesByIds(ids, user)
if (failedIds.length < ids.length) {
return res.json({
success: true,
failedIds
})
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), (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), (error, stats) => {
if (error) {
if (error.code !== 'ENOENT') {
console.log(error)
}
return resolve()
}
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), error => {
if (error) { return reject(error) }
return resolve()
})
})
})
})
success: false,
description: 'Could not delete any of the selected files.'
})
}

View File

@ -11,7 +11,7 @@ const utilsController = {}
utilsController.imageExtensions = ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png']
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov', '.mkv']
utilsController.getPrettyDate = function (date) {
utilsController.getPrettyDate = date => {
return date.getFullYear() + '-' +
(date.getMonth() + 1) + '-' +
date.getDate() + ' ' +
@ -23,7 +23,7 @@ utilsController.getPrettyDate = function (date) {
date.getSeconds()
}
utilsController.getPrettyBytes = function (num) {
utilsController.getPrettyBytes = num => {
// MIT License
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
if (!Number.isFinite(num)) { return num }
@ -51,7 +51,7 @@ utilsController.authorize = async (req, res) => {
res.status(401).json({ success: false, description: 'Invalid token.' })
}
utilsController.generateThumbs = function (file, basedomain) {
utilsController.generateThumbs = (file, basedomain) => {
const ext = path.extname(file.name).toLowerCase()
const isVideoExt = utilsController.videoExtensions.includes(ext)
const isImageExt = utilsController.imageExtensions.includes(ext)
@ -90,4 +90,84 @@ utilsController.generateThumbs = function (file, basedomain) {
})
}
utilsController.deleteFile = async file => {
const ext = path.extname(file).toLowerCase()
return new Promise((resolve, reject) => {
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 (!utilsController.imageExtensions.includes(ext) && !utilsController.videoExtensions.includes(ext)) {
return resolve()
}
file = file.substr(0, file.lastIndexOf('.')) + '.png'
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), error => {
if (error) { return reject(error) }
return resolve()
})
})
})
})
})
}
// This will return an array of IDs that could not be deleted
utilsController.bulkDeleteFilesByIds = async (ids, user) => {
if (!user) { return }
const files = await db.table('files')
.whereIn('id', ids)
.where(function () {
if (user.username !== 'root') {
this.where('userid', user.id)
}
})
const failedIds = ids.filter(id => !files.find(file => file.id === id))
// First, we delete all the physical files
await Promise.all(files.map(file => {
return utilsController.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 albumIds = []
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 && !albumIds.includes(file.albumid)) {
albumIds.push(file.albumid)
}
})
.catch(error => {
console.error(error)
failedIds.push(file.id)
})
}))
// Third, we update albums, if necessary
await Promise.all(albumIds.map(albumid => {
return db.table('albums')
.where('id', albumid)
.update('editedAt', Math.floor(Date.now() / 1000))
}))
return failedIds
}
module.exports = utilsController

View File

@ -14,7 +14,7 @@
<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=vZEyc9zyh6"></script>
<script type="text/javascript" src="js/auth.js?v=qXMCPUL26R"></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=vZEyc9zyh6">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=qXMCPUL26R">
<link rel="stylesheet" type="text/css" href="css/style.css?v=vZEyc9zyh6">
<link rel="stylesheet" type="text/css" href="css/dashboard.css?v=725MLaNmgj">
<link rel="stylesheet" type="text/css" href="css/dashboard.css?v=qXMCPUL26R">
<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=725MLaNmgj"></script>
<script type="text/javascript" src="js/dashboard.js?v=qXMCPUL26R"></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=vZEyc9zyh6">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css?v=qXMCPUL26R">
<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>
<script type="text/javascript" src="libs/clipboard.js/clipboard.min.js?v=8FbubjpYRC"></script>
<script type="text/javascript" src="js/home.js?v=BN63JuN6My"></script>
<script type="text/javascript" src="js/home.js?v=qXMCPUL26R"></script>
<!-- Open Graph tags -->
<meta property="og:type" content="website" />

View File

@ -149,6 +149,7 @@ html {
}
.image-container .controls .button:not(:active):not(:hover) {
color: #fff;
background-color: rgba(49, 54, 59, .75);
}

View File

@ -27,7 +27,7 @@ page.do = dest => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -55,7 +55,7 @@ page.verify = () => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}

View File

@ -7,7 +7,8 @@ const panel = {
token: localStorage.token,
filesView: localStorage.filesView,
clipboardJS: undefined,
selectedFiles: []
selectedFiles: [],
selectAlbumContainer: undefined
}
panel.preparePage = () => {
@ -28,7 +29,7 @@ panel.verifyToken = (token, reloadOnError) => {
.then(response => {
if (response.data.success === false) {
swal({
title: 'An error occurred',
title: 'An error occurred!',
text: response.data.description,
icon: 'error'
}).then(() => {
@ -48,7 +49,7 @@ panel.verifyToken = (token, reloadOnError) => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -112,7 +113,7 @@ panel.getUploads = (album, page, element) => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -145,11 +146,17 @@ panel.getUploads = (album, page, element) => {
</a>
</div>
<div class="column" style="text-align: right">
<a class="button is-small is-danger" title="Delete selected files" onclick="panel.deleteSelectedFiles()">
<a class="button is-small is-warning" title="Add to album" onclick="panel.addSelectedFilesToAlbum(${album})">
<span class="icon">
<i class="icon-plus"></i>
</span>
<span>Add to album</span>
</a>
<a class="button is-small is-danger" title="Bulk delete" onclick="panel.deleteSelectedFiles(${album})">
<span class="icon">
<i class="icon-trash"></i>
</span>
<span class="is-mobile-hidden">Delete selected files</span>
<span>Bulk delete</span>
</a>
</div>
</div>
@ -169,41 +176,46 @@ panel.getUploads = (album, page, element) => {
const table = document.getElementById('table')
for (const item of response.data.files) {
const selected = panel.selectedFiles.includes(item.id)
for (const file of response.data.files) {
const selected = panel.selectedFiles.includes(file.id)
if (!selected && allFilesSelected) { allFilesSelected = false }
const div = document.createElement('div')
let displayAlbumOrUser = item.album
let displayAlbumOrUser = file.album
if (panel.username === 'root') {
displayAlbumOrUser = ''
if (item.username !== undefined) { displayAlbumOrUser = item.username }
if (file.username !== undefined) { displayAlbumOrUser = file.username }
}
div.className = 'image-container column is-narrow'
if (item.thumb !== undefined) {
div.innerHTML = `<a class="image" href="${item.file}" target="_blank"><img src="${item.thumb}"/></a>`
if (file.thumb !== undefined) {
div.innerHTML = `<a class="image" href="${file.file}" target="_blank"><img src="${file.thumb}"/></a>`
} else {
div.innerHTML = `<a class="image" href="${item.file}" target="_blank"><h1 class="title">.${item.file.split('.').pop()}</h1></a>`
div.innerHTML = `<a class="image" href="${file.file}" target="_blank"><h1 class="title">.${file.file.split('.').pop()}</h1></a>`
}
div.innerHTML += `
<input type="checkbox" class="file-checkbox" title="Select this file" onclick="panel.selectFile(${item.id}, this)"${selected ? ' checked' : ''}>
<input type="checkbox" class="file-checkbox" title="Select this file" onclick="panel.selectFile(${file.id}, this)"${selected ? ' checked' : ''}>
<div class="controls">
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.file}">
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${file.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})">
<a class="button is-small is-warning" title="Add to album" onclick="panel.addToAlbum([${file.id}], ${album})">
<span class="icon">
<i class="icon-plus"></i>
</span>
</a>
<a class="button is-small is-danger" title="Delete file" onclick="panel.deleteFile(${file.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>
<p><span>${file.name}</span></p>
<p>${displayAlbumOrUser ? `<span>${displayAlbumOrUser}</span> ` : ''}${file.size}</div>
`
table.appendChild(div)
}
@ -237,37 +249,42 @@ panel.getUploads = (album, page, element) => {
const table = document.getElementById('table')
for (const item of response.data.files) {
const selected = panel.selectedFiles.includes(item.id)
for (const file of response.data.files) {
const selected = panel.selectedFiles.includes(file.id)
if (!selected && allFilesSelected) { allFilesSelected = false }
const tr = document.createElement('tr')
let displayAlbumOrUser = item.album
let displayAlbumOrUser = file.album
if (panel.username === 'root') {
displayAlbumOrUser = ''
if (item.username !== undefined) { displayAlbumOrUser = item.username }
if (file.username !== undefined) { displayAlbumOrUser = file.username }
}
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><input type="checkbox" class="file-checkbox" title="Select this file" onclick="panel.selectFile(${file.id}, this)"${selected ? ' checked' : ''}></th>
<th><a href="${file.file}" target="_blank">${file.file}</a></th>
<th>${displayAlbumOrUser}</th>
<td>${item.size}</td>
<td>${item.date}</td>
<td>${file.size}</td>
<td>${file.date}</td>
<td style="text-align: right">
<a class="button is-small is-primary" title="View thumbnail" onclick="panel.displayThumbnailModal('${item.thumb}')">
<a class="button is-small is-primary" title="View thumbnail" onclick="panel.displayThumbnailModal('${file.thumb}')"${file.thumb ? '' : ' style="display: none"'}>
<span class="icon">
<i class="icon-picture-1"></i>
</span>
</a>
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${item.file}">
<a class="button is-small is-info clipboard-js" title="Copy link to clipboard" data-clipboard-text="${file.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})">
<a class="button is-small is-warning" title="Add to album" onclick="panel.addToAlbum([${file.id}])">
<span class="icon">
<i class="icon-plus"></i>
</span>
</a>
<a class="button is-small is-danger" title="Delete file" onclick="panel.deleteFile(${file.id}, ${album}, ${page})">
<span class="icon">
<i class="icon-trash"></i>
</span>
@ -286,7 +303,7 @@ panel.getUploads = (album, page, element) => {
}
}).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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -297,6 +314,7 @@ panel.setFilesView = (view, album, page, element) => {
}
panel.displayThumbnailModal = thumb => {
if (!thumb) { return }
document.getElementById('modalImage').src = thumb
document.getElementById('modal').className += ' is-active'
}
@ -345,7 +363,7 @@ panel.deleteFile = (id, album, page) => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -354,15 +372,15 @@ panel.deleteFile = (id, album, page) => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
})
}
panel.deleteSelectedFiles = () => {
panel.deleteSelectedFiles = album => {
const count = panel.selectedFiles.length
if (!count) {
return swal('An error occurred', 'You have not selected any files.', 'error')
return swal('An error occurred!', 'You have not selected any files.', 'error')
}
const suffix = `file${count === 1 ? '' : 's'}`
@ -388,13 +406,13 @@ panel.deleteSelectedFiles = () => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
let deletedCount = count
let deleted = count
if (response.data.failedIds && response.data.failedIds.length) {
deletedCount -= response.data.failedIds.length
deleted -= response.data.failedIds.length
panel.selectedFiles = panel.selectedFiles.filter(id => response.data.failedIds.includes(id))
} else {
panel.selectedFiles = []
@ -402,23 +420,127 @@ panel.deleteSelectedFiles = () => {
localStorage.selectedFiles = JSON.stringify(panel.selectedFiles)
swal('Deleted!', `${deletedCount} file${deletedCount === 1 ? ' has' : 's have'} been deleted.`, 'success')
panel.getUploads()
swal('Deleted!', `${deleted} file${deleted === 1 ? ' has' : 's have'} been deleted.`, 'success')
panel.getUploads(album)
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
})
}
panel.addSelectedFilesToAlbum = album => {
const count = panel.selectedFiles.length
if (!count) {
return swal('An error occurred!', 'You have not selected any files.', 'error')
}
return panel.addToAlbum(panel.selectedFiles, album)
}
panel.addToAlbum = async (ids, album) => {
const proceed = await swal({
title: 'Are you sure?',
text: 'You will be able to choose an album after confirming this.',
buttons: {
cancel: true,
confirm: {
text: 'Yes',
closeModal: false
}
}
})
if (!proceed) { return }
const list = await axios.get('api/albums')
.catch(error => {
console.log(error)
swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
if (!list) { return }
if (list.data.success === false) {
if (list.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred!', list.data.description, 'error')
}
}
if (!panel.selectAlbumContainer) {
// We want to this to be re-usable
panel.selectAlbumContainer = document.createElement('div')
panel.selectAlbumContainer.id = 'selectAlbum'
panel.selectAlbumContainer.className = 'select is-fullwidth'
}
const options = list.data.albums
.map(album => `<option value="${album.id}">${album.name}</option>`)
.join('\n')
panel.selectAlbumContainer.innerHTML = `
<select>
<option value="">Choose an album</option>
<option value="-1">Remove from album</option>
${options}
</select>
<p class="help is-danger">If a file is already in an album, it will be moved.</p>
`
const choose = await swal({
content: panel.selectAlbumContainer,
buttons: {
cancel: true,
confirm: {
text: 'OK',
closeModal: false
}
}
})
if (!choose) { return }
let albumid = parseInt(panel.selectAlbumContainer.getElementsByTagName('select')[0].value)
if (isNaN(albumid)) {
return swal('An error occurred!', 'You did not choose an album.', 'error')
}
const add = await axios.post('api/albums/addfiles', { ids, albumid })
.catch(error => {
console.log(error)
swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
if (!add) { return }
if (add.data.success === false) {
if (add.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred!', add.data.description, 'error')
}
}
let added = ids.length
if (add.data.failedIds && add.data.failedIds.length) {
added -= add.data.failedIds.length
}
const suffix = `file${ids.length === 1 ? '' : 's'}`
if (!added) {
return swal('An error occurred!', `Could not add the ${suffix} to the album.`, 'error')
}
swal('Woohoo!', `Successfully ${albumid < 0 ? 'removed' : 'added'} ${added} ${suffix} ${albumid < 0 ? 'from' : 'to'} the album.`, 'success')
return panel.getUploads(album)
}
panel.getAlbums = () => {
axios.get('api/albums').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')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -460,26 +582,26 @@ panel.getAlbums = () => {
const table = document.getElementById('table')
for (const item of response.data.albums) {
for (const album of response.data.albums) {
const tr = document.createElement('tr')
tr.innerHTML = `
<tr>
<th>${item.name}</th>
<th>${item.files}</th>
<td>${item.date}</td>
<td><a href="${item.identifier}" target="_blank">${item.identifier}</a></td>
<th>${album.name}</th>
<th>${album.files}</th>
<td>${album.date}</td>
<td><a href="${album.identifier}" target="_blank">${album.identifier}</a></td>
<td style="text-align: right">
<a class="button is-small is-primary" title="Edit name" onclick="panel.renameAlbum(${item.id})">
<a class="button is-small is-primary" title="Edit name" onclick="panel.renameAlbum(${album.id})">
<span class="icon is-small">
<i class="icon-pencil-1"></i>
</span>
</a>
<a class="button is-small is-info 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="${album.identifier}">
<span class="icon is-small">
<i class="icon-clipboard"></i>
</span>
</a>
<a class="button is-small is-danger" title="Delete album" onclick="panel.deleteAlbum(${item.id})">
<a class="button is-small is-danger" title="Delete album" onclick="panel.deleteAlbum(${album.id})">
<span class="icon is-small">
<i class="icon-trash"></i>
</span>
@ -497,7 +619,7 @@ panel.getAlbums = () => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -526,7 +648,7 @@ panel.renameAlbum = id => {
})
.then(response => {
if (response.data.success === false) {
if (response.data.description === 'No token provided') { return panel.verifyToken(panel.token) } else if (response.data.description === 'Name already in use') { swal.showInputError('That name is already in use!') } else { swal('An error occurred', response.data.description, 'error') }
if (response.data.description === 'No token provided') { return panel.verifyToken(panel.token) } else if (response.data.description === 'Name already in use') { swal.showInputError('That name is already in use!') } else { swal('An error occurred!', response.data.description, 'error') }
return
}
@ -536,7 +658,7 @@ panel.renameAlbum = id => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
})
}
@ -552,19 +674,26 @@ panel.deleteAlbum = id => {
confirm: {
text: 'Yes, delete it!',
closeModal: false
},
purge: {
text: 'Umm, delete the files too please?',
value: 'purge',
className: 'swal-button--danger',
closeModal: false
}
}
}).then(value => {
if (!value) { return }
axios.post('api/albums/delete', {
id: id
id: id,
purge: value === 'purge'
})
.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')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -574,7 +703,7 @@ panel.deleteAlbum = id => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
})
}
@ -585,12 +714,12 @@ panel.submitAlbum = element => {
name: document.getElementById('albumName').value
})
.then(async response => {
panel.setLoading(element, false)
panel.isLoading(element, false)
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')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -600,8 +729,8 @@ panel.submitAlbum = element => {
})
.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')
panel.isLoading(element, false)
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -612,7 +741,7 @@ panel.getAlbumsSidebar = () => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -637,13 +766,13 @@ panel.getAlbumsSidebar = () => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.getAlbum = item => {
panel.setActiveMenu(item)
panel.getUploads(item.id)
panel.getAlbum = album => {
panel.setActiveMenu(album)
panel.getUploads(album.id)
}
panel.changeFileLength = () => {
@ -653,7 +782,7 @@ panel.changeFileLength = () => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -685,7 +814,7 @@ panel.changeFileLength = () => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -698,7 +827,7 @@ panel.setFileLength = (fileLength, element) => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -713,7 +842,7 @@ panel.setFileLength = (fileLength, element) => {
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -724,7 +853,7 @@ panel.changeToken = () => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -755,7 +884,7 @@ panel.changeToken = () => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -768,7 +897,7 @@ panel.getNewToken = element => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -784,7 +913,7 @@ panel.getNewToken = element => {
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -840,7 +969,7 @@ panel.sendNewPassword = (pass, element) => {
if (response.data.description === 'No token provided') {
return panel.verifyToken(panel.token)
} else {
return swal('An error occurred', response.data.description, 'error')
return swal('An error occurred!', response.data.description, 'error')
}
}
@ -855,7 +984,7 @@ panel.sendNewPassword = (pass, element) => {
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -876,7 +1005,6 @@ window.onload = () => {
}
const selectedFiles = localStorage.selectedFiles
console.log(selectedFiles)
if (selectedFiles) {
panel.selectedFiles = JSON.parse(selectedFiles)
}
@ -891,6 +1019,6 @@ window.onload = () => {
panel.clipboardJS.on('error', event => {
console.error(event)
return swal('An error occurred', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error')
return swal('An error occurred!', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error')
})
}

View File

@ -24,7 +24,7 @@ upload.checkIfPublic = () => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -50,7 +50,7 @@ upload.verifyToken = (token, reloadOnError) => {
.then(response => {
if (response.data.success === false) {
swal({
title: 'An error occurred',
title: 'An error occurred!',
text: response.data.description,
icon: 'error'
}).then(() => {
@ -68,7 +68,7 @@ upload.verifyToken = (token, reloadOnError) => {
})
.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')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -101,7 +101,7 @@ upload.prepareUpload = () => {
})
.catch(e => {
console.log(e)
return swal('An error occurred', 'There was an error with the request, please check the console for more information.', 'error')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
})
}
@ -292,6 +292,6 @@ window.onload = () => {
upload.clipboardJS.on('error', event => {
console.error(event)
return swal('An error occurred', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error')
return swal('An error occurred!', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error')
})
}

View File

@ -1,11 +1,11 @@
@font-face {
font-family: 'fontello';
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');
src: url('fontello.eot?1863770');
src: url('fontello.eot?1863770#iefix') format('embedded-opentype'),
url('fontello.woff2?1863770') format('woff2'),
url('fontello.woff?1863770') format('woff'),
url('fontello.ttf?1863770') format('truetype'),
url('fontello.svg?1863770#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -59,4 +59,5 @@
.icon-pencil-1:before { content: '\e805'; } /* '' */
.icon-clipboard:before { content: '\e806'; } /* '' */
.icon-arrows-cw:before { content: '\e807'; } /* '' */
.icon-plus:before { content: '\e808'; } /* '' */
.icon-paper-plane-empty:before { content: '\f1d9'; } /* '' */

Binary file not shown.

View File

@ -22,6 +22,8 @@
<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="plus" unicode="&#xe808;" d="M0 209l0 282 359 0 0 359 282 0 0-359 359 0 0-282-359 0 0-359-282 0 0 359-359 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>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -30,6 +30,7 @@ routes.get('/album/:id/:page', (req, res, next) => uploadController.list(req, re
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next))
routes.get('/albums/:sidebar', (req, res, next) => albumsController.list(req, res, next))
routes.post('/albums', (req, res, next) => albumsController.create(req, res, next))
routes.post('/albums/addfiles', (req, res, next) => albumsController.addFiles(req, res, next))
routes.post('/albums/delete', (req, res, next) => albumsController.delete(req, res, next))
routes.post('/albums/rename', (req, res, next) => albumsController.rename(req, res, next))
routes.get('/albums/test', (req, res, next) => albumsController.test(req, res, next))