Updates (experimental)

* Possible performance improvement. Some bulk db queries will now be executed in a single query instead of spawning multiple async task for each query. This is sorta experimental though, use it at your own risk (though I'll use it right away at safe.fiery.me).

* It's now possible for root user to add files to other users' albums through the API route. I don't plan on allowing root user to list other users' album list from the dashboard, I just thought that there'd be no harm in extending the API a little bit.

* Kinda better error logging for uncaught exception and unhandled rejection. Their stack trace should be logged now.
This commit is contained in:
Bobby Wibowo 2018-05-11 00:25:52 +07:00
parent a4f9a0f8df
commit 479db54cd3
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
4 changed files with 70 additions and 74 deletions

View File

@ -453,9 +453,11 @@ albumsController.addFiles = async (req, res, next) => {
if (albumid !== null) {
const album = await db.table('albums')
.where({
id: albumid,
userid: user.id
.where('id', albumid)
.where(function () {
if (user.username !== 'root') {
this.where('userid', user.id)
}
})
.first()
@ -476,34 +478,30 @@ albumsController.addFiles = async (req, res, next) => {
const failed = ids.filter(id => !files.find(file => file.id === id))
await Promise.all(files.map(file => {
const updateDb = await db.table('files')
.whereIn('id', files.map(file => file.id))
.update('albumid', albumid)
.catch(console.error)
if (!updateDb) {
return res.json({
success: false,
description: `Could not ${albumid === null ? 'add' : 'remove'} any files ${albumid === null ? 'to' : 'from'} the album.`
})
}
files.forEach(file => {
if (file.albumid && !albumids.includes(file.albumid)) {
albumids.push(file.albumid)
}
return db.table('files')
.where('id', file.id)
.update('albumid', albumid)
.catch(error => {
console.error(error)
failed.push(file.id)
})
}))
if (failed.length < ids.length) {
await Promise.all(albumids.map(albumid => {
return db.table('albums')
.where('id', albumid)
.update('editedAt', Math.floor(Date.now() / 1000))
}))
return res.json({ success: true, failed })
}
return res.json({
success: false,
description: `Could not ${albumid === null ? 'add' : 'remove'} any files ${albumid === null ? 'to' : 'from'} the album.`
})
await db.table('albums')
.whereIn('id', albumids)
.update('editedAt', Math.floor(Date.now() / 1000))
.catch(console.error)
return res.json({ success: true, failed })
}
module.exports = albumsController

View File

@ -164,7 +164,7 @@ uploadsController.actuallyUpload = async (req, res, user, albumid) => {
}
})
const result = await uploadsController.writeFilesToDb(req, res, user, infoMap)
const result = await uploadsController.formatInfoMap(req, res, user, infoMap)
.catch(erred)
if (result) {
@ -254,9 +254,7 @@ uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => {
resolve()
})
})
}))
.then(() => true)
.catch(erred)
})).catch(erred)
if (!chunksDeleted) { return }
// Delete UUID dir
@ -280,7 +278,7 @@ uploadsController.actuallyFinishChunks = async (req, res, user, albumid) => {
iteration++
if (iteration === files.length) {
const result = await uploadsController.writeFilesToDb(req, res, user, infoMap)
const result = await uploadsController.formatInfoMap(req, res, user, infoMap)
.catch(erred)
if (result) {
@ -301,7 +299,7 @@ uploadsController.appendToStream = (destFileStream, uuidDr, chunkNames) => {
append(++i)
})
.on('error', error => {
console.erred(error)
console.error(error)
destFileStream.end()
return reject(error)
})
@ -315,7 +313,7 @@ uploadsController.appendToStream = (destFileStream, uuidDr, chunkNames) => {
})
}
uploadsController.writeFilesToDb = (req, res, user, infoMap) => {
uploadsController.formatInfoMap = (req, res, user, infoMap) => {
return new Promise((resolve, reject) => {
let iteration = 0
const files = []
@ -410,17 +408,13 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
}
if (albumids.length) {
const editedAt = Math.floor(Date.now() / 1000)
await Promise.all(albumids.map(albumid => {
return db.table('albums')
.where('id', albumid)
.update('editedAt', editedAt)
.then(() => {})
.catch(error => {
console.erred(error)
albumSuccess = false
})
}))
await db.table('albums')
.whereIn('id', albumids)
.update('editedAt', Math.floor(Date.now() / 1000))
.catch(error => {
console.error(error)
albumSuccess = false
})
}
mappedFiles = files.map(file => {

View File

@ -137,8 +137,8 @@ utilsController.deleteFile = file => {
* @return {any[]} failed
*/
utilsController.bulkDeleteFiles = async (field, values, user) => {
if (!user) { return }
if (!['id', 'name'].includes(field)) { return }
if (!user || !['id', 'name'].includes(field)) { return }
const files = await db.table('files')
.whereIn(field, values)
.where(function () {
@ -147,48 +147,50 @@ utilsController.bulkDeleteFiles = async (field, values, user) => {
}
})
const deleted = []
const failed = values.filter(value => !files.find(file => file[field] === value))
const albumids = []
// Delete all files
// Delete all files physically
await Promise.all(files.map(file => {
return new Promise(async resolve => {
const deleteFile = await utilsController.deleteFile(file.name)
await utilsController.deleteFile(file.name)
.then(() => deleted.push(file.id))
.catch(error => {
console.error(error)
failed.push(file[field])
})
if (!deleteFile) { return resolve() }
await db.table('files')
.where(field, file[field])
.del()
.then(() => {
if (file.albumid && !albumids.includes(file.albumid)) {
albumids.push(file.albumid)
}
})
.catch(error => {
console.error(error)
failed.push(file[field])
})
return resolve()
})
}))
if (!deleted.length) { return failed }
// Delete all files from database
const deleteDb = await db.table('files')
.whereIn('id', deleted)
.del()
.catch(console.error)
if (!deleteDb) { return failed }
const filtered = files.filter(file => deleted.includes(file.id))
// Update albums if necessary
if (albumids.length) {
await Promise.all(albumids.map(albumid => {
return db.table('albums')
.where('id', albumid)
.update('editedAt', Math.floor(Date.now() / 1000))
}))
if (deleteDb) {
const albumids = []
filtered.forEach(file => {
if (file.albumid && !albumids.includes(file.albumid)) {
albumids.push(file.albumid)
}
})
await db.table('albums')
.whereIn('id', albumids)
.update('editedAt', Math.floor(Date.now() / 1000))
.catch(console.error)
}
if (config.cloudflare.purgeCache) {
// purgeCloudflareCache() is an async function, but let us not wait for it
const names = files.filter(file => !failed.includes(file[field])).map(file => file.name)
const names = filtered.map(file => file.name)
utilsController.purgeCloudflareCache(names)
}

View File

@ -73,9 +73,11 @@ safe.use((error, req, res, next) => {
safe.listen(config.port, () => console.log(`lolisafe started on port ${config.port}`))
process.on('uncaughtException', error => {
console.error(`Uncaught Exception:\n${error}`)
console.error('Uncaught Exception:')
console.error(error)
})
process.on('unhandledRejection', error => {
console.error(`Unhandled Rejection (Promise):\n${error}`)
console.error('Unhandled Rejection (Promise):')
console.error(error)
})