mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2024-12-13 07:56:23 +00:00
Updates
config.sample.js + uploadController.js: + Added option uploads > storeIP to toggle whether to store uploader's IPs into the database. uploadController.js + dashboard.js: + Added IP column when listing all uploads. + Improved album query when listing uploads. In addition, no longer query album when listing all uploads. + Delegate some tasks to client when listing uploads to save server's processing power, kek. Such as building the file's full URLs, and assigning album/user names. _globals.njk: + Bumped v1 version string.
This commit is contained in:
parent
4bee2ef376
commit
f48cbd1960
@ -207,6 +207,13 @@ module.exports = {
|
|||||||
chunkSize: 64 * 1024
|
chunkSize: 64 * 1024
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Store uploader's IPs into the database.
|
||||||
|
NOTE: Dashboard's Manage Uploads will display IP column regardless of whether
|
||||||
|
this is set to true or false.
|
||||||
|
*/
|
||||||
|
storeIP: true,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Chunk size for chunk uploads. Needs to be in MB.
|
Chunk size for chunk uploads. Needs to be in MB.
|
||||||
If this is enabled, every files uploaded from the homepage uploader will forcibly be chunked
|
If this is enabled, every files uploaded from the homepage uploader will forcibly be chunked
|
||||||
|
@ -545,7 +545,7 @@ uploadsController.formatInfoMap = (req, res, user, infoMap) => {
|
|||||||
type: info.data.mimetype,
|
type: info.data.mimetype,
|
||||||
size: info.data.size,
|
size: info.data.size,
|
||||||
hash: fileHash,
|
hash: fileHash,
|
||||||
ip: req.ip,
|
ip: config.uploads.storeIP !== false ? req.ip : null, // only disable if explicitly set to false
|
||||||
albumid: albumsAuthorized[info.data.albumid] ? info.data.albumid : null,
|
albumid: albumsAuthorized[info.data.albumid] ? info.data.albumid : null,
|
||||||
userid: user !== undefined ? user.id : null,
|
userid: user !== undefined ? user.id : null,
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
@ -688,25 +688,33 @@ uploadsController.list = async (req, res) => {
|
|||||||
const ismoderator = perms.is(user, 'moderator')
|
const ismoderator = perms.is(user, 'moderator')
|
||||||
if ((all || uploader) && !ismoderator) return res.status(403).end()
|
if ((all || uploader) && !ismoderator) return res.status(403).end()
|
||||||
|
|
||||||
|
const basedomain = config.domain
|
||||||
|
|
||||||
|
// For filtering by uploader's username
|
||||||
let uploaderID = null
|
let uploaderID = null
|
||||||
if (uploader)
|
if (uploader) {
|
||||||
uploaderID = await db.table('users')
|
uploaderID = await db.table('users')
|
||||||
.where('username', uploader)
|
.where('username', uploader)
|
||||||
.select('id')
|
.select('id')
|
||||||
.first()
|
.first()
|
||||||
.then(row => row ? row.id : null)
|
.then(row => row ? row.id : null)
|
||||||
|
// Close request if the provided username is not valid
|
||||||
|
if (!uploaderID)
|
||||||
|
return res.json({ success: false, description: 'User with that username could not be found.' })
|
||||||
|
}
|
||||||
|
|
||||||
function filter () {
|
function filter () {
|
||||||
if (req.params.id === undefined)
|
if (req.params.id === undefined)
|
||||||
this.where('id', '<>', '')
|
this.where('id', '<>', '') // TODO: Why is this necessary?
|
||||||
else
|
else
|
||||||
this.where('albumid', req.params.id)
|
this.where('albumid', req.params.id)
|
||||||
if (!ismoderator || !all)
|
if (!all)
|
||||||
this.where('userid', user.id)
|
this.where('userid', user.id)
|
||||||
else if (uploaderID)
|
else if (uploaderID)
|
||||||
this.where('userid', uploaderID)
|
this.where('userid', uploaderID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query uploads count for pagination
|
||||||
const count = await db.table('files')
|
const count = await db.table('files')
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.count('id as count')
|
.count('id as count')
|
||||||
@ -716,55 +724,73 @@ uploadsController.list = async (req, res) => {
|
|||||||
let offset = req.params.page
|
let offset = req.params.page
|
||||||
if (offset === undefined) offset = 0
|
if (offset === undefined) offset = 0
|
||||||
|
|
||||||
|
const columns = ['id', 'timestamp', 'name', 'userid', 'size']
|
||||||
|
// Only select IPs if we are listing all uploads
|
||||||
|
columns.push(all ? 'ip' : 'albumid')
|
||||||
|
|
||||||
const files = await db.table('files')
|
const files = await db.table('files')
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.orderBy('id', 'DESC')
|
.orderBy('id', 'DESC')
|
||||||
.limit(25)
|
.limit(25)
|
||||||
.offset(25 * offset)
|
.offset(25 * offset)
|
||||||
.select('id', 'albumid', 'timestamp', 'name', 'userid', 'size')
|
.select(columns)
|
||||||
|
|
||||||
const albums = await db.table('albums')
|
|
||||||
.where(function () {
|
|
||||||
this.where('enabled', 1)
|
|
||||||
if (!all || !ismoderator)
|
|
||||||
this.where('userid', user.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
const basedomain = config.domain
|
|
||||||
const userids = []
|
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
file.file = `${basedomain}/${file.name}`
|
|
||||||
|
|
||||||
file.album = ''
|
|
||||||
if (file.albumid !== undefined)
|
|
||||||
for (const album of albums)
|
|
||||||
if (file.albumid === album.id)
|
|
||||||
file.album = album.name
|
|
||||||
|
|
||||||
// Only push usernames if we are a moderator
|
|
||||||
if (all && ismoderator)
|
|
||||||
if (file.userid !== undefined && file.userid !== null && file.userid !== '')
|
|
||||||
userids.push(file.userid)
|
|
||||||
|
|
||||||
file.extname = utils.extname(file.name)
|
file.extname = utils.extname(file.name)
|
||||||
if (utils.mayGenerateThumb(file.extname))
|
if (utils.mayGenerateThumb(file.extname))
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -file.extname.length)}.png`
|
file.thumb = `/thumbs/${file.name.slice(0, -file.extname.length)}.png`
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are a normal user, send response
|
// If we are not listing all uploads, query album names
|
||||||
if (!ismoderator) return res.json({ success: true, files, count })
|
let albums = {}
|
||||||
|
if (!all) {
|
||||||
|
const albumids = files
|
||||||
|
.map(file => file.albumid)
|
||||||
|
.filter((v, i, a) => {
|
||||||
|
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
||||||
|
})
|
||||||
|
albums = await db.table('albums')
|
||||||
|
.whereIn('id', albumids)
|
||||||
|
.where('enabled', 1)
|
||||||
|
.where('userid', user.id)
|
||||||
|
.select('id', 'name')
|
||||||
|
.then(rows => {
|
||||||
|
// Build Object indexed by their IDs
|
||||||
|
const obj = {}
|
||||||
|
for (const row of rows) obj[row.id] = row.name
|
||||||
|
return obj
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// If we are a moderator but there are no uploads attached to a user, send response
|
// If we are a regular user, or we are not listing all uploads, send response
|
||||||
if (userids.length === 0) return res.json({ success: true, files, count })
|
if (!ismoderator || !all) return res.json({ success: true, files, count, albums, basedomain })
|
||||||
|
|
||||||
const users = await db.table('users').whereIn('id', userids)
|
// Otherwise proceed to querying usernames
|
||||||
for (const dbUser of users)
|
let users = {}
|
||||||
for (const file of files)
|
if (uploaderID) {
|
||||||
if (file.userid === dbUser.id)
|
// If we are already filtering by username, manually build array
|
||||||
file.username = dbUser.username
|
users[uploaderID] = uploader
|
||||||
|
} else {
|
||||||
|
const userids = files
|
||||||
|
.map(file => file.userid)
|
||||||
|
.filter((v, i, a) => {
|
||||||
|
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
||||||
|
})
|
||||||
|
// If there are no uploads attached to a registered user, send response
|
||||||
|
if (userids.length === 0) return res.json({ success: true, files, count, basedomain })
|
||||||
|
|
||||||
return res.json({ success: true, files, count })
|
// Query usernames of user IDs from currently selected files
|
||||||
|
users = await db.table('users')
|
||||||
|
.whereIn('id', userids)
|
||||||
|
.then(rows => {
|
||||||
|
// Build Object indexed by their IDs
|
||||||
|
const obj = {}
|
||||||
|
for (const row of rows) obj[row.id] = row.username
|
||||||
|
return obj
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json({ success: true, files, count, users, basedomain })
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = uploadsController
|
module.exports = uploadsController
|
||||||
|
@ -203,7 +203,7 @@ page.getItemID = function (element) {
|
|||||||
page.domClick = function (event) {
|
page.domClick = function (event) {
|
||||||
// We are processing clicks this way to avoid using "onclick" attribute
|
// We are processing clicks this way to avoid using "onclick" attribute
|
||||||
// Apparently we will need to use "unsafe-inline" for "script-src" directive
|
// Apparently we will need to use "unsafe-inline" for "script-src" directive
|
||||||
// of Content Security Policy (CSP), if we want ot use "onclick" attribute
|
// of Content Security Policy (CSP), if we want to use "onclick" attribute
|
||||||
// Though I think that only applies to some browsers (?)
|
// Though I think that only applies to some browsers (?)
|
||||||
// Either way, I personally would rather not
|
// Either way, I personally would rather not
|
||||||
// Of course it wouldn't have mattered if we didn't use CSP to begin with
|
// Of course it wouldn't have mattered if we didn't use CSP to begin with
|
||||||
@ -342,11 +342,12 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
if (response.data.description === 'No token provided') {
|
if (response.data.description === 'No token provided') {
|
||||||
return page.verifyToken(page.token)
|
return page.verifyToken(page.token)
|
||||||
} else {
|
} else {
|
||||||
|
if (element) page.isLoading(element, false)
|
||||||
return swal('An error occurred!', response.data.description, 'error')
|
return swal('An error occurred!', response.data.description, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pageNum && (response.data.files.length === 0)) {
|
const files = response.data.files
|
||||||
// Only remove loading class here, since beyond this the entire page will be replaced anyways
|
if (pageNum && (files === 0)) {
|
||||||
if (element) page.isLoading(element, false)
|
if (element) page.isLoading(element, false)
|
||||||
return swal('An error occurred!', `There are no more uploads to populate page ${pageNum + 1}.`, 'error')
|
return swal('An error occurred!', `There are no more uploads to populate page ${pageNum + 1}.`, 'error')
|
||||||
}
|
}
|
||||||
@ -354,6 +355,9 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
page.currentView = all ? 'uploadsAll' : 'uploads'
|
page.currentView = all ? 'uploadsAll' : 'uploads'
|
||||||
page.cache.uploads = {}
|
page.cache.uploads = {}
|
||||||
|
|
||||||
|
const albums = response.data.albums
|
||||||
|
const users = response.data.users
|
||||||
|
const basedomain = response.data.basedomain
|
||||||
const pagination = page.paginate(response.data.count, 25, pageNum)
|
const pagination = page.paginate(response.data.count, 25, pageNum)
|
||||||
|
|
||||||
let filter = ''
|
let filter = ''
|
||||||
@ -432,7 +436,30 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// Set to true to tick "all files" checkbox in list view
|
||||||
let allSelected = true
|
let allSelected = true
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
// Build full URLs
|
||||||
|
files[i].file = `${basedomain}/${files[i].name}`
|
||||||
|
if (files[i].thumb) files[i].thumb = `${basedomain}/${files[i].thumb}`
|
||||||
|
// Cache bare minimum data for thumbnails viewer
|
||||||
|
page.cache.uploads[files[i].id] = {
|
||||||
|
name: files[i].name,
|
||||||
|
thumb: files[i].thumb,
|
||||||
|
original: files[i].file
|
||||||
|
}
|
||||||
|
// Prettify
|
||||||
|
files[i].prettyBytes = page.getPrettyBytes(parseInt(files[i].size))
|
||||||
|
files[i].prettyDate = page.getPrettyDate(new Date(files[i].timestamp * 1000))
|
||||||
|
// Update selected status
|
||||||
|
files[i].selected = page.selected[page.currentView].includes(files[i].id)
|
||||||
|
if (allSelected && !files[i].selected) allSelected = false
|
||||||
|
// Appendix (display album or user)
|
||||||
|
files[i].appendix = files[i].albumid ? albums[files[i].albumid] : ''
|
||||||
|
if (all) files[i].appendix = files[i].userid ? users[files[i].userid] : ''
|
||||||
|
}
|
||||||
|
|
||||||
if (page.views[page.currentView].type === 'thumbs') {
|
if (page.views[page.currentView].type === 'thumbs') {
|
||||||
page.dom.innerHTML = `
|
page.dom.innerHTML = `
|
||||||
${pagination}
|
${pagination}
|
||||||
@ -447,24 +474,8 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
|
|
||||||
const table = document.getElementById('table')
|
const table = document.getElementById('table')
|
||||||
|
|
||||||
for (let i = 0; i < response.data.files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const upload = response.data.files[i]
|
const upload = files[i]
|
||||||
const selected = page.selected[page.currentView].includes(upload.id)
|
|
||||||
if (!selected && allSelected) allSelected = false
|
|
||||||
|
|
||||||
page.cache.uploads[upload.id] = {
|
|
||||||
name: upload.name,
|
|
||||||
thumb: upload.thumb,
|
|
||||||
original: upload.file
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prettify
|
|
||||||
upload.prettyBytes = page.getPrettyBytes(parseInt(upload.size))
|
|
||||||
upload.prettyDate = page.getPrettyDate(new Date(upload.timestamp * 1000))
|
|
||||||
|
|
||||||
let displayAlbumOrUser = upload.album
|
|
||||||
if (all) displayAlbumOrUser = upload.username || ''
|
|
||||||
|
|
||||||
const div = document.createElement('div')
|
const div = document.createElement('div')
|
||||||
div.className = 'image-container column is-narrow'
|
div.className = 'image-container column is-narrow'
|
||||||
div.dataset.id = upload.id
|
div.dataset.id = upload.id
|
||||||
@ -474,7 +485,7 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
div.innerHTML = `<a class="image" href="${upload.file}" target="_blank" rel="noopener"><h1 class="title">${upload.extname || 'N/A'}</h1></a>`
|
div.innerHTML = `<a class="image" href="${upload.file}" target="_blank" rel="noopener"><h1 class="title">${upload.extname || 'N/A'}</h1></a>`
|
||||||
|
|
||||||
div.innerHTML += `
|
div.innerHTML += `
|
||||||
<input type="checkbox" class="checkbox" title="Select this file" data-action="select"${selected ? ' checked' : ''}>
|
<input type="checkbox" class="checkbox" title="Select this file" data-action="select"${upload.selected ? ' checked' : ''}>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<a class="button is-small is-primary" title="View thumbnail" data-action="display-thumbnail"${upload.thumb ? '' : ' disabled'}>
|
<a class="button is-small is-primary" title="View thumbnail" data-action="display-thumbnail"${upload.thumb ? '' : ' disabled'}>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
@ -499,7 +510,7 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<p><span class="name" title="${upload.file}">${upload.name}</span></p>
|
<p><span class="name" title="${upload.file}">${upload.name}</span></p>
|
||||||
<p>${displayAlbumOrUser ? `<span>${displayAlbumOrUser}</span> – ` : ''}${upload.prettyBytes}</p>
|
<p>${upload.appendix ? `<span>${upload.appendix}</span> – ` : ''}${upload.prettyBytes}</p>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -508,9 +519,6 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
page.lazyLoad.update()
|
page.lazyLoad.update()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let albumOrUser = 'Album'
|
|
||||||
if (all) albumOrUser = 'User'
|
|
||||||
|
|
||||||
page.dom.innerHTML = `
|
page.dom.innerHTML = `
|
||||||
${pagination}
|
${pagination}
|
||||||
${extraControls}
|
${extraControls}
|
||||||
@ -521,8 +529,9 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
<tr>
|
<tr>
|
||||||
<th><input id="selectAll" class="checkbox" type="checkbox" title="Select all uploads" data-action="select-all"></th>
|
<th><input id="selectAll" class="checkbox" type="checkbox" title="Select all uploads" data-action="select-all"></th>
|
||||||
<th style="width: 25%">File</th>
|
<th style="width: 25%">File</th>
|
||||||
<th>${albumOrUser}</th>
|
<th>${all ? 'User' : 'Album'}</th>
|
||||||
<th>Size</th>
|
<th>Size</th>
|
||||||
|
${all ? '<th>IP</th>' : ''}
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -538,31 +547,16 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
|
|
||||||
const table = document.getElementById('table')
|
const table = document.getElementById('table')
|
||||||
|
|
||||||
for (let i = 0; i < response.data.files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const upload = response.data.files[i]
|
const upload = files[i]
|
||||||
const selected = page.selected[page.currentView].includes(upload.id)
|
|
||||||
if (!selected && allSelected) allSelected = false
|
|
||||||
|
|
||||||
page.cache.uploads[upload.id] = {
|
|
||||||
name: upload.name,
|
|
||||||
thumb: upload.thumb,
|
|
||||||
original: upload.file
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prettify
|
|
||||||
upload.prettyBytes = page.getPrettyBytes(parseInt(upload.size))
|
|
||||||
upload.prettyDate = page.getPrettyDate(new Date(upload.timestamp * 1000))
|
|
||||||
|
|
||||||
let displayAlbumOrUser = upload.album
|
|
||||||
if (all) displayAlbumOrUser = upload.username || ''
|
|
||||||
|
|
||||||
const tr = document.createElement('tr')
|
const tr = document.createElement('tr')
|
||||||
tr.dataset.id = upload.id
|
tr.dataset.id = upload.id
|
||||||
tr.innerHTML = `
|
tr.innerHTML = `
|
||||||
<td class="controls"><input type="checkbox" class="checkbox" title="Select this file" data-action="select"${selected ? ' checked' : ''}></td>
|
<td class="controls"><input type="checkbox" class="checkbox" title="Select this file" data-action="select"${upload.selected ? ' checked' : ''}></td>
|
||||||
<th><a href="${upload.file}" target="_blank" rel="noopener" title="${upload.file}">${upload.name}</a></th>
|
<th><a href="${upload.file}" target="_blank" rel="noopener" title="${upload.file}">${upload.name}</a></th>
|
||||||
<th>${displayAlbumOrUser}</th>
|
<th>${upload.appendix}</th>
|
||||||
<td>${upload.prettyBytes}</td>
|
<td>${upload.prettyBytes}</td>
|
||||||
|
${all ? `<td>${upload.ip || ''}</td>` : ''}
|
||||||
<td>${upload.prettyDate}</td>
|
<td>${upload.prettyDate}</td>
|
||||||
<td class="controls" style="text-align: right">
|
<td class="controls" style="text-align: right">
|
||||||
<a class="button is-small is-primary" title="View thumbnail" data-action="display-thumbnail"${upload.thumb ? '' : ' disabled'}>
|
<a class="button is-small is-primary" title="View thumbnail" data-action="display-thumbnail"${upload.thumb ? '' : ' disabled'}>
|
||||||
@ -594,14 +588,14 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allSelected && response.data.files.length) {
|
if (allSelected && files.length) {
|
||||||
const selectAll = document.getElementById('selectAll')
|
const selectAll = document.getElementById('selectAll')
|
||||||
if (selectAll) selectAll.checked = true
|
if (selectAll) selectAll.checked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page.currentView === 'uploads') page.views.uploads.album = album
|
if (page.currentView === 'uploads') page.views.uploads.album = album
|
||||||
if (page.currentView === 'uploadsAll') page.views.uploadsAll.uploader = uploader
|
if (page.currentView === 'uploadsAll') page.views.uploadsAll.uploader = uploader
|
||||||
page.views[page.currentView].pageNum = response.data.files.length ? pageNum : 0
|
page.views[page.currentView].pageNum = files.length ? pageNum : 0
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
if (element) page.isLoading(element, false)
|
if (element) page.isLoading(element, false)
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
v3: CSS and JS files (libs such as bulma, lazyload, etc).
|
v3: CSS and JS files (libs such as bulma, lazyload, etc).
|
||||||
v4: Renders in /public/render/* directories (to be used by render.js).
|
v4: Renders in /public/render/* directories (to be used by render.js).
|
||||||
#}
|
#}
|
||||||
{% set v1 = "WNcjDAmPAR" %}
|
{% set v1 = "uDNOxxQGxC" %}
|
||||||
{% set v2 = "hiboQUzAzp" %}
|
{% set v2 = "hiboQUzAzp" %}
|
||||||
{% set v3 = "DKoamSTKbO" %}
|
{% set v3 = "DKoamSTKbO" %}
|
||||||
{% set v4 = "43gxmxi7v8" %}
|
{% set v4 = "43gxmxi7v8" %}
|
||||||
|
Loading…
Reference in New Issue
Block a user