mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-18 17:21:33 +00:00
Updates
Added pagination to uploads and users list. With that, /api/uploads and /api/users API routes will now add "count" property to their response object. Enabled Delete user button in users list. With that also added /api/users/disable API route. As usual, you can only disable users whose usergroup is lower than your own. Click event will no longer trigger on "disabled" elements (basically any elements with "disabled" attribute). Changed all arrow functions into regular functions in public JS files (there were only a few that I somehow missed). Bumped v1 version string.
This commit is contained in:
parent
c0d09c395c
commit
31a6940ab4
@ -159,23 +159,29 @@ authController.editUser = async (req, res, next) => {
|
||||
else if (target.username === 'root')
|
||||
return res.json({ success: false, description: 'Root user may not be edited.' })
|
||||
|
||||
const username = `${req.body.username}`
|
||||
if (username.length < 4 || username.length > 32)
|
||||
return res.json({ success: false, description: 'Username must have 4-32 characters.' })
|
||||
const update = {}
|
||||
|
||||
let permission = req.body.group ? perms.permissions[req.body.group] : target.permission
|
||||
if (typeof permission !== 'number' || permission < 0) permission = target.permission
|
||||
if (req.body.username !== undefined) {
|
||||
update.username = `${req.body.username}`
|
||||
if (update.username.length < 4 || update.username.length > 32)
|
||||
return res.json({ success: false, description: 'Username must have 4-32 characters.' })
|
||||
}
|
||||
|
||||
if (req.body.enabled !== undefined)
|
||||
update.enabled = Boolean(req.body.enabled)
|
||||
|
||||
if (req.body.group !== undefined) {
|
||||
update.permission = perms.permissions[req.body.group] || target.permission
|
||||
if (typeof update.permission !== 'number' || update.permission < 0)
|
||||
update.permission = target.permission
|
||||
}
|
||||
|
||||
await db.table('users')
|
||||
.where('id', id)
|
||||
.update({
|
||||
username,
|
||||
enabled: Boolean(req.body.enabled),
|
||||
permission
|
||||
})
|
||||
.update(update)
|
||||
|
||||
if (!req.body.resetPassword)
|
||||
return res.json({ success: true, username })
|
||||
return res.json({ success: true, update })
|
||||
|
||||
const password = randomstring.generate(16)
|
||||
bcrypt.hash(password, 10, async (error, hash) => {
|
||||
@ -188,10 +194,19 @@ authController.editUser = async (req, res, next) => {
|
||||
.where('id', id)
|
||||
.update('password', hash)
|
||||
|
||||
return res.json({ success: true, password })
|
||||
return res.json({ success: true, update, password })
|
||||
})
|
||||
}
|
||||
|
||||
authController.disableUser = async (req, res, next) => {
|
||||
const body = {
|
||||
id: req.body.id,
|
||||
enabled: false
|
||||
}
|
||||
req.body = body
|
||||
return authController.editUser(req, res, next)
|
||||
}
|
||||
|
||||
authController.listUsers = async (req, res, next) => {
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) return
|
||||
@ -199,17 +214,19 @@ authController.listUsers = async (req, res, next) => {
|
||||
const isadmin = perms.is(user, 'admin')
|
||||
if (!isadmin) return res.status(403).end()
|
||||
|
||||
const count = await db.table('users')
|
||||
.count('id as count')
|
||||
.then(rows => rows[0].count)
|
||||
if (!count) return res.json({ success: true, users: [], count })
|
||||
|
||||
let offset = req.params.page
|
||||
if (offset === undefined) offset = 0
|
||||
|
||||
const users = await db.table('users')
|
||||
// .orderBy('id', 'DESC')
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
.select('id', 'username', 'enabled', 'fileLength', 'permission')
|
||||
|
||||
if (!users.length) return res.json({ success: true, users })
|
||||
|
||||
const userids = []
|
||||
|
||||
for (const user of users) {
|
||||
@ -242,7 +259,7 @@ authController.listUsers = async (req, res, next) => {
|
||||
user.diskUsage = maps[user.id].size
|
||||
}
|
||||
|
||||
return res.json({ success: true, users })
|
||||
return res.json({ success: true, users, count })
|
||||
}
|
||||
|
||||
module.exports = authController
|
||||
|
@ -660,9 +660,11 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
|
||||
|
||||
uploadsController.delete = async (req, res) => {
|
||||
const id = parseInt(req.body.id)
|
||||
req.body.field = 'id'
|
||||
req.body.values = isNaN(id) ? undefined : [id]
|
||||
delete req.body.id
|
||||
const body = {
|
||||
field: 'id',
|
||||
values: isNaN(id) ? undefined : [id]
|
||||
}
|
||||
req.body = body
|
||||
return uploadsController.bulkDelete(req, res)
|
||||
}
|
||||
|
||||
@ -687,25 +689,31 @@ uploadsController.list = async (req, res) => {
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) return
|
||||
|
||||
let offset = req.params.page
|
||||
if (offset === undefined) offset = 0
|
||||
|
||||
// Headers is string-only, this seem to be the safest and lightest
|
||||
const all = req.headers.all === '1'
|
||||
const ismoderator = perms.is(user, 'moderator')
|
||||
if (all && !ismoderator) return res.status(403).end()
|
||||
|
||||
function filter () {
|
||||
if (req.params.id === undefined)
|
||||
this.where('id', '<>', '')
|
||||
else
|
||||
this.where('albumid', req.params.id)
|
||||
if (!all || !ismoderator)
|
||||
this.where('userid', user.id)
|
||||
}
|
||||
|
||||
const count = await db.table('files')
|
||||
.where(filter)
|
||||
.count('id as count')
|
||||
.then(rows => rows[0].count)
|
||||
if (!count) return res.json({ success: true, files: [], count })
|
||||
|
||||
let offset = req.params.page
|
||||
if (offset === undefined) offset = 0
|
||||
|
||||
const files = await db.table('files')
|
||||
.where(function () {
|
||||
if (req.params.id === undefined)
|
||||
this.where('id', '<>', '')
|
||||
else
|
||||
this.where('albumid', req.params.id)
|
||||
})
|
||||
.where(function () {
|
||||
if (!all || !ismoderator)
|
||||
this.where('userid', user.id)
|
||||
})
|
||||
.where(filter)
|
||||
.orderBy('id', 'DESC')
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
@ -741,10 +749,10 @@ uploadsController.list = async (req, res) => {
|
||||
}
|
||||
|
||||
// If we are a normal user, send response
|
||||
if (!ismoderator) return res.json({ success: true, files })
|
||||
if (!ismoderator) return res.json({ success: true, files, count })
|
||||
|
||||
// If we are a moderator but there are no uploads attached to a user, send response
|
||||
if (userids.length === 0) return res.json({ success: true, files })
|
||||
if (userids.length === 0) return res.json({ success: true, files, count })
|
||||
|
||||
const users = await db.table('users').whereIn('id', userids)
|
||||
for (const dbUser of users)
|
||||
@ -752,7 +760,7 @@ uploadsController.list = async (req, res) => {
|
||||
if (file.userid === dbUser.id)
|
||||
file.username = dbUser.username
|
||||
|
||||
return res.json({ success: true, files })
|
||||
return res.json({ success: true, files, count })
|
||||
}
|
||||
|
||||
module.exports = uploadsController
|
||||
|
@ -142,3 +142,14 @@ hr {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-link.is-current {
|
||||
background-color: #3794d2;
|
||||
border-color: #3794d2;
|
||||
}
|
||||
|
||||
.pagination-link:hover,
|
||||
.pagination-next:hover,
|
||||
.pagination-previous:hover {
|
||||
border-color: #3794d2;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ const page = {
|
||||
byteUnits: ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
}
|
||||
|
||||
page.getPrettyBytes = num => {
|
||||
page.getPrettyBytes = function (num) {
|
||||
// MIT License
|
||||
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
|
@ -196,12 +196,15 @@ page.domClick = function (event) {
|
||||
// Skip elements that have no action data
|
||||
if (!element.dataset || !element.dataset.action) return
|
||||
|
||||
// Skip disabled elements
|
||||
if (element.hasAttribute('disabled')) return
|
||||
|
||||
event.stopPropagation() // maybe necessary
|
||||
const id = page.getItemID(element)
|
||||
const action = element.dataset.action
|
||||
|
||||
// Handle pagination actions
|
||||
if (['page-prev', 'page-next'].includes(action)) {
|
||||
if (['page-prev', 'page-next', 'page-goto'].includes(action)) {
|
||||
const views = {}
|
||||
let func = null
|
||||
|
||||
@ -223,6 +226,9 @@ page.domClick = function (event) {
|
||||
case 'page-next':
|
||||
views.pageNum = page.views[page.currentView].pageNum + 1
|
||||
return func(views, element)
|
||||
case 'page-goto':
|
||||
views.pageNum = parseInt(element.dataset.goto)
|
||||
return func(views, element)
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,6 +265,8 @@ page.domClick = function (event) {
|
||||
return page.getNewToken(element)
|
||||
case 'edit-user':
|
||||
return page.editUser(id)
|
||||
case 'disable-user':
|
||||
return page.disableUser(id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,12 +318,7 @@ page.getUploads = function ({ album, pageNum, all } = {}, element) {
|
||||
page.currentView = 'uploads'
|
||||
page.cache.uploads = {}
|
||||
|
||||
const pagination = `
|
||||
<nav class="pagination is-centered">
|
||||
<a class="button pagination-previous" data-action="page-prev">Previous</a>
|
||||
<a class="button pagination-next" data-action="page-next">Next page</a>
|
||||
</nav>
|
||||
`
|
||||
const pagination = page.paginate(response.data.count, 25, pageNum)
|
||||
|
||||
const controls = `
|
||||
<div class="columns">
|
||||
@ -362,6 +365,7 @@ page.getUploads = function ({ album, pageNum, all } = {}, element) {
|
||||
${controls}
|
||||
<div id="table" class="columns is-multiline is-mobile is-centered">
|
||||
</div>
|
||||
<hr>
|
||||
${pagination}
|
||||
`
|
||||
page.fadeIn()
|
||||
@ -1531,7 +1535,7 @@ page.setActiveMenu = function (activeItem) {
|
||||
activeItem.classList.add('is-active')
|
||||
}
|
||||
|
||||
page.getPrettyDate = date => {
|
||||
page.getPrettyDate = function (date) {
|
||||
return date.getFullYear() + '-' +
|
||||
(date.getMonth() < 9 ? '0' : '') + // month's index starts from zero
|
||||
(date.getMonth() + 1) + '-' +
|
||||
@ -1545,7 +1549,7 @@ page.getPrettyDate = date => {
|
||||
date.getSeconds()
|
||||
}
|
||||
|
||||
page.getPrettyBytes = num => {
|
||||
page.getPrettyBytes = function (num) {
|
||||
// MIT License
|
||||
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
@ -1562,7 +1566,7 @@ page.getPrettyBytes = num => {
|
||||
return `${neg ? '-' : ''}${numStr} ${unit}`
|
||||
}
|
||||
|
||||
page.getUsers = ({ pageNum } = {}, element) => {
|
||||
page.getUsers = function ({ pageNum } = {}, element) {
|
||||
if (element) page.isLoading(element, true)
|
||||
if (pageNum === undefined) pageNum = 0
|
||||
|
||||
@ -1587,12 +1591,7 @@ page.getUsers = ({ pageNum } = {}, element) => {
|
||||
page.currentView = 'users'
|
||||
page.cache.users = {}
|
||||
|
||||
const pagination = `
|
||||
<nav class="pagination is-centered">
|
||||
<a class="button pagination-previous" data-action="page-prev">Previous</a>
|
||||
<a class="button pagination-next" data-action="page-next">Next page</a>
|
||||
</nav>
|
||||
`
|
||||
const pagination = page.paginate(response.data.count, 25, pageNum)
|
||||
|
||||
const controls = `
|
||||
<div class="columns">
|
||||
@ -1686,7 +1685,7 @@ page.getUsers = ({ pageNum } = {}, element) => {
|
||||
<i class="icon-pencil-1"></i>
|
||||
</span>
|
||||
</a>
|
||||
<a class="button is-small is-warning" title="Disable user (WIP)" data-action="disable-user" disabled>
|
||||
<a class="button is-small is-warning" title="Disable user" data-action="disable-user" ${enabled ? '' : 'disabled'}>
|
||||
<span class="icon">
|
||||
<i class="icon-hammer"></i>
|
||||
</span>
|
||||
@ -1721,7 +1720,7 @@ page.editUser = function (id) {
|
||||
const user = page.cache.users[id]
|
||||
if (!user) return
|
||||
|
||||
const groupOptions = Object.keys(page.permissions).map((g, i, a) => {
|
||||
const groupOptions = Object.keys(page.permissions).map(function (g, i, a) {
|
||||
const selected = g === user.displayGroup
|
||||
const disabled = !(a[i + 1] && page.permissions[a[i + 1]])
|
||||
return `<option value="${g}"${selected ? ' selected' : ''}${disabled ? ' disabled' : ''}>${g}</option>`
|
||||
@ -1773,8 +1772,8 @@ page.editUser = function (id) {
|
||||
closeModal: false
|
||||
}
|
||||
}
|
||||
}).then(function (value) {
|
||||
if (!value) return
|
||||
}).then(function (proceed) {
|
||||
if (!proceed) return
|
||||
|
||||
axios.post('api/users/edit', {
|
||||
id,
|
||||
@ -1803,8 +1802,8 @@ page.editUser = function (id) {
|
||||
icon: 'success',
|
||||
content: div
|
||||
})
|
||||
} else if (response.data.name !== user.name) {
|
||||
swal('Success!', `${user.username} was renamed into: ${response.data.name}.`, 'success')
|
||||
} else if (response.data.update && response.data.update.username !== user.username) {
|
||||
swal('Success!', `${user.username} was renamed into: ${response.data.update.name}.`, 'success')
|
||||
} else {
|
||||
swal('Success!', 'The user was edited!', 'success')
|
||||
}
|
||||
@ -1817,12 +1816,17 @@ page.editUser = function (id) {
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
page.disableUser = function (id) {
|
||||
const user = page.cache.users[id]
|
||||
if (!user || !user.enabled) return
|
||||
|
||||
const content = document.createElement('div')
|
||||
content.innerHTML = `You will be disabling a user with the username <b>${page.cache.users[id].username}</b>!`
|
||||
|
||||
swal({
|
||||
title: 'Are you sure?',
|
||||
text: `You will be disabling a user with the username <b>${page.cache.users.id.username}!</b>`,
|
||||
icon: 'warning',
|
||||
content,
|
||||
dangerMode: true,
|
||||
buttons: {
|
||||
cancel: true,
|
||||
@ -1837,12 +1841,11 @@ page.disableUser = function (id) {
|
||||
axios.post('api/users/disable', { id }).then(function (response) {
|
||||
if (!response) return
|
||||
|
||||
if (response.data.success === false) {
|
||||
if (response.data.success === false)
|
||||
if (response.data.description === 'No token provided')
|
||||
return page.verifyToken(page.token)
|
||||
else
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
|
||||
swal('Success!', 'The user has been disabled.', 'success')
|
||||
page.getUsers(page.views.users)
|
||||
@ -1852,7 +1855,56 @@ page.disableUser = function (id) {
|
||||
})
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
page.paginate = function (totalItems, itemsPerPage, currentPage) {
|
||||
// Roughly based on https://github.com/mayuska/pagination/blob/master/index.js
|
||||
currentPage = currentPage + 1
|
||||
const step = 3
|
||||
const numPages = Math.ceil(totalItems / itemsPerPage)
|
||||
|
||||
let template = ''
|
||||
const elementsToShow = step * 2
|
||||
const add = {
|
||||
pageNum (start, end) {
|
||||
for (let i = start; i <= end; ++i)
|
||||
template += `<li><a class="pagination-link ${i === currentPage ? ' is-current' : ''}" aria-label="Goto page ${i}" data-action="page-goto" data-goto="${i - 1}">${i}</a></li>`
|
||||
},
|
||||
startDots () {
|
||||
template += `
|
||||
<li><a class="pagination-link" aria-label="Goto page 1" data-action="page-goto" data-goto="0">1</a></li>
|
||||
<li><span class="pagination-ellipsis">…</span></li>
|
||||
`
|
||||
},
|
||||
endDots () {
|
||||
template += `
|
||||
<li><span class="pagination-ellipsis">…</span></li>
|
||||
<li><a class="pagination-link" aria-label="Goto page ${numPages}" data-action="page-goto" data-goto="${numPages - 1}">${numPages}</a></li>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsToShow >= numPages) {
|
||||
add.pageNum(1, numPages)
|
||||
} else if (currentPage < elementsToShow) {
|
||||
add.pageNum(1, elementsToShow)
|
||||
add.endDots()
|
||||
} else if (currentPage > numPages - elementsToShow) {
|
||||
add.startDots()
|
||||
add.pageNum(numPages - elementsToShow, numPages)
|
||||
} else {
|
||||
add.startDots()
|
||||
add.pageNum(currentPage - step, currentPage, step)
|
||||
add.endDots()
|
||||
}
|
||||
|
||||
return `
|
||||
<nav class="pagination is-centered is-small">
|
||||
<a class="button pagination-previous" data-action="page-prev">Previous</a>
|
||||
<a class="button pagination-next" data-action="page-next">Next page</a>
|
||||
<ul class="pagination-list">${template}</ul>
|
||||
</nav>
|
||||
`
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
// Add 'no-touch' class to non-touch devices
|
||||
|
@ -44,5 +44,6 @@ routes.post('/filelength/change', (req, res, next) => authController.changeFileL
|
||||
routes.get('/users', (req, res, next) => authController.listUsers(req, res, next))
|
||||
routes.get('/users/:page', (req, res, next) => authController.listUsers(req, res, next))
|
||||
routes.post('/users/edit', (req, res, next) => authController.editUser(req, res, next))
|
||||
routes.post('/users/disable', (req, res, next) => authController.disableUser(req, res, next))
|
||||
|
||||
module.exports = routes
|
||||
|
@ -15,7 +15,7 @@
|
||||
v2: Images and config files (manifest.json, browserconfig.xml, etc).
|
||||
v3: CSS and JS files (libs such as bulma, lazyload, etc).
|
||||
#}
|
||||
{% set v1 = "9cK64TLE3Z" %}
|
||||
{% set v1 = "DXJsv4Spfk" %}
|
||||
{% set v2 = "Ii3JYKIhb0" %}
|
||||
{% set v3 = "ll7yHY3b2b" %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user