From 8724d45ce0a6f8cc91ff18ffccadcf93f8d330fb Mon Sep 17 00:00:00 2001 From: Bobby Wibowo Date: Fri, 30 Mar 2018 09:39:53 +0700 Subject: [PATCH] Updates * 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. --- controllers/albumsController.js | 150 +++++++++++++-- controllers/uploadController.js | 89 ++------- controllers/utilsController.js | 86 ++++++++- pages/auth.html | 2 +- pages/dashboard.html | 6 +- pages/home.html | 4 +- public/css/dashboard.css | 1 + public/js/auth.js | 4 +- public/js/dashboard.js | 280 ++++++++++++++++++++-------- public/js/home.js | 10 +- public/libs/fontello/fontello.css | 13 +- public/libs/fontello/fontello.eot | Bin 6980 -> 7104 bytes public/libs/fontello/fontello.svg | 2 + public/libs/fontello/fontello.ttf | Bin 6812 -> 6936 bytes public/libs/fontello/fontello.woff | Bin 4168 -> 4228 bytes public/libs/fontello/fontello.woff2 | Bin 3324 -> 3420 bytes routes/api.js | 1 + 17 files changed, 466 insertions(+), 182 deletions(-) diff --git a/controllers/albumsController.js b/controllers/albumsController.js index b9db9a0..ff310dd 100644 --- a/controllers/albumsController.js +++ b/controllers/albumsController.js @@ -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 diff --git a/controllers/uploadController.js b/controllers/uploadController.js index b9b0377..94f83c7 100644 --- a/controllers/uploadController.js +++ b/controllers/uploadController.js @@ -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.' }) } diff --git a/controllers/utilsController.js b/controllers/utilsController.js index 760349b..0fd48b1 100644 --- a/controllers/utilsController.js +++ b/controllers/utilsController.js @@ -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.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 diff --git a/pages/auth.html b/pages/auth.html index 6c06a76..b3b8680 100644 --- a/pages/auth.html +++ b/pages/auth.html @@ -14,7 +14,7 @@ - + diff --git a/pages/dashboard.html b/pages/dashboard.html index f910a99..5c54a5e 100644 --- a/pages/dashboard.html +++ b/pages/dashboard.html @@ -11,13 +11,13 @@ - + - + - + diff --git a/pages/home.html b/pages/home.html index 3efcdca..a6cb0d9 100644 --- a/pages/home.html +++ b/pages/home.html @@ -11,13 +11,13 @@ - + - + diff --git a/public/css/dashboard.css b/public/css/dashboard.css index e17cead..91c8c31 100644 --- a/public/css/dashboard.css +++ b/public/css/dashboard.css @@ -149,6 +149,7 @@ html { } .image-container .controls .button:not(:active):not(:hover) { + color: #fff; background-color: rgba(49, 54, 59, .75); } diff --git a/public/js/auth.js b/public/js/auth.js index 885bdc8..a0e8e44 100644 --- a/public/js/auth.js +++ b/public/js/auth.js @@ -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') }) } diff --git a/public/js/dashboard.js b/public/js/dashboard.js index dc3a654..a564bbe 100644 --- a/public/js/dashboard.js +++ b/public/js/dashboard.js @@ -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) => { @@ -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 = `` + if (file.thumb !== undefined) { + div.innerHTML = `` } else { - div.innerHTML = `

.${item.file.split('.').pop()}

` + div.innerHTML = `

.${file.file.split('.').pop()}

` } div.innerHTML += ` - +
-

${item.name}

-

${displayAlbumOrUser ? `${displayAlbumOrUser} – ` : ''}${item.size}

+

${file.name}

+

${displayAlbumOrUser ? `${displayAlbumOrUser} – ` : ''}${file.size} ` 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 = ` - - ${item.file} + + ${file.file} ${displayAlbumOrUser} - ${item.size} - ${item.date} + ${file.size} + ${file.date} - + - + - + + + + + + @@ -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 => ``) + .join('\n') + + panel.selectAlbumContainer.innerHTML = ` + +

If a file is already in an album, it will be moved.

+ ` + + 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 = ` - ${item.name} - ${item.files} - ${item.date} - ${item.identifier} + ${album.name} + ${album.files} + ${album.date} + ${album.identifier} - + - + - + @@ -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') }) } diff --git a/public/js/home.js b/public/js/home.js index d0f2310..dd12d9c 100644 --- a/public/js/home.js +++ b/public/js/home.js @@ -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') }) } diff --git a/public/libs/fontello/fontello.css b/public/libs/fontello/fontello.css index c1f5ba5..aa98534 100644 --- a/public/libs/fontello/fontello.css +++ b/public/libs/fontello/fontello.css @@ -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'; } /* '' */ diff --git a/public/libs/fontello/fontello.eot b/public/libs/fontello/fontello.eot index 694afc15196c288abc99fcdbf2c8c1ce4bdc6825..ddcc62092a6b509ad21f4840f2385ee66cbff83c 100644 GIT binary patch delta 595 zcmX|7O=uHA6#m}MWOuU@lh%WcjmRoRkW>YssfQk_2T|})M2baF*8FT}v*}_|sb0Kz z5WO_asff~g(u)*w5J4nDy;abI1@R&+O2Ct#;vpu!O{p`y`R04ydmnFRJ=YpHTNxmo zX_z^=<*lj4pChs#B;F!1yFcfeU^Jy+(0RS#-$of7eqa?66P!002}axg@u}AX+Sox>h&6VP zRPK?8F}GkN-tRZ{8>6G&8&)d8HNd_8$*dlI_ zxCn*glUaj6f*=AjW(Y|_&VeX#4pQ!s=h;I+4Mxj~?az)49{Rt@r(yED%iV7ub}!v! z@^#)>J)qXSt!t@?Ur?eQO}4o`_GA~etHw#`%j$@}tupbmqS5~u)qJd!7*b^LG?HRu dl0`Pf$st7v{W>`nlVN3{rjx0m=19s-`~zF=h2H=G delta 489 zcmXw#KS&%w6vn@|yEl8gd*)P36cdgsOi=V1ku!)zgcK<)L`hPF$+>q|?CPM0C ziv33BdDeI#n9FZp{Nh@(42YegFUK1%d%FO4g!62X4SU>t&HRqJqZpQE?>`%@|6$_; z8=b*qPC6O8jrj}nolwpy+D)XH&zRE_GW0*cPceIZsIjLer%N+d?^mGV8|RB^%2y{t z-J3vri(aAUw5fknVJrY~3gls4uXE>2kLz`yhX2Y5an^FJ2KQKq zG24j4;YjFh%5CKe_so_ds8Gt&U_ry0TksPI%^Rmkct=q8JIXDNYW#KUAf;>Qb^J@ejuTco6^q diff --git a/public/libs/fontello/fontello.svg b/public/libs/fontello/fontello.svg index 53a7c05..d93df9d 100644 --- a/public/libs/fontello/fontello.svg +++ b/public/libs/fontello/fontello.svg @@ -22,6 +22,8 @@ + + diff --git a/public/libs/fontello/fontello.ttf b/public/libs/fontello/fontello.ttf index ba8effebdabcbdd9bb748a4edb71d97844c83922..defc7ceb76fc7b28be600e18c9b6802af441f2b4 100644 GIT binary patch delta 580 zcmX|-F=!J}7{~wLz02k9UYa%zHCB;RgHX~a2&qd4br1!IBDElbaweAxnoA!|D(U3n zAWjLlSX8i1x=0~|hz<_A>7ZaiT*RUrI0;%h)STb7wD0)%zyJ5+{qKA4Vfp@K_sH$z z08Im!tpu*hzx;fH{Zp>>%B{9zti>8I%ot8h`N6$j_cD72kji&kiu)r#c7xUQ8;iAt z+KX?1cnqWu*FCp7^5^+4z<$eovd)ckRor2}$-Ynz+MV$;v++L;bGR|ln5($f9{U;l z3+(fO+fno`!%SA$J54w6-ak5+cn;A%sJT}A>*|A19$<#~vYPkQkJH81z*x7pafRN5 zGvr@tf*1mIh^^fOtIhXoLdO*M4OW5{nZLipF%V%}nGiD$Pbi8NL+-(u~S8jK!P9wBc%zQgnko5nRC$f zA^kjONXT5W(y4vLa_-RXM?Q^_KUf<4{J66jERi3+vX&3X75DqK;icvuMXV79Tv${hf0!@7?Xu-H~eldIF#+0JCY^ za&BYe$IM53z4S`iRKNQQ5ZUD1%sF{`t5%(3J_HOWZ!J6QN14}H!};R--1(0t_lhc@ zekf$EO!VjQ5FlqbA1$zI|Y=QfpS06iVGKX0hv&QN~32;vaP zK|EEjSS9aQLdY(;lvUAMo&BIU&wEo>bwP5L%cugrV1?N*n3H(!tT)xLA-6 z*Wk$j>jxsKx=Fd;d(wB8ecL|y3x@E5&dYDVQ_t#pa*U*F8gbMA7nqpG8TC?zw&}Cd q5M9DzFZjBwFB*pEl%$ZL5Vep>8ih$GNrqdAOu1hpv3fJ|qV*B03~faK diff --git a/public/libs/fontello/fontello.woff b/public/libs/fontello/fontello.woff index e6ee1624d5ea037f40c687ccebccde5352ea2634..bc5413bf35a99120efc02a23384e9fb9eb088b39 100644 GIT binary patch delta 1991 zcmV;&2RQi1AcP?lcTYw}00961000n#01p5F000{pkrY1_FJojcyIs! z4fFs203ZMW03ZRy3?Ob{ZDjxe4io?Y0e1iZ0?o{w9v&cYZ*z1201u1+004Xd005!e z)|(b^a%FG;01yb1OaV;-KERWk0XTmSnhe(fc%1E#!4bkR3V z&a+qoumyi_Y*SSfKj+@}ukCB!d;Pz5Ti15&+`5geUE6JJ@Pv|Wu;7AZ3@{av1)^+% ziAGTQF!`b;Xfl%p5)wvYkPk{oki{r5QT%2IlK6r6K?MntXhP6v%+kJjUb|re@%Em3 z@4N5z-E)5D_d6f~th~tY^BurJ1{Y}HgAkO>y$XLa-QngO7%v+x4;MvY(usngj!FoC zlbWn@L^qE4{kpEIiXsRA{*XTu4Cp@H=QUJK)jV#+rMR39K^A05q${}L6$6<-J{RDt z=F7(exN6IGJIVG}&(>{Wc^O};Th;sD8mQsQ%F08*VBbOpHk)Q5B6G%Z1SF6S%SbmJ z2HJl(EFeQe zvKuiFPo#@|g<`(1kjn-`#R7NFoH{l0-DbT**R(U9ZqFG_)7$luRhfJfr%IpAO)n-C z371dZI)x858HUzDrTHsVGq zZp3~0V#pT?U|%7W4ayX6BF*ntrujtW(w>C_U;R{_?n%e9*pol{#70i1I(=P*
mHskVJn)`_}m17_a=v?yzGg$Hz}r9)`=<81+;a9j;k;hH627(;v?dL-Jb>;m>A3ie8?Sq z1B+qM!`5NCgx>3T9=Wv_H8waAInhuXqwnJ`>j7@U^H#;$!~SR+Vv!S(i9vtXa3V?{ zciSyQo) z4-Dkj(4w`Tk;|x39M^m+k85>RH#_(%I6P z=}4y%@y1wVthp&14M!slPu39%(Ph}JLu??5if$3+lSa-dw+Ym`guHltlkp|>L_^ZKZhmN){o8C7i#+PKoe{?w}t&olsuf- z#g!u;381h=k!cS|8#aqkA~}I12~%KKVWJbC0?yrhOw+U`ttsJaO?VTe_oyu)Ihe~9 ziaAMWOSJJqYc3lq8u5Q(I+>7UoL`(-IR16~-K6#VJbrWW?!5hip6#0ZNb@e}HZ!(QoLDtW)aL?e>`+Z%qvLBClPP0)%GTNs z4`KulTBoghH`f-{X*@`ntXTB@-}r85ARafF5#mE*)aR68%bI_JsDuQ9wt_Gyb4iwx ziMCX2?8aeCbz>0=+=n3t@>@RPJ+3KENw$7C?<6N&s`Zaw@bg9g$8S`A{sQkE(0Af827?WQO8wL#kk)8$blYk8x zC=bT}{{aC0C=6Zz00000TmZlU906njm;v4byaN^kMgx%m0RR913$wxvmH~f34#FT1 zKvAZ)T4*;OWELE01Y$|XG8rIEZ?DF!Z}k`74Ccp~{}veF0wYXtg&WLZ!NCHnv28=n zwV_|TaG{8SOOIA^Xk8O}_Z>)|pPZUf-DXL9$c)|w&hjXv(UNL8Du!be?)5GtNl}>g Zgy|*iH0BL~?<7fhoMZ6b!Elq)4|VCljxPWJ delta 1978 zcmV;r2SxaVA;=&UcTYw}00961000n301p5F000`CkrY1_D`Rb8Z~y=S*Z=?keE%>K#86y*9puRdn z7wrc6W`dNmHAV%T3DK}0B=7)cafkx;#)x^zweHV6yI(l9SE(`ia((X`oS_=i-QIQ5 zo1&j93oA`l@gzF)L>c3W(#8|zu4V?bABzUN9>M?s z0JB&EumyizY!p=#KIh(gD1eJ%%i<+QGO`1NCP!fYYC?P=_qr^n<%@8#4f%u?;L`gIuXf&qToqA`tKmzgR zo_p`ydw0({-}$}^3}EdAdWY@=3KIB0g%B7}vG;$;NHmY1QXsUV`vO!Hg=sGef-=q! z0H;++p@@E*2!}OIQDj*V0K!Jth(@%K77FT$s;B|K?2~<7k01#W6S)hj2gOKNq>zu$ zbqf{D2(H_z+fI=KwKEM{TwTRi8&(TGTmx^owzhUp(8)K@1zT*}N=TGY90LaIh=knJ z5#WCtM+78@WEY&n{as z!_}o5OL%v)uB)B=7ST@gS=Tq;eQo#nQC)w2bLHgcL z*aSw#WaJ|g$YGu}!zMD zbu&~b86hKr14Sbjm3Y2Zmfoq((y8jjy^9CG{HZqEmo;Fb!{rP=$(ntbbMY4*sEmKd5Qi= zC%H$)wNkMC0xu(uF_EBvSreyn7=#h9VFJFlzk8}4A1??mfd*I!qSl6?fEKDWVW6WqO;shJ zRuZT@k+i7atN1H1^tQ)M>XW^`N)+X+(Mml`_Ef^iGLaOO2$L$H>&iGMpE%vpfOK?p zXsA%=@9*hpZEb3b#TxGdz^H#cYH!*&G%_^uf<{=%*B_S5 zBZlD?+=$r#-aJdToV{85BK4rn7k3qr%sXfBz@xSX!6uD_W6?$WmR)~crOd7#n@^>V zH+pL;@B1qUI!4HF$6!aL_L*rOZ*+L!Y~X#QM*uR0t*N6kRzES zqLEBvIf*K64~UgF{o%s|KOX+`IN<@0IO7S=xS&Ii7jS(gDXmku4wcG@qSTR`me_}7 z3jRB=Jw7>i$p01X2G0RR9100000000000000000000 z0000SR0dW6gLViY36^jX2nvoQj3Nsd00A}vBm*o2AO(d@2Z13Bfd(7%7$Xt(d6Tk| z{Z#^YSo{kQpbB-Uh8m%uHH66OL=VSt&PJYBl(Ow0%R0QY_6453vklyUSqthDw5Fz7;h^ovu0=)c^mv()HZ`COP|e zxg6i~3oIU+qxJZcXXCbW*~5k~*` zioX&Bz(Bq=SVdJsI~;_ zUV%6g3uu38Q6T^&1>`dxcG$Cye`#pY!~W+fkP9pnos>0z07L--0vt>ZaBw-mA>;su zlmi@!9N|73=DKzTRQK-FOW?~z*J&@h^KA?j9QClUx@N~OvkEbtJ&Kx z$(whz`+<;X%;W~iiW;;#Yc@8&Q57Sh$x!Xq(*BZ`8Xrw zL_BABNmC6E*XT6*buoi_B!?g@jf15Z<`IqN>f9iCXFgx_0h^7r1`IaSfP+w#u=JGG z)QrcBZDDWBo;u*9a8<_x)PG@QSH0N;n4yvK6{^7ZG-LE6cV@}2^c;D!Q9uh2JaJ2kD6h6#Biua6{z{++TL%Q z_8e>9f6!vGIo+uo2w}tCr9dbfDF`EmaN>v{fk={wA_dW;B8D`?l3pFheXyZ36D*!x zGLOz6ffy2rBZ&l(Ng{<5q>_p>(vVJiI)m!F5;CVKxT+?}yAyH;RLN*jKTHO(;?YC=-_I>++2bzeS12Ox|PRBzWY`hM7(Dx6SQyv z4u3=bar*adr{&?`VxWNPwa8;N5c-G$h3t;?fhFErAg#QfVksN5!!zo%0N!RF(~~+k z@`VjbOXoA>aa;l%9KIM>MsSD-L^$V0*QHI(b*ZmKPFOA@r4D6;@aW^VMc+#j-q-Yt zT_3!5zgRhVnqxGfNj1r58*N??+DEcQP#(o|FqO5zHLQ_l@&?B_<>0bJ{}(lwgeW#2 zcATMU4vV#s!d8no#~@-@r;vUBG6rOfwTMz0H!{GZgb!bfq!Z-8it_g(30bPG-`N%_ z8VJp!q`97u-cU|4&d9t;%;VuBb?VJK*p^*{tZq*U%W6+ftKnK&YNs_D#qqZ5luUI( zTciCJJasEs^_W>nQ%A6z^S4QT?phZ1FKx?PHOT9{)%14h?gw5&mSu#Kt|iN}dN%Zy zvb9>oPKHoNmbZkrb5SwdU$dzfitR+zT*{_N8#=bbe`3+`^sMX0l)Zb~alWZ5e9oU! zRuNX&sl37)zXjVVE~|(h?3n(Deb7FH;|0B;sT^Cy2ADhwxt40Obd8#0;c#Z^G_Oew ziTql&MUNVNvO12iCxO)y&`?0e8VP7>4|`_K1hf>8u~q`w+QaVIIs)1Y$XEvfo$X3D#EB#=-`L#26bFLROglfW2oT@>lG7ZB(=eMFmAAIy)?7q9I51coDfAg{n$~)bzH%o81h4Gtjas265 zHPaP`51*puDC;Qj7o>A`r|ByQ)jlPBKuXEyo{`7_q9qaLj& z;*hVeGQpqFw{y@j(VzH07r7(;>|eHG+xidZFTVJ?>n6tM_dR!+XX{+z-N8TIk9RJ* z=;RBD_XPj&z&jUT1dF*&XnHSdWMurt<*q)`kiA^{p(ih})lm9VcHHvNRc^KPrnA<6 zH?d-6NePQsX}r+3`};zA`|RnBj(a=&cV3PZ92G=qSGKDtQv&?>U63W zC$gzFB*f*iEKL&v2pOk@g}Q>3Ah+dI9JXdEhAvb^k>JmoJ4l9RdA*5IDrcOkp+K2Y z2^=%vjL!&SM66v9W7I8)7&Q>mer$9^xZ7puia=JGnheSEHd~}fJKo8J!m2e|U78}; z6aX&4eUMOuRQeE?*9NYjQ_WSUX+Y$H-I`;TRuw5SMEadon5xb>1LK$y1Y)2Gg&4DH z45^jVD&w-0i>zBMgy513ImHMHaRI&&L{^PTQrfaM?Qo!5feO{_c55;`%WDfFP_-f% z396{S*Ly5p69k$CV4*hJs{@1?5DoV{7#hiBSeEy_(f}{AAaEiV$QMMeWD0h;f_*XM z$cRniPRrr+MUj;roycv&vV9R`q(vrhQ!`9o7-^~D95>|c`ohp`u%V)| zqK)SEL&M;nKmZ8%kLaH_I#>NoGkE|2KK^0ZjmjAppNn+hz%%1DUg4}%j zVlPFUa?AFS!mV3}`=4V~uGaSxnZ16CrS;HIjv35i7dB%IBN)Y2c%&;w29HAd3Ka3^ zhX#)sAEzI~H2j!^hacOp6{Fa3Tj1e^nqkbi)ojHOicGR?&ozJsER z?6PEQT_=X(cgJvT_b8An-v_I9r+vA8OkyzKHCwPeY)kvChWvXwJP!(rf-Ok%`IkR*jv(nu$ROtPSF9nI-ssLv^|`W9k2 ze*rqC(O$ zb0{)y4jtu;0O}vbt8Lw9iO%m?pu5Ik_AKOmgO5x#`+&hn;|m>_T`7B#+sP###Tt+slFjm8mt`y+hyt3HmC$GLc|frJr;tFX3D!(`H) zkPvqX{`uc8-+eB<`)DBfL@*x_WD!(lS8Y;Yas*>5REFNuzYJnJ$Q|8eP@#dD<=-b` z;B>p^_5lM5fWuhI|NFuR+d%tl&#=d;DENQ0eY2tjPjkatv3$f<2Fo|_brLkQR@4;r zbk)h=ksSbL+S0xUps+?RtS#!$PCBN;u8IHubEWIK|4nk{?{Yc5=NDK=bgdNO&iu?m zW-+^*DKQI0ZiPw#*;Nr(Vk;|se`MG9(yG(?3c4jPob;tTK|sbh=3o{sUb&IV$$b}3 z(*p*Ug*21KNkz?~IoLgSX(TT6=iWK^jf) zzYXk&WxL(R)7hE8J>0@aAze8f;@HDjB(}9=tsOI32i%cNGx^woCR!O(v$)e{&)xq0 z&m)q80yw)M_a@^~Xd(*)O^#8U9IG}tPHl3$+T;Ya$w|~EC+7aYWl}cPO%3g~4koL0 zj{v=QFKL{mH~$!}dqa5ol$f+**hjfgw<%$?{U2SM;?z!@_wWIoE#Sk?@po% zb@#O-XO_Yjs>B#f808c3r!llVvX&2ML+0kyGfA0v;q>?@8JP zA(6nNHG#wylV6|$rT7>~AdN-N(|^w8g8M|HWdE`hrE$l)N*a*L9O#6~Y(3B!RT{dGL07WqMh@M{qXz}_qy)Vv zqBo`0eJ}$X+DgItvIEx7WYM1t29U)-au`G&gDGGLB^XK(!zj%Rhnlg35mOXusfPJ` zLwbiw89tzXnvr0HF(L*=L7J+qLu)r}JGJbAGdZ6Fqv=1lmW+yMai?b97>X=?d1fs1 zu!_lX!CTQ)%6mqZqe}`|{sR9zqo=l6$9i=wFdk|&tztD0eL{f=7+h-t6T>wlo`yZe zBvj*;iyCMOGGs@+>h7DGdQ+{^iSrpr>?eA#^X0X`WWW-qg5@7g%JuVZj7Uv4&99#+ zWg4I{!byy9+hXr-5{cGDS=6O2At1#Gp5mj+2A%0l=3du{O<*5E>8JS_p&VsE|{ zDJIN;smAl2v+#n#bcg4b+%-m?gUi-`;*u$7-*3 zt9BV=&IMDf>*p~C%GSDR++;Wo7#XglF}f%?!>`HI2o-In)iiITMrA|CVfar> zN1j=BbDOevW1G(Rb%oE>lk;1`Z8|Ne@Fr}*HtWk4rWPw^x@R3sTq5!DaZnbGTD(#5 zHlGQ6^O6lFJz?pwSdL8tO}c>@jnd0DhN90>bF$?dJsU7jWCjUlHo`0t?AE}1=8)iK zBg`YgZw<_00SRF?!Xgsl)=k|gYL8HXa{RAgL=j61I2Q53w2f=^Lq z_(#3KJ9AWYi1{PixI=Ar(EIAXP7h?IbOhk_yLJ|5@AK-(gtc|Bef)tD{prZ$ZND~9{nW6Mv_x2a@SDG=)uffG%@tvJF z#1qGX3-2BAAL561@m+84-HZ3ZFrJDJnjf6-@7a*tY=F3PLT^B0H|5row?88@PJiXkF%cMx`5Ttj<0_ zx9obH_qS_Yy}@O{u}5R3jZTQL&>8&rI1B68xWLCQy?#)4Nl7*2*R7!I#51NHRSzA8 zIh;6#9UfNUixgoKg{+5mup<0-qN9q5n&ClED3?XI;;0xG91$0j#k5r-Dm~EuwxZbn zSvOA)ztYjtGB_9)+mV0V4r4V+bD~dIz&Fj!Ri{exqK{Y4w=~0Ifp49!ZK=188Q0@b z{TCPy2T3(^e7#qg6F<7l_a})nx3>Dr1?qOCK3ISKPa}Q(`kOz<*&Dq98atv)jV!Ij z*xmKKV!d~pGdWi*4FYYhCWVzsE$h@t5SSuH=b%$pRnt^K)+CXsYLznPT9&#*3m*Ht z)g`oZ{b~&8fm>s>gg`ix2+J9)v1go&WHve(qpe;tS}BGOc5Oo!1u6}Wks3kxPW5X| zq2SAGCv(9piF&-W;LIt&dhBjI!TGS4to-%x*_)H7d)^154Sk!h_52ibJfqMc32%d4C z6ZTYR{UdFg06+gZUOS_In63_Q10n_@Ti53TTs;7P_W%eivn-5-87OHM%ckKMZa%QR zdO|Bvr;O#4XE1E!AQymt@?>yB3%_Q~ne13XL?#PE!fauQEM!hyDLgqFMIf?AWQ^p< z*>hTX46w+hz;T{sgs7=13>EbimYPO0r_L6hE?Ol5H60Kcn{{EYoFV$`ADGvi?xYF- zRN52^$$0TEHZzhm#_lo|#CDhl%MJgE%@3c7TAE6ck`o zl#J(Eu18Kv5R-vyKuml(+-5=to54$?Bjzz9=4 zS_Qq-3hfDYMYV7;d2}q5O!D+7lEkKgmA<-6O)F|BFWdN#~;U#z8Q8JmqU$^VfxF^y2w_7mmiWhfW_jcG51%+g8?f|3o@9 zZ-Qh-AGlI~Zo(V=Y>7AeIgT>|D_-T(^;_Gehqkrp1TQ?>jz7dtGeRHtLS__&oC>pt z`LCOI5h+5FOCy?OVf!H6vu 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))