mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2024-12-13 16:06:21 +00:00
Updates (dashboard)
+ Better pagination. + Added more advanced filtering system in Manage Uploads. It now supports filtering with multiple usernames and/or IPs. It also supports refining the matches with wildcards. Todo? Perhaps add simple file name filtering for regular users in the future?
This commit is contained in:
parent
ec6069d962
commit
06ac31d02e
@ -684,34 +684,86 @@ uploadsController.list = async (req, res) => {
|
||||
|
||||
// Headers is string-only, this seem to be the safest and lightest
|
||||
const all = req.headers.all === '1'
|
||||
const uploader = req.headers.uploader
|
||||
const filters = req.headers.filters
|
||||
const ismoderator = perms.is(user, 'moderator')
|
||||
if ((all || uploader) && !ismoderator) return res.status(403).end()
|
||||
if ((all || filters) && !ismoderator) return res.status(403).end()
|
||||
|
||||
const basedomain = config.domain
|
||||
|
||||
// For filtering by uploader's username
|
||||
let uploaderID = null
|
||||
if (uploader) {
|
||||
uploaderID = await db.table('users')
|
||||
.where('username', uploader)
|
||||
// For filtering uploads
|
||||
const _filters = {
|
||||
uploaders: [],
|
||||
names: [],
|
||||
ips: [],
|
||||
flags: {
|
||||
nouser: false,
|
||||
noip: false
|
||||
}
|
||||
}
|
||||
|
||||
// Perhaps this can be simplified even further?
|
||||
if (filters) {
|
||||
const usernames = []
|
||||
filters
|
||||
.split(' ')
|
||||
.map((v, i, a) => {
|
||||
if (/[^\\]\\$/.test(v) && a[i + 1]) {
|
||||
const tmp = `${v.slice(0, -1)} ${a[i + 1]}`
|
||||
a[i + 1] = ''
|
||||
return tmp
|
||||
}
|
||||
return v.replace(/\\\\/, '\\')
|
||||
})
|
||||
.map((v, i) => {
|
||||
const x = v.indexOf(':')
|
||||
if (x >= 0 && v.substring(x + 1))
|
||||
return [v.substring(0, x), v.substring(x + 1)]
|
||||
else if (v.startsWith('-'))
|
||||
return [v]
|
||||
})
|
||||
.forEach(v => {
|
||||
if (!v) return
|
||||
if (v[0] === 'user') usernames.push(v[1])
|
||||
else if (v[0] === 'name') _filters.names.push(v[1])
|
||||
else if (v[0] === 'ip') _filters.ips.push(v[1])
|
||||
else if (v[0] === '-user') _filters.flags.nouser = true
|
||||
else if (v[0] === '-ip') _filters.flags.noip = true
|
||||
})
|
||||
_filters.uploaders = await db.table('users')
|
||||
.whereIn('username', usernames)
|
||||
.select('id')
|
||||
.first()
|
||||
.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.' })
|
||||
.then(rows => rows.map(v => v.id))
|
||||
}
|
||||
|
||||
function filter () {
|
||||
if (req.params.id === undefined)
|
||||
this.where('id', '<>', '') // TODO: Why is this necessary?
|
||||
else
|
||||
if (req.params.id !== undefined) {
|
||||
this.where('albumid', req.params.id)
|
||||
if (!all)
|
||||
} else if (!all) {
|
||||
this.where('userid', user.id)
|
||||
else if (uploaderID)
|
||||
this.where('userid', uploaderID)
|
||||
} else {
|
||||
// Fisrt, look for uploads matching ANY of the supplied 'user' OR 'ip' filters
|
||||
// Then, refined the matches using the supplied 'name' filters
|
||||
const raw = []
|
||||
const source = []
|
||||
if (_filters.uploaders.length)
|
||||
source.push(`\`userid\` in (${_filters.uploaders.map(v => `'${v}'`).join(', ')})`)
|
||||
if (_filters.ips.length)
|
||||
source.push(`\`ip\` in (${_filters.ips.map(v => `'${v}'`).join(', ')})`)
|
||||
if (_filters.flags.nouser)
|
||||
source.push('(`userid` is null or \'\')')
|
||||
if (_filters.flags.noip)
|
||||
source.push('(`ip` is null or \'\')')
|
||||
if (source.length)
|
||||
raw.push(`(${source.join(' or ')})`)
|
||||
if (_filters.names.length)
|
||||
raw.push(`(${_filters.names.map(v => {
|
||||
if (v.includes('*'))
|
||||
return `\`name\` like '${v.replace(/\*/g, '%')}'`
|
||||
else
|
||||
return `\`name\` = '${v}'`
|
||||
}).join(' or ')})`)
|
||||
this.whereRaw(raw.join(' and '))
|
||||
}
|
||||
}
|
||||
|
||||
// Query uploads count for pagination
|
||||
@ -763,32 +815,29 @@ uploadsController.list = async (req, res) => {
|
||||
}
|
||||
|
||||
// If we are a regular user, or we are not listing all uploads, send response
|
||||
// TODO: !ismoderator is probably redundant (?)
|
||||
if (!ismoderator || !all) return res.json({ success: true, files, count, albums, basedomain })
|
||||
|
||||
// Otherwise proceed to querying usernames
|
||||
let users = {}
|
||||
if (uploaderID) {
|
||||
// If we are already filtering by username, manually build array
|
||||
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 })
|
||||
|
||||
// Query usernames of user IDs from currently selected files
|
||||
users = await db.table('users')
|
||||
const users = await db.table('users')
|
||||
.whereIn('id', userids)
|
||||
.select('id', 'username')
|
||||
.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 })
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ const page = {
|
||||
// config of uploads view (all)
|
||||
uploadsAll: {
|
||||
type: localStorage[lsKeys.viewType.uploadsAll],
|
||||
uploader: null, // uploader's name
|
||||
filters: null, // uploads' filters
|
||||
pageNum: null, // page num
|
||||
all: true
|
||||
},
|
||||
@ -259,8 +259,10 @@ page.domClick = function (event) {
|
||||
return page.editUser(id)
|
||||
case 'disable-user':
|
||||
return page.disableUser(id)
|
||||
case 'filter-by-uploader':
|
||||
return page.filterByUploader(element)
|
||||
case 'filters-help':
|
||||
return page.filtersHelp(element)
|
||||
case 'filter-uploads':
|
||||
return page.filterUploads(element)
|
||||
case 'view-user-uploads':
|
||||
return page.viewUserUploads(id)
|
||||
case 'page-prev':
|
||||
@ -298,7 +300,7 @@ page.switchPage = function (action, element) {
|
||||
func = page.getUploads
|
||||
views.album = page.views[page.currentView].album
|
||||
views.all = page.views[page.currentView].all
|
||||
views.uploader = page.views[page.currentView].uploader
|
||||
views.filters = page.views[page.currentView].filters
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
@ -321,10 +323,10 @@ page.switchPage = function (action, element) {
|
||||
}
|
||||
}
|
||||
|
||||
page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
||||
page.getUploads = function ({ pageNum, album, all, filters } = {}, element) {
|
||||
if (element) page.isLoading(element, true)
|
||||
|
||||
if ((all || uploader) && !page.permissions.moderator)
|
||||
if ((all || filters) && !page.permissions.moderator)
|
||||
return swal('An error occurred!', 'You can not do this!', 'error')
|
||||
|
||||
if (typeof pageNum !== 'number' || pageNum < 0)
|
||||
@ -336,7 +338,7 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
||||
|
||||
const headers = {}
|
||||
if (all) headers.all = '1'
|
||||
if (uploader) headers.uploader = uploader
|
||||
if (filters) headers.filters = filters
|
||||
axios.get(url, { headers }).then(function (response) {
|
||||
if (response.data.success === false)
|
||||
if (response.data.description === 'No token provided') {
|
||||
@ -360,16 +362,23 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
||||
const basedomain = response.data.basedomain
|
||||
const pagination = page.paginate(response.data.count, 25, pageNum)
|
||||
|
||||
let filter = ''
|
||||
let filter = '<div class="column is-hidden-mobile"></div>'
|
||||
if (all)
|
||||
filter = `
|
||||
<div class="column is-one-quarter">
|
||||
<div class="column">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input id="uploader" class="input is-small" type="text" placeholder="Username" value="${uploader || ''}">
|
||||
<input id="filters" class="input is-small" type="text" placeholder="Filters" value="${filters || ''}">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-small is-breeze" title="Filter by uploader" data-action="filter-by-uploader">
|
||||
<a class="button is-small is-breeze" title="Help?" data-action="filters-help">
|
||||
<span class="icon">
|
||||
<i class="icon-help-circled"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-small is-breeze" title="Filter uploads" data-action="filter-uploads">
|
||||
<span class="icon">
|
||||
<i class="icon-filter"></i>
|
||||
</span>
|
||||
@ -381,7 +390,6 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
||||
const extraControls = `
|
||||
<div class="columns" style="margin-top: 10px">
|
||||
${filter}
|
||||
<div class="column is-hidden-mobile"></div>
|
||||
<div class="column is-one-quarter">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
@ -456,8 +464,8 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
||||
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] : ''
|
||||
else files[i].appendix = files[i].albumid ? albums[files[i].albumid] : ''
|
||||
}
|
||||
|
||||
if (page.views[page.currentView].type === 'thumbs') {
|
||||
@ -594,7 +602,7 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
||||
}
|
||||
|
||||
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.filters = filters
|
||||
page.views[page.currentView].pageNum = files.length ? pageNum : 0
|
||||
}).catch(function (error) {
|
||||
if (element) page.isLoading(element, false)
|
||||
@ -770,16 +778,46 @@ page.clearSelection = function () {
|
||||
})
|
||||
}
|
||||
|
||||
page.filterByUploader = function (element) {
|
||||
const uploader = document.getElementById('uploader').value
|
||||
page.getUploads({ all: true, uploader }, element)
|
||||
page.filtersHelp = function (element) {
|
||||
const content = document.createElement('div')
|
||||
content.style = 'text-align: left'
|
||||
content.innerHTML = `
|
||||
This supports 3 filter keys, namely <b>user</b> (username), <b>ip</b> and <b>name</b> (file name).
|
||||
Each key can be specified more than once.
|
||||
Backlashes should be used if the usernames have spaces.
|
||||
There are also 2 additional flags, namely <b>-user</b> and <b>-ip</b>, which will match uploads by non-registered users and have no IPs respectively.
|
||||
|
||||
How does it work?
|
||||
First, it will filter uploads matching ANY of the supplied <b>user</b> or <b>ip</b> keys.
|
||||
Then, it will refine the matches using the supplied <b>name</b> keys.
|
||||
|
||||
Examples:
|
||||
|
||||
Uploads from user with username "demo":
|
||||
<span class="is-code">user:demo</span>
|
||||
|
||||
Uploads from users with username either "John Doe" OR "demo":
|
||||
<span class="is-code">user:John\\ Doe user:demo</span>
|
||||
|
||||
Uploads from IP "127.0.0.1" AND which file names match "*.rar" OR "*.zip":
|
||||
<span class="is-code">ip:127.0.0.1 name:*.rar name:*.zip</span>
|
||||
|
||||
Uploads from user with username "test" OR from non-registered users:
|
||||
<span class="is-code">user:test -user</span>
|
||||
`.trim().replace(/^ {6}/gm, '').replace(/\n/g, '<br>')
|
||||
swal({ content })
|
||||
}
|
||||
|
||||
page.filterUploads = function (element) {
|
||||
const filters = document.getElementById('filters').value
|
||||
page.getUploads({ all: true, filters }, element)
|
||||
}
|
||||
|
||||
page.viewUserUploads = function (id) {
|
||||
const user = page.cache.users[id]
|
||||
if (!user) return
|
||||
page.setActiveMenu(document.getElementById('itemManageUploads'))
|
||||
page.getUploads({ all: true, uploader: user.username })
|
||||
page.getUploads({ all: true, filters: `user:${user.username.replace(/ /g, '\\ ')}` })
|
||||
}
|
||||
|
||||
page.deleteFile = function (id) {
|
||||
@ -1956,17 +1994,17 @@ page.paginate = function (totalItems, itemsPerPage, currentPage) {
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsToShow >= numPages) {
|
||||
if (elementsToShow + 1 >= numPages) {
|
||||
add.pageNum(1, numPages)
|
||||
} else if (currentPage < elementsToShow) {
|
||||
add.pageNum(1, elementsToShow)
|
||||
add.endDots()
|
||||
} else if (currentPage > numPages - elementsToShow) {
|
||||
} else if (currentPage > numPages - elementsToShow + 1) {
|
||||
add.startDots()
|
||||
add.pageNum(numPages - elementsToShow, numPages)
|
||||
add.pageNum(numPages - elementsToShow + 1, numPages)
|
||||
} else {
|
||||
add.startDots()
|
||||
add.pageNum(currentPage - step, currentPage, step)
|
||||
add.pageNum(currentPage - step + 1, currentPage + step - 1)
|
||||
add.endDots()
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
v3: CSS and JS files (libs such as bulma, lazyload, etc).
|
||||
v4: Renders in /public/render/* directories (to be used by render.js).
|
||||
#}
|
||||
{% set v1 = "uDNOxxQGxC" %}
|
||||
{% set v1 = "sAfUXhJ9Gz" %}
|
||||
{% set v2 = "hiboQUzAzp" %}
|
||||
{% set v3 = "DKoamSTKbO" %}
|
||||
{% set v4 = "43gxmxi7v8" %}
|
||||
|
Loading…
Reference in New Issue
Block a user