mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2024-12-13 07:56:23 +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
|
// Headers is string-only, this seem to be the safest and lightest
|
||||||
const all = req.headers.all === '1'
|
const all = req.headers.all === '1'
|
||||||
const uploader = req.headers.uploader
|
const filters = req.headers.filters
|
||||||
const ismoderator = perms.is(user, 'moderator')
|
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
|
const basedomain = config.domain
|
||||||
|
|
||||||
// For filtering by uploader's username
|
// For filtering uploads
|
||||||
let uploaderID = null
|
const _filters = {
|
||||||
if (uploader) {
|
uploaders: [],
|
||||||
uploaderID = await db.table('users')
|
names: [],
|
||||||
.where('username', uploader)
|
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')
|
.select('id')
|
||||||
.first()
|
.then(rows => rows.map(v => v.id))
|
||||||
.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', '<>', '') // TODO: Why is this necessary?
|
|
||||||
else
|
|
||||||
this.where('albumid', req.params.id)
|
this.where('albumid', req.params.id)
|
||||||
if (!all)
|
} else if (!all) {
|
||||||
this.where('userid', user.id)
|
this.where('userid', user.id)
|
||||||
else if (uploaderID)
|
} else {
|
||||||
this.where('userid', uploaderID)
|
// 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
|
// 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
|
// 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 })
|
if (!ismoderator || !all) return res.json({ success: true, files, count, albums, basedomain })
|
||||||
|
|
||||||
// Otherwise proceed to querying usernames
|
// 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
|
const userids = files
|
||||||
.map(file => file.userid)
|
.map(file => file.userid)
|
||||||
.filter((v, i, a) => {
|
.filter((v, i, a) => {
|
||||||
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
||||||
})
|
})
|
||||||
|
|
||||||
// If there are no uploads attached to a registered user, send response
|
// If there are no uploads attached to a registered user, send response
|
||||||
if (userids.length === 0) return res.json({ success: true, files, count, basedomain })
|
if (userids.length === 0) return res.json({ success: true, files, count, basedomain })
|
||||||
|
|
||||||
// Query usernames of user IDs from currently selected files
|
// Query usernames of user IDs from currently selected files
|
||||||
users = await db.table('users')
|
const users = await db.table('users')
|
||||||
.whereIn('id', userids)
|
.whereIn('id', userids)
|
||||||
|
.select('id', 'username')
|
||||||
.then(rows => {
|
.then(rows => {
|
||||||
// Build Object indexed by their IDs
|
// Build Object indexed by their IDs
|
||||||
const obj = {}
|
const obj = {}
|
||||||
for (const row of rows) obj[row.id] = row.username
|
for (const row of rows) obj[row.id] = row.username
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return res.json({ success: true, files, count, users, basedomain })
|
return res.json({ success: true, files, count, users, basedomain })
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ const page = {
|
|||||||
// config of uploads view (all)
|
// config of uploads view (all)
|
||||||
uploadsAll: {
|
uploadsAll: {
|
||||||
type: localStorage[lsKeys.viewType.uploadsAll],
|
type: localStorage[lsKeys.viewType.uploadsAll],
|
||||||
uploader: null, // uploader's name
|
filters: null, // uploads' filters
|
||||||
pageNum: null, // page num
|
pageNum: null, // page num
|
||||||
all: true
|
all: true
|
||||||
},
|
},
|
||||||
@ -259,8 +259,10 @@ page.domClick = function (event) {
|
|||||||
return page.editUser(id)
|
return page.editUser(id)
|
||||||
case 'disable-user':
|
case 'disable-user':
|
||||||
return page.disableUser(id)
|
return page.disableUser(id)
|
||||||
case 'filter-by-uploader':
|
case 'filters-help':
|
||||||
return page.filterByUploader(element)
|
return page.filtersHelp(element)
|
||||||
|
case 'filter-uploads':
|
||||||
|
return page.filterUploads(element)
|
||||||
case 'view-user-uploads':
|
case 'view-user-uploads':
|
||||||
return page.viewUserUploads(id)
|
return page.viewUserUploads(id)
|
||||||
case 'page-prev':
|
case 'page-prev':
|
||||||
@ -298,7 +300,7 @@ page.switchPage = function (action, element) {
|
|||||||
func = page.getUploads
|
func = page.getUploads
|
||||||
views.album = page.views[page.currentView].album
|
views.album = page.views[page.currentView].album
|
||||||
views.all = page.views[page.currentView].all
|
views.all = page.views[page.currentView].all
|
||||||
views.uploader = page.views[page.currentView].uploader
|
views.filters = page.views[page.currentView].filters
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
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 (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')
|
return swal('An error occurred!', 'You can not do this!', 'error')
|
||||||
|
|
||||||
if (typeof pageNum !== 'number' || pageNum < 0)
|
if (typeof pageNum !== 'number' || pageNum < 0)
|
||||||
@ -336,7 +338,7 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
|
|
||||||
const headers = {}
|
const headers = {}
|
||||||
if (all) headers.all = '1'
|
if (all) headers.all = '1'
|
||||||
if (uploader) headers.uploader = uploader
|
if (filters) headers.filters = filters
|
||||||
axios.get(url, { headers }).then(function (response) {
|
axios.get(url, { headers }).then(function (response) {
|
||||||
if (response.data.success === false)
|
if (response.data.success === false)
|
||||||
if (response.data.description === 'No token provided') {
|
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 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 = '<div class="column is-hidden-mobile"></div>'
|
||||||
if (all)
|
if (all)
|
||||||
filter = `
|
filter = `
|
||||||
<div class="column is-one-quarter">
|
<div class="column">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<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>
|
||||||
<div class="control">
|
<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">
|
<span class="icon">
|
||||||
<i class="icon-filter"></i>
|
<i class="icon-filter"></i>
|
||||||
</span>
|
</span>
|
||||||
@ -381,7 +390,6 @@ page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) {
|
|||||||
const extraControls = `
|
const extraControls = `
|
||||||
<div class="columns" style="margin-top: 10px">
|
<div class="columns" style="margin-top: 10px">
|
||||||
${filter}
|
${filter}
|
||||||
<div class="column is-hidden-mobile"></div>
|
|
||||||
<div class="column is-one-quarter">
|
<div class="column is-one-quarter">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<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)
|
files[i].selected = page.selected[page.currentView].includes(files[i].id)
|
||||||
if (allSelected && !files[i].selected) allSelected = false
|
if (allSelected && !files[i].selected) allSelected = false
|
||||||
// Appendix (display album or user)
|
// 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 (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') {
|
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 === '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
|
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)
|
||||||
@ -770,16 +778,46 @@ page.clearSelection = function () {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
page.filterByUploader = function (element) {
|
page.filtersHelp = function (element) {
|
||||||
const uploader = document.getElementById('uploader').value
|
const content = document.createElement('div')
|
||||||
page.getUploads({ all: true, uploader }, element)
|
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) {
|
page.viewUserUploads = function (id) {
|
||||||
const user = page.cache.users[id]
|
const user = page.cache.users[id]
|
||||||
if (!user) return
|
if (!user) return
|
||||||
page.setActiveMenu(document.getElementById('itemManageUploads'))
|
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) {
|
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)
|
add.pageNum(1, numPages)
|
||||||
} else if (currentPage < elementsToShow) {
|
} else if (currentPage < elementsToShow) {
|
||||||
add.pageNum(1, elementsToShow)
|
add.pageNum(1, elementsToShow)
|
||||||
add.endDots()
|
add.endDots()
|
||||||
} else if (currentPage > numPages - elementsToShow) {
|
} else if (currentPage > numPages - elementsToShow + 1) {
|
||||||
add.startDots()
|
add.startDots()
|
||||||
add.pageNum(numPages - elementsToShow, numPages)
|
add.pageNum(numPages - elementsToShow + 1, numPages)
|
||||||
} else {
|
} else {
|
||||||
add.startDots()
|
add.startDots()
|
||||||
add.pageNum(currentPage - step, currentPage, step)
|
add.pageNum(currentPage - step + 1, currentPage + step - 1)
|
||||||
add.endDots()
|
add.endDots()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 = "uDNOxxQGxC" %}
|
{% set v1 = "sAfUXhJ9Gz" %}
|
||||||
{% 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