mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-31 07:11:33 +00:00
feat: configurable uploads/albums/users per page
please check sample.config.js for new options if missing from config, defaults to 25 per page (old defaults)
This commit is contained in:
parent
a6e1943655
commit
285e79c5a7
@ -655,6 +655,15 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
Dashboard config.
|
||||
*/
|
||||
dashboard: {
|
||||
uploadsPerPage: 24,
|
||||
albumsPerPage: 10,
|
||||
usersPerPage: 10
|
||||
},
|
||||
|
||||
/*
|
||||
Cloudflare support.
|
||||
*/
|
||||
|
@ -20,8 +20,14 @@ const self = {
|
||||
onHold: new Set() // temporarily held random album identifiers
|
||||
}
|
||||
|
||||
/** Preferences */
|
||||
|
||||
const homeDomain = utils.conf.homeDomain || utils.conf.domain
|
||||
|
||||
const albumsPerPage = config.dashboard
|
||||
? Math.max(Math.min(config.dashboard.albumsPerPage || 0, 100), 1)
|
||||
: 25
|
||||
|
||||
const zipMaxTotalSize = parseInt(config.cloudflare.zipMaxTotalSize)
|
||||
const zipMaxTotalSizeBytes = zipMaxTotalSize * 1e6
|
||||
const zipOptions = config.uploads.jsZipOptions
|
||||
@ -112,41 +118,48 @@ self.list = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Base result object
|
||||
const result = { success: true, albums: [], albumsPerPage, count: 0, homeDomain }
|
||||
|
||||
// Query albums count for pagination
|
||||
const count = await utils.db.table('albums')
|
||||
result.count = await utils.db.table('albums')
|
||||
.where(filter)
|
||||
.count('id as count')
|
||||
.then(rows => rows[0].count)
|
||||
if (!count) {
|
||||
return res.json({ success: true, albums: [], count })
|
||||
if (!result.count) {
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
const fields = ['id', 'name']
|
||||
|
||||
let albums
|
||||
if (simple) {
|
||||
albums = await utils.db.table('albums')
|
||||
result.albums = await utils.db.table('albums')
|
||||
.where(filter)
|
||||
.select(fields)
|
||||
|
||||
return res.json({ success: true, albums, count })
|
||||
} else {
|
||||
let offset = req.path_parameters && Number(req.path_parameters.page)
|
||||
if (isNaN(offset)) offset = 0
|
||||
else if (offset < 0) offset = Math.max(0, Math.ceil(count / 25) + offset)
|
||||
|
||||
fields.push('identifier', 'enabled', 'timestamp', 'editedAt', 'zipGeneratedAt', 'download', 'public', 'description')
|
||||
if (all) fields.push('userid')
|
||||
|
||||
albums = await utils.db.table('albums')
|
||||
.where(filter)
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
.select(fields)
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
let offset = req.path_parameters && Number(req.path_parameters.page)
|
||||
if (isNaN(offset)) {
|
||||
offset = 0
|
||||
} else if (offset < 0) {
|
||||
offset = Math.max(0, Math.ceil(result.count / albumsPerPage) + offset)
|
||||
}
|
||||
|
||||
fields.push('identifier', 'enabled', 'timestamp', 'editedAt', 'zipGeneratedAt', 'download', 'public', 'description')
|
||||
if (all) {
|
||||
fields.push('userid')
|
||||
}
|
||||
|
||||
result.albums = await utils.db.table('albums')
|
||||
.where(filter)
|
||||
.limit(albumsPerPage)
|
||||
.offset(albumsPerPage * offset)
|
||||
.select(fields)
|
||||
|
||||
const albumids = {}
|
||||
for (const album of albums) {
|
||||
for (const album of result.albums) {
|
||||
album.download = album.download !== 0
|
||||
album.public = album.public !== 0
|
||||
album.uploads = 0
|
||||
@ -171,7 +184,7 @@ self.list = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(albums.map(album => getAlbumZipSize(album)))
|
||||
await Promise.all(result.albums.map(album => getAlbumZipSize(album)))
|
||||
|
||||
const uploads = await utils.db.table('files')
|
||||
.whereIn('albumid', Object.keys(albumids))
|
||||
@ -186,19 +199,17 @@ self.list = async (req, res) => {
|
||||
|
||||
// If we are not listing all albums, send response
|
||||
if (!all) {
|
||||
return res.json({ success: true, albums, count, homeDomain })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
// Otherwise proceed to querying usernames
|
||||
const userids = albums
|
||||
const userids = result.albums
|
||||
.map(album => album.userid)
|
||||
.filter((v, i, a) => {
|
||||
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
||||
})
|
||||
.filter(utils.filterUniquifySqlArray)
|
||||
|
||||
// If there are no albums attached to a registered user, send response
|
||||
if (!userids.length) {
|
||||
return res.json({ success: true, albums, count, homeDomain })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
// Query usernames of user IDs from currently selected files
|
||||
@ -206,12 +217,13 @@ self.list = async (req, res) => {
|
||||
.whereIn('id', userids)
|
||||
.select('id', 'username')
|
||||
|
||||
const users = {}
|
||||
result.users = {}
|
||||
|
||||
for (const user of usersTable) {
|
||||
users[user.id] = user.username
|
||||
result.users[user.id] = user.username
|
||||
}
|
||||
|
||||
return res.json({ success: true, albums, count, users, homeDomain })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
self.create = async (req, res) => {
|
||||
|
@ -26,9 +26,15 @@ const self = {
|
||||
}
|
||||
}
|
||||
|
||||
/** Preferences */
|
||||
|
||||
// https://github.com/kelektiv/node.bcrypt.js/tree/v5.0.1#a-note-on-rounds
|
||||
const saltRounds = 10
|
||||
|
||||
const usersPerPage = config.dashboard
|
||||
? Math.max(Math.min(config.dashboard.usersPerPage || 0, 100), 1)
|
||||
: 25
|
||||
|
||||
self.verify = async (req, res) => {
|
||||
utils.assertRequestType(req, 'application/json')
|
||||
|
||||
@ -360,24 +366,30 @@ self.listUsers = async (req, res) => {
|
||||
const isadmin = perms.is(user, 'admin')
|
||||
if (!isadmin) throw new ClientError('', { statusCode: 403 })
|
||||
|
||||
const count = await utils.db.table('users')
|
||||
// Base result object
|
||||
const result = { success: true, users: [], usersPerPage, count: 0 }
|
||||
|
||||
result.count = await utils.db.table('users')
|
||||
.count('id as count')
|
||||
.then(rows => rows[0].count)
|
||||
if (!count) {
|
||||
return res.json({ success: true, users: [], count })
|
||||
if (!result.count) {
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
let offset = req.path_parameters && Number(req.path_parameters.page)
|
||||
if (isNaN(offset)) offset = 0
|
||||
else if (offset < 0) offset = Math.max(0, Math.ceil(count / 25) + offset)
|
||||
if (isNaN(offset)) {
|
||||
offset = 0
|
||||
} else if (offset < 0) {
|
||||
offset = Math.max(0, Math.ceil(result.count / usersPerPage) + offset)
|
||||
}
|
||||
|
||||
const users = await utils.db.table('users')
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
result.users = await utils.db.table('users')
|
||||
.limit(usersPerPage)
|
||||
.offset(usersPerPage * offset)
|
||||
.select('id', 'username', 'enabled', 'timestamp', 'permission', 'registration')
|
||||
|
||||
const pointers = {}
|
||||
for (const user of users) {
|
||||
for (const user of result.users) {
|
||||
user.groups = perms.mapPermissions(user)
|
||||
delete user.permission
|
||||
user.uploads = 0
|
||||
@ -394,7 +406,7 @@ self.listUsers = async (req, res) => {
|
||||
pointers[upload.userid].usage += parseInt(upload.size)
|
||||
}
|
||||
|
||||
return res.json({ success: true, users, count })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
module.exports = self
|
||||
|
@ -62,6 +62,10 @@ const enableHashing = config.uploads.hash === undefined
|
||||
const queryDatabaseForIdentifierMatch = config.uploads.queryDatabaseForIdentifierMatch ||
|
||||
config.uploads.queryDbForFileCollisions // old config name for identical behavior
|
||||
|
||||
const uploadsPerPage = config.dashboard
|
||||
? Math.max(Math.min(config.dashboard.uploadsPerPage || 0, 100), 1)
|
||||
: 25
|
||||
|
||||
/** Chunks helper class & function **/
|
||||
|
||||
class ChunksData {
|
||||
@ -1686,18 +1690,24 @@ self.list = async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
// Base result object
|
||||
const result = { success: true, files: [], uploadsPerPage, count: 0, basedomain }
|
||||
|
||||
// Query uploads count for pagination
|
||||
const count = await utils.db.table('files')
|
||||
result.count = await utils.db.table('files')
|
||||
.where(filter)
|
||||
.count('id as count')
|
||||
.then(rows => rows[0].count)
|
||||
if (!count) {
|
||||
return res.json({ success: true, files: [], count })
|
||||
if (!result.count) {
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
let offset = req.path_parameters && Number(req.path_parameters.page)
|
||||
if (isNaN(offset)) offset = 0
|
||||
else if (offset < 0) offset = Math.max(0, Math.ceil(count / 25) + offset)
|
||||
if (isNaN(offset)) {
|
||||
offset = 0
|
||||
} else if (offset < 0) {
|
||||
offset = Math.max(0, Math.ceil(result.count / uploadsPerPage) + offset)
|
||||
}
|
||||
|
||||
const columns = ['id', 'name', 'original', 'userid', 'size', 'timestamp']
|
||||
if (utils.retentions.enabled) columns.push('expirydate')
|
||||
@ -1724,33 +1734,33 @@ self.list = async (req, res) => {
|
||||
orderByRaw = '`id` desc'
|
||||
}
|
||||
|
||||
const files = await utils.db.table('files')
|
||||
result.files = await utils.db.table('files')
|
||||
.where(filter)
|
||||
.orderByRaw(orderByRaw)
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
.limit(uploadsPerPage)
|
||||
.offset(uploadsPerPage * offset)
|
||||
.select(columns)
|
||||
|
||||
if (!files.length) {
|
||||
return res.json({ success: true, files, count, basedomain })
|
||||
if (!result.files.length) {
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
for (const file of result.files) {
|
||||
file.extname = utils.extname(file.name)
|
||||
if (utils.mayGenerateThumb(file.extname)) {
|
||||
file.thumb = `thumbs/${file.name.slice(0, -file.extname.length)}.png`
|
||||
}
|
||||
}
|
||||
|
||||
result.albums = {}
|
||||
|
||||
// If we queried albumid, query album names
|
||||
let albums = {}
|
||||
if (columns.includes('albumid')) {
|
||||
const albumids = files
|
||||
const albumids = result.files
|
||||
.map(file => file.albumid)
|
||||
.filter((v, i, a) => {
|
||||
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
||||
})
|
||||
albums = await utils.db.table('albums')
|
||||
.filter(utils.filterUniquifySqlArray)
|
||||
|
||||
result.albums = await utils.db.table('albums')
|
||||
.whereIn('id', albumids)
|
||||
.where('enabled', 1)
|
||||
.select('id', 'name')
|
||||
@ -1766,21 +1776,18 @@ self.list = async (req, res) => {
|
||||
|
||||
// If we are not listing all uploads, send response
|
||||
if (!all) {
|
||||
return res.json({ success: true, files, count, albums, basedomain })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
// Otherwise proceed to querying usernames
|
||||
let usersTable = filterObj.uploaders
|
||||
if (!usersTable.length) {
|
||||
const userids = files
|
||||
const userids = result.files
|
||||
.map(file => file.userid)
|
||||
.filter((v, i, a) => {
|
||||
return v !== null && v !== undefined && v !== '' && a.indexOf(v) === i
|
||||
})
|
||||
.filter(utils.filterUniquifySqlArray)
|
||||
|
||||
// If there are no uploads attached to a registered user, send response
|
||||
if (!userids.length) {
|
||||
return res.json({ success: true, files, count, albums, basedomain })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
// Query usernames of user IDs from currently selected files
|
||||
@ -1789,12 +1796,13 @@ self.list = async (req, res) => {
|
||||
.select('id', 'username')
|
||||
}
|
||||
|
||||
const users = {}
|
||||
result.users = {}
|
||||
|
||||
for (const user of usersTable) {
|
||||
users[user.id] = user.username
|
||||
result.users[user.id] = user.username
|
||||
}
|
||||
|
||||
return res.json({ success: true, files, count, users, albums, basedomain })
|
||||
return res.json(result)
|
||||
}
|
||||
|
||||
/** Get file info */
|
||||
|
@ -386,6 +386,13 @@ self.mask = string => {
|
||||
}
|
||||
}
|
||||
|
||||
self.filterUniquifySqlArray = (value, index, array) => {
|
||||
return value !== null &&
|
||||
value !== undefined &&
|
||||
value !== '' &&
|
||||
array.indexOf(value) === index
|
||||
}
|
||||
|
||||
self.assertRequestType = (req, type) => {
|
||||
if (!req.is(type)) {
|
||||
throw new ClientError(`Request Content-Type must be ${type}.`)
|
||||
|
@ -545,7 +545,8 @@ page.getUploads = (params = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const pages = Math.ceil(response.data.count / 25)
|
||||
const uploadsPerPage = response.data.uploadsPerPage || 25
|
||||
const pages = Math.ceil(response.data.count / uploadsPerPage)
|
||||
const files = response.data.files
|
||||
if (params.pageNum && (files.length === 0)) {
|
||||
page.updateTrigger(params.trigger)
|
||||
@ -564,8 +565,10 @@ page.getUploads = (params = {}) => {
|
||||
const users = response.data.users
|
||||
const basedomain = response.data.basedomain
|
||||
|
||||
if (params.pageNum < 0) params.pageNum = Math.max(0, pages + params.pageNum)
|
||||
const pagination = page.paginate(response.data.count, 25, params.pageNum)
|
||||
if (params.pageNum < 0) {
|
||||
params.pageNum = Math.max(0, pages + params.pageNum)
|
||||
}
|
||||
const pagination = page.paginate(response.data.count, uploadsPerPage, params.pageNum)
|
||||
|
||||
const filter = `
|
||||
<div class="column">
|
||||
@ -671,7 +674,7 @@ page.getUploads = (params = {}) => {
|
||||
.replace(/(data-action="page-ellipsis")/g, `$1 data-jumpid="${bottomJumpId}"`)
|
||||
|
||||
// Whether there are any unselected items
|
||||
let unselected = false
|
||||
let unselected = true
|
||||
|
||||
const showOriginalNames = page.views[page.currentView].originalNames
|
||||
const hasExpiryDateColumn = files.some(file => typeof file.expirydate !== 'undefined')
|
||||
@ -720,7 +723,9 @@ page.getUploads = (params = {}) => {
|
||||
|
||||
// Update selected status
|
||||
files[i].selected = page.selected[page.currentView].includes(files[i].id)
|
||||
if (!files[i].selected) unselected = true
|
||||
if (files[i].selected) {
|
||||
unselected = false
|
||||
}
|
||||
|
||||
// Appendix (display album or user)
|
||||
if (params.all) {
|
||||
@ -888,7 +893,7 @@ page.getUploads = (params = {}) => {
|
||||
}
|
||||
|
||||
const selectAll = document.querySelector('#selectAll')
|
||||
if (selectAll && !unselected && files.length) {
|
||||
if (selectAll && !unselected) {
|
||||
selectAll.checked = true
|
||||
selectAll.title = 'Unselect all'
|
||||
}
|
||||
@ -1615,7 +1620,8 @@ page.getAlbums = (params = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const pages = Math.ceil(response.data.count / 25)
|
||||
const albumsPerPage = response.data.albumsPerPage || 25
|
||||
const pages = Math.ceil(response.data.count / albumsPerPage)
|
||||
const albums = response.data.albums
|
||||
if (params.pageNum && (albums.length === 0)) {
|
||||
page.updateTrigger(params.trigger)
|
||||
@ -1633,8 +1639,10 @@ page.getAlbums = (params = {}) => {
|
||||
const users = response.data.users
|
||||
const homeDomain = response.data.homeDomain || window.location.origin
|
||||
|
||||
if (params.pageNum < 0) params.pageNum = Math.max(0, pages + params.pageNum)
|
||||
const pagination = page.paginate(response.data.count, 25, params.pageNum)
|
||||
if (params.pageNum < 0) {
|
||||
params.pageNum = Math.max(0, pages + params.pageNum)
|
||||
}
|
||||
const pagination = page.paginate(response.data.count, albumsPerPage, params.pageNum)
|
||||
|
||||
const filter = `
|
||||
<div class="column">
|
||||
@ -1722,7 +1730,7 @@ page.getAlbums = (params = {}) => {
|
||||
.replace(/(data-action="page-ellipsis")/g, `$1 data-jumpid="${bottomJumpId}"`)
|
||||
|
||||
// Whether there are any unselected items
|
||||
let unselected = false
|
||||
let unselected = true
|
||||
|
||||
const createNewAlbum = `
|
||||
<h2 class="subtitle">Create new album</h2>
|
||||
@ -1793,7 +1801,9 @@ page.getAlbums = (params = {}) => {
|
||||
const albumUrl = homeDomain + albumUrlText
|
||||
|
||||
const selected = page.selected[page.currentView].includes(album.id)
|
||||
if (!selected) unselected = true
|
||||
if (selected) {
|
||||
unselected = false
|
||||
}
|
||||
|
||||
// Prettify
|
||||
album.hasZip = album.zipSize !== null
|
||||
@ -2386,7 +2396,8 @@ page.getUsers = (params = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const pages = Math.ceil(response.data.count / 25)
|
||||
const usersPerPage = response.data.usersPerPage || 25
|
||||
const pages = Math.ceil(response.data.count / usersPerPage)
|
||||
const users = response.data.users
|
||||
if (params.pageNum && (users.length === 0)) {
|
||||
page.updateTrigger(params.trigger)
|
||||
@ -2401,8 +2412,10 @@ page.getUsers = (params = {}) => {
|
||||
page.currentView = 'users'
|
||||
page.cache = {}
|
||||
|
||||
if (params.pageNum < 0) params.pageNum = Math.max(0, pages + params.pageNum)
|
||||
const pagination = page.paginate(response.data.count, 25, params.pageNum)
|
||||
if (params.pageNum < 0) {
|
||||
params.pageNum = Math.max(0, pages + params.pageNum)
|
||||
}
|
||||
const pagination = page.paginate(response.data.count, usersPerPage, params.pageNum)
|
||||
|
||||
const filter = `
|
||||
<div class="column">
|
||||
@ -2494,7 +2507,7 @@ page.getUsers = (params = {}) => {
|
||||
.replace(/(data-action="page-ellipsis")/g, `$1 data-jumpid="${bottomJumpId}"`)
|
||||
|
||||
// Whether there are any unselected items
|
||||
let unselected = false
|
||||
let unselected = true
|
||||
|
||||
page.dom.innerHTML = `
|
||||
${pagination}
|
||||
@ -2528,7 +2541,9 @@ page.getUsers = (params = {}) => {
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const user = users[i]
|
||||
const selected = page.selected[page.currentView].includes(user.id)
|
||||
if (!selected) unselected = true
|
||||
if (selected) {
|
||||
unselected = false
|
||||
}
|
||||
|
||||
let displayGroup = null
|
||||
const groups = Object.keys(user.groups)
|
||||
|
Loading…
Reference in New Issue
Block a user