mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-31 07:11:33 +00:00
feat: cleaned up routes init
asserting auth and JSON body will now be done via route-specific mini middlewares (authController's requireUser or optionalUser)
This commit is contained in:
parent
2dccaacf33
commit
d7d6a29123
@ -102,18 +102,18 @@ self.unholdAlbumIdentifiers = res => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.list = async (req, res) => {
|
self.list = async (req, res) => {
|
||||||
const user = await utils.authorize(req)
|
|
||||||
|
|
||||||
const all = req.headers.all === '1'
|
const all = req.headers.all === '1'
|
||||||
const simple = req.headers.simple
|
const simple = req.headers.simple
|
||||||
const ismoderator = perms.is(user, 'moderator')
|
const ismoderator = perms.is(req.locals.user, 'moderator')
|
||||||
if (all && !ismoderator) return res.status(403).end()
|
if (all && !ismoderator) {
|
||||||
|
return res.status(403).end()
|
||||||
|
}
|
||||||
|
|
||||||
const filter = function () {
|
const filter = function () {
|
||||||
if (!all) {
|
if (!all) {
|
||||||
this.where({
|
this.where({
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: req.locals.user.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,34 +227,32 @@ self.list = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.create = async (req, res) => {
|
self.create = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
const user = await utils.authorize(req)
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const name = typeof req.body.name === 'string'
|
const name = typeof req.body.name === 'string'
|
||||||
? utils.escape(req.body.name.trim().substring(0, self.titleMaxLength))
|
? utils.escape(req.body.name.trim().substring(0, self.titleMaxLength))
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
if (!name) throw new ClientError('No album name specified.')
|
if (!name) {
|
||||||
|
throw new ClientError('No album name specified.')
|
||||||
|
}
|
||||||
|
|
||||||
const album = await utils.db.table('albums')
|
const album = await utils.db.table('albums')
|
||||||
.where({
|
.where({
|
||||||
name,
|
name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: req.locals.user.id
|
||||||
})
|
})
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
if (album) throw new ClientError('Album name already in use.', { statusCode: 403 })
|
if (album) {
|
||||||
|
throw new ClientError('Album name already in use.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
|
||||||
const identifier = await self.getUniqueAlbumIdentifier(res)
|
const identifier = await self.getUniqueAlbumIdentifier(res)
|
||||||
|
|
||||||
const ids = await utils.db.table('albums').insert({
|
const ids = await utils.db.table('albums').insert({
|
||||||
name,
|
name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id,
|
userid: req.locals.user.id,
|
||||||
identifier,
|
identifier,
|
||||||
timestamp: Math.floor(Date.now() / 1000),
|
timestamp: Math.floor(Date.now() / 1000),
|
||||||
editedAt: 0,
|
editedAt: 0,
|
||||||
@ -272,39 +270,33 @@ self.create = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.delete = async (req, res) => {
|
self.delete = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
// Re-map Request.body for .disable()
|
||||||
|
req.body.del = true
|
||||||
// Parse POST body and re-map for .disable()
|
|
||||||
req.body = await req.json()
|
|
||||||
.then(obj => {
|
|
||||||
obj.del = true
|
|
||||||
return obj
|
|
||||||
})
|
|
||||||
|
|
||||||
return self.disable(req, res)
|
return self.disable(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.disable = async (req, res) => {
|
self.disable = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
const ismoderator = perms.is(req.locals.user, 'moderator')
|
||||||
const user = await utils.authorize(req)
|
|
||||||
const ismoderator = perms.is(user, 'moderator')
|
|
||||||
|
|
||||||
// Parse POST body, if required
|
|
||||||
req.body = req.body || await req.json()
|
|
||||||
|
|
||||||
const id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
if (isNaN(id)) throw new ClientError('No album specified.')
|
if (isNaN(id)) {
|
||||||
|
throw new ClientError('No album specified.')
|
||||||
|
}
|
||||||
|
|
||||||
const purge = req.body.purge
|
const purge = req.body.purge
|
||||||
|
|
||||||
|
// Only allow moderators to delete other users' albums
|
||||||
const del = ismoderator ? req.body.del : false
|
const del = ismoderator ? req.body.del : false
|
||||||
|
|
||||||
const filter = function () {
|
const filter = function () {
|
||||||
this.where('id', id)
|
this.where('id', id)
|
||||||
|
|
||||||
|
// Only allow moderators to disable other users' albums
|
||||||
if (!ismoderator) {
|
if (!ismoderator) {
|
||||||
this.andWhere({
|
this.andWhere({
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: req.locals.user.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,7 +318,7 @@ self.disable = async (req, res) => {
|
|||||||
|
|
||||||
if (files.length) {
|
if (files.length) {
|
||||||
const ids = files.map(file => file.id)
|
const ids = files.map(file => file.id)
|
||||||
const failed = await utils.bulkDeleteFromDb('id', ids, user)
|
const failed = await utils.bulkDeleteFromDb('id', ids, req.locals.user)
|
||||||
if (failed.length) {
|
if (failed.length) {
|
||||||
return res.json({ success: false, failed })
|
return res.json({ success: false, failed })
|
||||||
}
|
}
|
||||||
@ -352,36 +344,38 @@ self.disable = async (req, res) => {
|
|||||||
await paths.unlink(path.join(paths.zips, `${album.identifier}.zip`))
|
await paths.unlink(path.join(paths.zips, `${album.identifier}.zip`))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Re-throw non-ENOENT error
|
// Re-throw non-ENOENT error
|
||||||
if (error.code !== 'ENOENT') throw error
|
if (error.code !== 'ENOENT') {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ success: true })
|
return res.json({ success: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
self.edit = async (req, res) => {
|
self.edit = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
const ismoderator = perms.is(req.locals.user, 'moderator')
|
||||||
const user = await utils.authorize(req)
|
|
||||||
const ismoderator = perms.is(user, 'moderator')
|
|
||||||
|
|
||||||
// Parse POST body, if required
|
|
||||||
req.body = req.body || await req.json()
|
|
||||||
|
|
||||||
const id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
if (isNaN(id)) throw new ClientError('No album specified.')
|
if (isNaN(id)) {
|
||||||
|
throw new ClientError('No album specified.')
|
||||||
|
}
|
||||||
|
|
||||||
const name = typeof req.body.name === 'string'
|
const name = typeof req.body.name === 'string'
|
||||||
? utils.escape(req.body.name.trim().substring(0, self.titleMaxLength))
|
? utils.escape(req.body.name.trim().substring(0, self.titleMaxLength))
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
if (!name) throw new ClientError('No album name specified.')
|
if (!name) {
|
||||||
|
throw new ClientError('No album name specified.')
|
||||||
|
}
|
||||||
|
|
||||||
const filter = function () {
|
const filter = function () {
|
||||||
this.where('id', id)
|
this.where('id', id)
|
||||||
|
|
||||||
|
// Only allow moderators to edit other users' albums
|
||||||
if (!ismoderator) {
|
if (!ismoderator) {
|
||||||
this.andWhere({
|
this.andWhere({
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: req.locals.user.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,14 +396,14 @@ self.edit = async (req, res) => {
|
|||||||
.where({
|
.where({
|
||||||
name,
|
name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: req.locals.user.id
|
||||||
})
|
})
|
||||||
.whereNot('id', id)
|
.whereNot('id', id)
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
if ((album.enabled || (albumNewState === true)) && nameInUse) {
|
if ((album.enabled || (albumNewState === true)) && nameInUse) {
|
||||||
if (req._old) {
|
if (req._legacy) {
|
||||||
// Old rename API (stick with 200 status code for this)
|
// Legacy rename API (stick with 200 status code for this)
|
||||||
throw new ClientError('You did not specify a new name.', { statusCode: 200 })
|
throw new ClientError('You did not specify a new name.', { statusCode: 200 })
|
||||||
} else {
|
} else {
|
||||||
throw new ClientError('Album name already in use.', { statusCode: 403 })
|
throw new ClientError('Album name already in use.', { statusCode: 403 })
|
||||||
@ -448,7 +442,9 @@ self.edit = async (req, res) => {
|
|||||||
await paths.rename(oldZip, newZip)
|
await paths.rename(oldZip, newZip)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Re-throw non-ENOENT error
|
// Re-throw non-ENOENT error
|
||||||
if (error.code !== 'ENOENT') throw error
|
if (error.code !== 'ENOENT') {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
@ -461,16 +457,11 @@ self.edit = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.rename = async (req, res) => {
|
self.rename = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
// Re-map Request.body for .edit()
|
||||||
|
req.body = {
|
||||||
// Parse POST body and re-map for .edit()
|
_legacy: true,
|
||||||
req.body = await req.json()
|
name: req.body.name
|
||||||
.then(obj => {
|
|
||||||
return {
|
|
||||||
_old: true,
|
|
||||||
name: obj.name
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
return self.edit(req, res)
|
return self.edit(req, res)
|
||||||
}
|
}
|
||||||
@ -529,6 +520,7 @@ self.getUpstreamCompat = async (req, res) => {
|
|||||||
// map to .get() with chibisafe/upstream compatibility
|
// map to .get() with chibisafe/upstream compatibility
|
||||||
// This API is known to be used in Pitu/Magane
|
// This API is known to be used in Pitu/Magane
|
||||||
req.locals.upstreamCompat = true
|
req.locals.upstreamCompat = true
|
||||||
|
|
||||||
res._json = res.json
|
res._json = res.json
|
||||||
res.json = (body = {}) => {
|
res.json = (body = {}) => {
|
||||||
// Rebuild JSON payload to match lolisafe upstream
|
// Rebuild JSON payload to match lolisafe upstream
|
||||||
@ -549,7 +541,10 @@ self.getUpstreamCompat = async (req, res) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (rebuild.message) rebuild.message = rebuild.message.replace(/\.$/, '')
|
if (rebuild.message) {
|
||||||
|
rebuild.message = rebuild.message.replace(/\.$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
return res._json(rebuild)
|
return res._json(rebuild)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,7 +588,9 @@ self.generateZip = async (req, res) => {
|
|||||||
return
|
return
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Re-throw non-ENOENT error
|
// Re-throw non-ENOENT error
|
||||||
if (error.code !== 'ENOENT') throw error
|
if (error.code !== 'ENOENT') {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,19 +670,15 @@ self.generateZip = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.addFiles = async (req, res) => {
|
self.addFiles = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
const user = await utils.authorize(req)
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const ids = req.body.ids
|
const ids = req.body.ids
|
||||||
if (!Array.isArray(ids) || !ids.length) {
|
if (!Array.isArray(ids) || !ids.length) {
|
||||||
throw new ClientError('No files specified.')
|
throw new ClientError('No files specified.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let albumid = parseInt(req.body.albumid)
|
let albumid = parseInt(req.body.albumid)
|
||||||
if (isNaN(albumid) || albumid < 0) albumid = null
|
if (isNaN(albumid) || albumid < 0) {
|
||||||
|
albumid = null
|
||||||
|
}
|
||||||
|
|
||||||
const failed = []
|
const failed = []
|
||||||
const albumids = []
|
const albumids = []
|
||||||
@ -694,8 +687,10 @@ self.addFiles = async (req, res) => {
|
|||||||
const album = await utils.db.table('albums')
|
const album = await utils.db.table('albums')
|
||||||
.where('id', albumid)
|
.where('id', albumid)
|
||||||
.where(function () {
|
.where(function () {
|
||||||
if (user.username !== 'root') {
|
// Only allow "root" user to arbitrarily add/remove files to/from any albums
|
||||||
this.where('userid', user.id)
|
// NOTE: Dashboard does not facilitate this, intended for manual API calls
|
||||||
|
if (req.locals.user.username !== 'root') {
|
||||||
|
this.where('userid', req.locals.user.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.first()
|
.first()
|
||||||
@ -709,7 +704,7 @@ self.addFiles = async (req, res) => {
|
|||||||
|
|
||||||
const files = await utils.db.table('files')
|
const files = await utils.db.table('files')
|
||||||
.whereIn('id', ids)
|
.whereIn('id', ids)
|
||||||
.where('userid', user.id)
|
.where('userid', req.locals.user.id)
|
||||||
|
|
||||||
failed.push(...ids.filter(id => !files.find(file => file.id === id)))
|
failed.push(...ids.filter(id => !files.find(file => file.id === id)))
|
||||||
|
|
||||||
|
@ -35,27 +35,79 @@ const usersPerPage = config.dashboard
|
|||||||
? Math.max(Math.min(config.dashboard.usersPerPage || 0, 100), 1)
|
? Math.max(Math.min(config.dashboard.usersPerPage || 0, 100), 1)
|
||||||
: 25
|
: 25
|
||||||
|
|
||||||
|
self.assertUser = async (token, fields) => {
|
||||||
|
// Default fields/columns to fetch from database
|
||||||
|
const _fields = ['id', 'username', 'enabled', 'timestamp', 'permission', 'registration']
|
||||||
|
|
||||||
|
// Allow fetching additional fields/columns
|
||||||
|
if (typeof fields === 'string') {
|
||||||
|
fields = [fields]
|
||||||
|
}
|
||||||
|
if (Array.isArray(fields)) {
|
||||||
|
_fields.push(...fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await utils.db.table('users')
|
||||||
|
.where('token', token)
|
||||||
|
.select(_fields)
|
||||||
|
.first()
|
||||||
|
if (user) {
|
||||||
|
if (user.enabled === false || user.enabled === 0) {
|
||||||
|
throw new ClientError('This account has been disabled.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
} else {
|
||||||
|
throw new ClientError('Invalid token.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ is next() if this was a synchronous middleware function
|
||||||
|
self.requireUser = async (req, res, _, fields) => {
|
||||||
|
// Throws when token is missing, thus use only for users-only routes
|
||||||
|
const token = req.headers.token
|
||||||
|
if (token === undefined) {
|
||||||
|
throw new ClientError('No token provided.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add user data to Request.locals.user
|
||||||
|
req.locals.user = await self.assertUser(token, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ is next() if this was a synchronous middleware function
|
||||||
|
self.optionalUser = async (req, res, _, fields) => {
|
||||||
|
// Throws when token if missing only when private is set to true in config,
|
||||||
|
// thus use for routes that can handle no auth requests
|
||||||
|
const token = req.headers.token
|
||||||
|
if (token) {
|
||||||
|
// Add user data to Request.locals.user
|
||||||
|
req.locals.user = await self.assertUser(token, fields)
|
||||||
|
} else if (config.private === true) {
|
||||||
|
throw new ClientError('No token provided.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.verify = async (req, res) => {
|
self.verify = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const username = typeof req.body.username === 'string'
|
const username = typeof req.body.username === 'string'
|
||||||
? req.body.username.trim()
|
? req.body.username.trim()
|
||||||
: ''
|
: ''
|
||||||
if (!username) throw new ClientError('No username provided.')
|
if (!username) {
|
||||||
|
throw new ClientError('No username provided.')
|
||||||
|
}
|
||||||
|
|
||||||
const password = typeof req.body.password === 'string'
|
const password = typeof req.body.password === 'string'
|
||||||
? req.body.password.trim()
|
? req.body.password.trim()
|
||||||
: ''
|
: ''
|
||||||
if (!password) throw new ClientError('No password provided.')
|
if (!password) {
|
||||||
|
throw new ClientError('No password provided.')
|
||||||
|
}
|
||||||
|
|
||||||
const user = await utils.db.table('users')
|
const user = await utils.db.table('users')
|
||||||
.where('username', username)
|
.where('username', username)
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
if (!user) throw new ClientError('Wrong credentials.', { statusCode: 403 })
|
if (!user) {
|
||||||
|
throw new ClientError('Wrong credentials.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
|
||||||
if (user.enabled === false || user.enabled === 0) {
|
if (user.enabled === false || user.enabled === 0) {
|
||||||
throw new ClientError('This account has been disabled.', { statusCode: 403 })
|
throw new ClientError('This account has been disabled.', { statusCode: 403 })
|
||||||
@ -70,11 +122,6 @@ self.verify = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.register = async (req, res) => {
|
self.register = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
if (config.enableUserAccounts === false) {
|
if (config.enableUserAccounts === false) {
|
||||||
throw new ClientError('Registration is currently disabled.', { statusCode: 403 })
|
throw new ClientError('Registration is currently disabled.', { statusCode: 403 })
|
||||||
}
|
}
|
||||||
@ -119,12 +166,6 @@ self.register = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.changePassword = async (req, res) => {
|
self.changePassword = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
const user = await utils.authorize(req)
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const password = typeof req.body.password === 'string'
|
const password = typeof req.body.password === 'string'
|
||||||
? req.body.password.trim()
|
? req.body.password.trim()
|
||||||
: ''
|
: ''
|
||||||
@ -135,7 +176,7 @@ self.changePassword = async (req, res) => {
|
|||||||
const hash = await bcrypt.hash(password, saltRounds)
|
const hash = await bcrypt.hash(password, saltRounds)
|
||||||
|
|
||||||
await utils.db.table('users')
|
await utils.db.table('users')
|
||||||
.where('id', user.id)
|
.where('id', req.locals.user.id)
|
||||||
.update('password', hash)
|
.update('password', hash)
|
||||||
|
|
||||||
return res.json({ success: true })
|
return res.json({ success: true })
|
||||||
@ -152,14 +193,10 @@ self.assertPermission = (user, target) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.createUser = async (req, res) => {
|
self.createUser = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
const isadmin = perms.is(req.locals.user, 'admin')
|
||||||
const user = await utils.authorize(req)
|
if (!isadmin) {
|
||||||
|
return res.status(403).end()
|
||||||
// Parse POST body
|
}
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const isadmin = perms.is(user, 'admin')
|
|
||||||
if (!isadmin) return res.status(403).end()
|
|
||||||
|
|
||||||
const username = typeof req.body.username === 'string'
|
const username = typeof req.body.username === 'string'
|
||||||
? req.body.username.trim()
|
? req.body.username.trim()
|
||||||
@ -215,22 +252,22 @@ self.createUser = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.editUser = async (req, res) => {
|
self.editUser = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
const isadmin = perms.is(req.locals.user, 'admin')
|
||||||
const user = await utils.authorize(req)
|
if (!isadmin) {
|
||||||
|
return res.status(403).end()
|
||||||
// Parse POST body, if required
|
}
|
||||||
req.body = req.body || await req.json()
|
|
||||||
|
|
||||||
const isadmin = perms.is(user, 'admin')
|
|
||||||
if (!isadmin) throw new ClientError('', { statusCode: 403 })
|
|
||||||
|
|
||||||
const id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
if (isNaN(id)) throw new ClientError('No user specified.')
|
if (isNaN(id)) {
|
||||||
|
throw new ClientError('No user specified.')
|
||||||
|
}
|
||||||
|
|
||||||
const target = await utils.db.table('users')
|
const target = await utils.db.table('users')
|
||||||
.where('id', id)
|
.where('id', id)
|
||||||
.first()
|
.first()
|
||||||
self.assertPermission(user, target)
|
|
||||||
|
// Ensure this user has permission to tamper with target user
|
||||||
|
self.assertPermission(req.locals.user, target)
|
||||||
|
|
||||||
const update = {}
|
const update = {}
|
||||||
|
|
||||||
@ -272,38 +309,33 @@ self.editUser = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.disableUser = async (req, res) => {
|
self.disableUser = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
// Re-map Request.body for .editUser()
|
||||||
|
req.body = {
|
||||||
// Parse POST body and re-map for .editUser()
|
id: req.body.id,
|
||||||
req.body = await req.json()
|
|
||||||
.then(obj => {
|
|
||||||
return {
|
|
||||||
id: obj.id,
|
|
||||||
enabled: false
|
enabled: false
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
return self.editUser(req, res)
|
return self.editUser(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.deleteUser = async (req, res) => {
|
self.deleteUser = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
const isadmin = perms.is(req.locals.user, 'admin')
|
||||||
const user = await utils.authorize(req)
|
if (!isadmin) {
|
||||||
|
return res.status(403).end()
|
||||||
// Parse POST body
|
}
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const isadmin = perms.is(user, 'admin')
|
|
||||||
if (!isadmin) throw new ClientError('', { statusCode: 403 })
|
|
||||||
|
|
||||||
const id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
const purge = req.body.purge
|
const purge = req.body.purge
|
||||||
if (isNaN(id)) throw new ClientError('No user specified.')
|
if (isNaN(id)) {
|
||||||
|
throw new ClientError('No user specified.')
|
||||||
|
}
|
||||||
|
|
||||||
const target = await utils.db.table('users')
|
const target = await utils.db.table('users')
|
||||||
.where('id', id)
|
.where('id', id)
|
||||||
.first()
|
.first()
|
||||||
self.assertPermission(user, target)
|
|
||||||
|
// Ensure this user has permission to tamper with target user
|
||||||
|
self.assertPermission(req.locals.user, target)
|
||||||
|
|
||||||
const files = await utils.db.table('files')
|
const files = await utils.db.table('files')
|
||||||
.where('userid', id)
|
.where('userid', id)
|
||||||
@ -312,7 +344,7 @@ self.deleteUser = async (req, res) => {
|
|||||||
if (files.length) {
|
if (files.length) {
|
||||||
const fileids = files.map(file => file.id)
|
const fileids = files.map(file => file.id)
|
||||||
if (purge) {
|
if (purge) {
|
||||||
const failed = await utils.bulkDeleteFromDb('id', fileids, user)
|
const failed = await utils.bulkDeleteFromDb('id', fileids, req.locals.user)
|
||||||
utils.invalidateStatsCache('uploads')
|
utils.invalidateStatsCache('uploads')
|
||||||
if (failed.length) {
|
if (failed.length) {
|
||||||
return res.json({ success: false, failed })
|
return res.json({ success: false, failed })
|
||||||
@ -361,10 +393,10 @@ self.bulkDeleteUsers = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.listUsers = async (req, res) => {
|
self.listUsers = async (req, res) => {
|
||||||
const user = await utils.authorize(req)
|
const isadmin = perms.is(req.locals.user, 'admin')
|
||||||
|
if (!isadmin) {
|
||||||
const isadmin = perms.is(user, 'admin')
|
return res.status(403).end()
|
||||||
if (!isadmin) throw new ClientError('', { statusCode: 403 })
|
}
|
||||||
|
|
||||||
// Base result object
|
// Base result object
|
||||||
const result = { success: true, users: [], usersPerPage, count: 0 }
|
const result = { success: true, users: [], usersPerPage, count: 0 }
|
||||||
|
@ -63,16 +63,13 @@ self.unholdTokens = res => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.verify = async (req, res) => {
|
self.verify = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const token = typeof req.body.token === 'string'
|
const token = typeof req.body.token === 'string'
|
||||||
? req.body.token.trim()
|
? req.body.token.trim()
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
if (!token) throw new ClientError('No token provided.', { statusCode: 403 })
|
if (!token) {
|
||||||
|
throw new ClientError('No token provided.', { statusCode: 403 })
|
||||||
|
}
|
||||||
|
|
||||||
const user = await utils.db.table('users')
|
const user = await utils.db.table('users')
|
||||||
.where('token', token)
|
.where('token', token)
|
||||||
@ -106,17 +103,14 @@ self.verify = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.list = async (req, res) => {
|
self.list = async (req, res) => {
|
||||||
const user = await utils.authorize(req)
|
return res.json({ success: true, token: req.locals.user.token })
|
||||||
return res.json({ success: true, token: user.token })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.change = async (req, res) => {
|
self.change = async (req, res) => {
|
||||||
const user = await utils.authorize(req, 'token')
|
|
||||||
|
|
||||||
const newToken = await self.getUniqueToken(res)
|
const newToken = await self.getUniqueToken(res)
|
||||||
|
|
||||||
await utils.db.table('users')
|
await utils.db.table('users')
|
||||||
.where('token', user.token)
|
.where('token', req.locals.user.token)
|
||||||
.update({
|
.update({
|
||||||
token: newToken,
|
token: newToken,
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
|
@ -259,15 +259,8 @@ self.upload = async (req, res) => {
|
|||||||
throw new ClientError('Request Content-Type must be either multipart/form-data or application/json.')
|
throw new ClientError('Request Content-Type must be either multipart/form-data or application/json.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let user
|
|
||||||
if (config.private === true) {
|
|
||||||
user = await utils.authorize(req)
|
|
||||||
} else if (req.headers.token) {
|
|
||||||
user = await utils.assertUser(req.headers.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.privateUploadGroup) {
|
if (config.privateUploadGroup) {
|
||||||
if (!user || !perms.is(user, config.privateUploadGroup)) {
|
if (!req.locals.user || !perms.is(req.locals.user, config.privateUploadGroup)) {
|
||||||
throw new ClientError(config.privateUploadCustomResponse || 'Your usergroup is not permitted to upload new files.', { statusCode: 403 })
|
throw new ClientError(config.privateUploadCustomResponse || 'Your usergroup is not permitted to upload new files.', { statusCode: 403 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,18 +268,18 @@ self.upload = async (req, res) => {
|
|||||||
let albumid = parseInt(req.headers.albumid || (req.path_parameters && req.path_parameters.albumid))
|
let albumid = parseInt(req.headers.albumid || (req.path_parameters && req.path_parameters.albumid))
|
||||||
if (isNaN(albumid)) albumid = null
|
if (isNaN(albumid)) albumid = null
|
||||||
|
|
||||||
const age = self.assertRetentionPeriod(user, req.headers.age)
|
const age = self.assertRetentionPeriod(req.locals.user, req.headers.age)
|
||||||
|
|
||||||
if (isMultipart) {
|
if (isMultipart) {
|
||||||
return self.actuallyUpload(req, res, user, { albumid, age })
|
return self.actuallyUpload(req, res, { albumid, age })
|
||||||
} else {
|
} else {
|
||||||
// Parse POST body
|
// Parse POST body
|
||||||
req.body = await req.json()
|
req.body = await req.json()
|
||||||
return self.actuallyUploadUrls(req, res, user, { albumid, age })
|
return self.actuallyUploadUrls(req, res, { albumid, age })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.actuallyUpload = async (req, res, user, data = {}) => {
|
self.actuallyUpload = async (req, res, data = {}) => {
|
||||||
// Init empty Request.body and Request.files
|
// Init empty Request.body and Request.files
|
||||||
req.body = {}
|
req.body = {}
|
||||||
req.files = []
|
req.files = []
|
||||||
@ -471,7 +464,7 @@ self.actuallyUpload = async (req, res, user, data = {}) => {
|
|||||||
const filesData = req.files
|
const filesData = req.files
|
||||||
|
|
||||||
if (utils.scan.instance) {
|
if (utils.scan.instance) {
|
||||||
const scanResult = await self.scanFiles(req, user, filesData)
|
const scanResult = await self.scanFiles(req, filesData)
|
||||||
if (scanResult) {
|
if (scanResult) {
|
||||||
throw new ClientError(scanResult)
|
throw new ClientError(scanResult)
|
||||||
}
|
}
|
||||||
@ -479,13 +472,13 @@ self.actuallyUpload = async (req, res, user, data = {}) => {
|
|||||||
|
|
||||||
await self.stripTags(req, filesData)
|
await self.stripTags(req, filesData)
|
||||||
|
|
||||||
const result = await self.storeFilesToDb(req, res, user, filesData)
|
const result = await self.storeFilesToDb(req, res, filesData)
|
||||||
return self.sendUploadResponse(req, res, user, result)
|
return self.sendUploadResponse(req, res, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** URL uploads */
|
/** URL uploads */
|
||||||
|
|
||||||
self.actuallyUploadUrls = async (req, res, user, data = {}) => {
|
self.actuallyUploadUrls = async (req, res, data = {}) => {
|
||||||
if (!config.uploads.urlMaxSize) {
|
if (!config.uploads.urlMaxSize) {
|
||||||
throw new ClientError('Upload by URLs is disabled at the moment.', { statusCode: 403 })
|
throw new ClientError('Upload by URLs is disabled at the moment.', { statusCode: 403 })
|
||||||
}
|
}
|
||||||
@ -670,36 +663,23 @@ self.actuallyUploadUrls = async (req, res, user, data = {}) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (utils.scan.instance) {
|
if (utils.scan.instance) {
|
||||||
const scanResult = await self.scanFiles(req, user, filesData)
|
const scanResult = await self.scanFiles(req, filesData)
|
||||||
if (scanResult) {
|
if (scanResult) {
|
||||||
throw new ClientError(scanResult)
|
throw new ClientError(scanResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await self.storeFilesToDb(req, res, user, filesData)
|
const result = await self.storeFilesToDb(req, res, filesData)
|
||||||
return self.sendUploadResponse(req, res, user, result)
|
return self.sendUploadResponse(req, res, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Chunk uploads */
|
/** Chunk uploads */
|
||||||
|
|
||||||
self.finishChunks = async (req, res) => {
|
self.finishChunks = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
|
|
||||||
if (!chunkedUploads) {
|
if (!chunkedUploads) {
|
||||||
throw new ClientError('Chunked upload is disabled.', { statusCode: 403 })
|
throw new ClientError('Chunked upload is disabled.', { statusCode: 403 })
|
||||||
}
|
}
|
||||||
|
|
||||||
let user
|
|
||||||
if (config.private === true) {
|
|
||||||
user = await utils.authorize(req)
|
|
||||||
if (!user) return
|
|
||||||
} else if (req.headers.token) {
|
|
||||||
user = await utils.assertUser(req.headers.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse POST body
|
|
||||||
req.body = await req.json()
|
|
||||||
|
|
||||||
const files = req.body.files
|
const files = req.body.files
|
||||||
if (!Array.isArray(files) || !files.length) {
|
if (!Array.isArray(files) || !files.length) {
|
||||||
throw new ClientError('Bad request.')
|
throw new ClientError('Bad request.')
|
||||||
@ -710,7 +690,7 @@ self.finishChunks = async (req, res) => {
|
|||||||
file.uuid = `${req.ip}_${file.uuid}`
|
file.uuid = `${req.ip}_${file.uuid}`
|
||||||
})
|
})
|
||||||
|
|
||||||
return self.actuallyFinishChunks(req, res, user, files)
|
return self.actuallyFinishChunks(req, res, files)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// Unlink temp files (do not wait)
|
// Unlink temp files (do not wait)
|
||||||
Promise.all(files.map(async file => {
|
Promise.all(files.map(async file => {
|
||||||
@ -723,7 +703,7 @@ self.finishChunks = async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
self.actuallyFinishChunks = async (req, res, user, files) => {
|
self.actuallyFinishChunks = async (req, res, files) => {
|
||||||
const filesData = []
|
const filesData = []
|
||||||
await Promise.all(files.map(async file => {
|
await Promise.all(files.map(async file => {
|
||||||
if (!file.uuid || typeof chunksData[file.uuid] === 'undefined') {
|
if (!file.uuid || typeof chunksData[file.uuid] === 'undefined') {
|
||||||
@ -754,7 +734,7 @@ self.actuallyFinishChunks = async (req, res, user, files) => {
|
|||||||
throw new ClientError(`${extname ? `${extname.substr(1).toUpperCase()} files` : 'Files with no extension'} are not permitted.`)
|
throw new ClientError(`${extname ? `${extname.substr(1).toUpperCase()} files` : 'Files with no extension'} are not permitted.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const age = self.assertRetentionPeriod(user, file.age)
|
const age = self.assertRetentionPeriod(req.locals.user, file.age)
|
||||||
|
|
||||||
let size = file.size
|
let size = file.size
|
||||||
if (size === undefined) {
|
if (size === undefined) {
|
||||||
@ -814,7 +794,7 @@ self.actuallyFinishChunks = async (req, res, user, files) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
if (utils.scan.instance) {
|
if (utils.scan.instance) {
|
||||||
const scanResult = await self.scanFiles(req, user, filesData)
|
const scanResult = await self.scanFiles(req, filesData)
|
||||||
if (scanResult) {
|
if (scanResult) {
|
||||||
throw new ClientError(scanResult)
|
throw new ClientError(scanResult)
|
||||||
}
|
}
|
||||||
@ -822,8 +802,8 @@ self.actuallyFinishChunks = async (req, res, user, files) => {
|
|||||||
|
|
||||||
await self.stripTags(req, filesData)
|
await self.stripTags(req, filesData)
|
||||||
|
|
||||||
const result = await self.storeFilesToDb(req, res, user, filesData)
|
const result = await self.storeFilesToDb(req, res, filesData)
|
||||||
return self.sendUploadResponse(req, res, user, result)
|
return self.sendUploadResponse(req, res, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cleanUpChunks = async uuid => {
|
self.cleanUpChunks = async uuid => {
|
||||||
@ -883,9 +863,9 @@ self.assertScanFileBypass = data => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scanFiles = async (req, user, filesData) => {
|
self.scanFiles = async (req, filesData) => {
|
||||||
const filenames = filesData.map(file => file.filename)
|
const filenames = filesData.map(file => file.filename)
|
||||||
if (self.assertScanUserBypass(user, filenames)) {
|
if (self.assertScanUserBypass(req.locals.user, filenames)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +930,7 @@ self.stripTags = async (req, filesData) => {
|
|||||||
|
|
||||||
/** Database functions */
|
/** Database functions */
|
||||||
|
|
||||||
self.storeFilesToDb = async (req, res, user, filesData) => {
|
self.storeFilesToDb = async (req, res, filesData) => {
|
||||||
const files = []
|
const files = []
|
||||||
const exists = []
|
const exists = []
|
||||||
const albumids = []
|
const albumids = []
|
||||||
@ -960,10 +940,10 @@ self.storeFilesToDb = async (req, res, user, filesData) => {
|
|||||||
// Check if the file exists by checking its hash and size
|
// Check if the file exists by checking its hash and size
|
||||||
const dbFile = await utils.db.table('files')
|
const dbFile = await utils.db.table('files')
|
||||||
.where(function () {
|
.where(function () {
|
||||||
if (user === undefined) {
|
if (req.locals.user) {
|
||||||
this.whereNull('userid')
|
this.where('userid', req.locals.user.id)
|
||||||
} else {
|
} else {
|
||||||
this.where('userid', user.id)
|
this.whereNull('userid')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.where({
|
.where({
|
||||||
@ -1002,8 +982,8 @@ self.storeFilesToDb = async (req, res, user, filesData) => {
|
|||||||
timestamp
|
timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user) {
|
if (req.locals.user) {
|
||||||
data.userid = user.id
|
data.userid = req.locals.user.id
|
||||||
data.albumid = file.albumid
|
data.albumid = file.albumid
|
||||||
if (data.albumid !== null && !albumids.includes(data.albumid)) {
|
if (data.albumid !== null && !albumids.includes(data.albumid)) {
|
||||||
albumids.push(data.albumid)
|
albumids.push(data.albumid)
|
||||||
@ -1023,10 +1003,11 @@ self.storeFilesToDb = async (req, res, user, filesData) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
if (files.length) {
|
if (files.length) {
|
||||||
|
// albumids should be empty if non-registerd users (no auth requests)
|
||||||
let authorizedIds = []
|
let authorizedIds = []
|
||||||
if (albumids.length) {
|
if (albumids.length) {
|
||||||
authorizedIds = await utils.db.table('albums')
|
authorizedIds = await utils.db.table('albums')
|
||||||
.where({ userid: user.id })
|
.where({ userid: req.locals.user.id })
|
||||||
.whereIn('id', albumids)
|
.whereIn('id', albumids)
|
||||||
.select('id')
|
.select('id')
|
||||||
.then(rows => rows.map(row => row.id))
|
.then(rows => rows.map(row => row.id))
|
||||||
@ -1057,7 +1038,7 @@ self.storeFilesToDb = async (req, res, user, filesData) => {
|
|||||||
|
|
||||||
/** Final response */
|
/** Final response */
|
||||||
|
|
||||||
self.sendUploadResponse = async (req, res, user, result) => {
|
self.sendUploadResponse = async (req, res, result) => {
|
||||||
// Send response
|
// Send response
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
@ -1079,7 +1060,7 @@ self.sendUploadResponse = async (req, res, user, result) => {
|
|||||||
|
|
||||||
// If uploaded by user, add delete URL (intended for ShareX and its derivatives)
|
// If uploaded by user, add delete URL (intended for ShareX and its derivatives)
|
||||||
// Homepage uploader will not use this (use dashboard instead)
|
// Homepage uploader will not use this (use dashboard instead)
|
||||||
if (user) {
|
if (req.locals.user) {
|
||||||
map.deleteUrl = `${utils.conf.homeDomain}/file/${file.name}?delete`
|
map.deleteUrl = `${utils.conf.homeDomain}/file/${file.name}?delete`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1091,30 +1072,20 @@ self.sendUploadResponse = async (req, res, user, result) => {
|
|||||||
/** Delete uploads */
|
/** Delete uploads */
|
||||||
|
|
||||||
self.delete = async (req, res) => {
|
self.delete = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
// Re-map Request.body for .bulkDelete()
|
||||||
|
// This is the legacy API used by lolisafe v3's frontend
|
||||||
// Parse POST body and re-map for .bulkDelete()
|
|
||||||
// Original API used by lolisafe v3's frontend
|
|
||||||
// Meanwhile this fork's frontend uses .bulkDelete() straight away
|
// Meanwhile this fork's frontend uses .bulkDelete() straight away
|
||||||
req.body = await req.json()
|
const id = parseInt(req.body.id)
|
||||||
.then(obj => {
|
req.body = {
|
||||||
const id = parseInt(obj.id)
|
_legacy: true,
|
||||||
return {
|
|
||||||
field: 'id',
|
field: 'id',
|
||||||
values: isNaN(id) ? undefined : [id]
|
values: isNaN(id) ? undefined : [id]
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
return self.bulkDelete(req, res)
|
return self.bulkDelete(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.bulkDelete = async (req, res) => {
|
self.bulkDelete = async (req, res) => {
|
||||||
utils.assertRequestType(req, 'application/json')
|
|
||||||
const user = await utils.authorize(req)
|
|
||||||
|
|
||||||
// Parse POST body, if required
|
|
||||||
req.body = req.body || await req.json()
|
|
||||||
|
|
||||||
const field = req.body.field || 'id'
|
const field = req.body.field || 'id'
|
||||||
const values = req.body.values
|
const values = req.body.values
|
||||||
|
|
||||||
@ -1122,7 +1093,7 @@ self.bulkDelete = async (req, res) => {
|
|||||||
throw new ClientError('No array of files specified.')
|
throw new ClientError('No array of files specified.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const failed = await utils.bulkDeleteFromDb(field, values, user)
|
const failed = await utils.bulkDeleteFromDb(field, values, req.locals.user)
|
||||||
|
|
||||||
return res.json({ success: true, failed })
|
return res.json({ success: true, failed })
|
||||||
}
|
}
|
||||||
@ -1130,13 +1101,13 @@ self.bulkDelete = async (req, res) => {
|
|||||||
/** List uploads */
|
/** List uploads */
|
||||||
|
|
||||||
self.list = async (req, res) => {
|
self.list = async (req, res) => {
|
||||||
const user = await utils.authorize(req)
|
|
||||||
|
|
||||||
const all = req.headers.all === '1'
|
const all = req.headers.all === '1'
|
||||||
const filters = req.headers.filters
|
const filters = req.headers.filters
|
||||||
const minoffset = Number(req.headers.minoffset) || 0
|
const minoffset = Number(req.headers.minoffset) || 0
|
||||||
const ismoderator = perms.is(user, 'moderator')
|
const ismoderator = perms.is(req.locals.user, 'moderator')
|
||||||
if (all && !ismoderator) return res.status(403).end()
|
if (all && !ismoderator) {
|
||||||
|
return res.status(403).end()
|
||||||
|
}
|
||||||
|
|
||||||
const albumid = req.path_parameters && Number(req.path_parameters.albumid)
|
const albumid = req.path_parameters && Number(req.path_parameters.albumid)
|
||||||
const basedomain = utils.conf.domain
|
const basedomain = utils.conf.domain
|
||||||
@ -1522,7 +1493,7 @@ self.list = async (req, res) => {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// If not listing all uploads, list user's uploads
|
// If not listing all uploads, list user's uploads
|
||||||
this.where('userid', user.id)
|
this.where('userid', req.locals.user.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, refine using any of the supplied 'albumid' keys and/or NULL flag
|
// Then, refine using any of the supplied 'albumid' keys and/or NULL flag
|
||||||
@ -1746,8 +1717,7 @@ self.list = async (req, res) => {
|
|||||||
/** Get file info */
|
/** Get file info */
|
||||||
|
|
||||||
self.get = async (req, res) => {
|
self.get = async (req, res) => {
|
||||||
const user = await utils.authorize(req)
|
const ismoderator = perms.is(req.locals.user, 'moderator')
|
||||||
const ismoderator = perms.is(user, 'moderator')
|
|
||||||
|
|
||||||
const identifier = req.path_parameters && req.path_parameters.identifier
|
const identifier = req.path_parameters && req.path_parameters.identifier
|
||||||
if (identifier === undefined) {
|
if (identifier === undefined) {
|
||||||
@ -1757,8 +1727,9 @@ self.get = async (req, res) => {
|
|||||||
const file = await utils.db.table('files')
|
const file = await utils.db.table('files')
|
||||||
.where('name', identifier)
|
.where('name', identifier)
|
||||||
.where(function () {
|
.where(function () {
|
||||||
|
// Only allow moderators to get any files' information
|
||||||
if (!ismoderator) {
|
if (!ismoderator) {
|
||||||
this.where('userid', user.id)
|
this.where('userid', req.locals.user.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.first()
|
.first()
|
||||||
|
@ -398,33 +398,11 @@ self.assertRequestType = (req, type) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertUser = async (token, fields) => {
|
self.assertJSON = async (req, res) => {
|
||||||
const _fields = ['id', 'username', 'enabled', 'timestamp', 'permission', 'registration']
|
// Assert Request Content-Type
|
||||||
if (typeof fields === 'string') fields = [fields]
|
self.assertRequestType(req, 'application/json')
|
||||||
if (Array.isArray(fields)) {
|
// Parse JSON payload
|
||||||
_fields.push(...fields)
|
req.body = await req.json()
|
||||||
}
|
|
||||||
|
|
||||||
const user = await self.db.table('users')
|
|
||||||
.where('token', token)
|
|
||||||
.select(_fields)
|
|
||||||
.first()
|
|
||||||
if (user) {
|
|
||||||
if (user.enabled === false || user.enabled === 0) {
|
|
||||||
throw new ClientError('This account has been disabled.', { statusCode: 403 })
|
|
||||||
}
|
|
||||||
return user
|
|
||||||
} else {
|
|
||||||
throw new ClientError('Invalid token.', { statusCode: 403 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.authorize = async (req, fields) => {
|
|
||||||
const token = req.headers.token
|
|
||||||
if (token === undefined) {
|
|
||||||
throw new ClientError('No token provided.', { statusCode: 403 })
|
|
||||||
}
|
|
||||||
return self.assertUser(token, fields)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.generateThumbs = async (name, extname, force) => {
|
self.generateThumbs = async (name, extname, force) => {
|
||||||
@ -801,10 +779,10 @@ self.invalidateStatsCache = type => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const generateStats = async (req, res) => {
|
const generateStats = async (req, res) => {
|
||||||
const user = await self.authorize(req)
|
const isadmin = perms.is(req.locals.user, 'admin')
|
||||||
|
if (!isadmin) {
|
||||||
const isadmin = perms.is(user, 'admin')
|
return res.status(403).end()
|
||||||
if (!isadmin) throw new ClientError('', { statusCode: 403 })
|
}
|
||||||
|
|
||||||
const hrstart = process.hrtime()
|
const hrstart = process.hrtime()
|
||||||
const stats = {}
|
const stats = {}
|
||||||
|
115
routes/api.js
115
routes/api.js
@ -1,10 +1,10 @@
|
|||||||
const { Router } = require('hyper-express')
|
const { Router } = require('hyper-express')
|
||||||
const routes = new Router()
|
const routes = new Router()
|
||||||
const albumsController = require('./../controllers/albumsController')
|
const albums = require('./../controllers/albumsController')
|
||||||
const authController = require('./../controllers/authController')
|
const auth = require('./../controllers/authController')
|
||||||
const tokenController = require('./../controllers/tokenController')
|
const tokens = require('./../controllers/tokenController')
|
||||||
const uploadController = require('./../controllers/uploadController')
|
const upload = require('./../controllers/uploadController')
|
||||||
const utilsController = require('./../controllers/utilsController')
|
const utils = require('./../controllers/utilsController')
|
||||||
const config = require('./../config')
|
const config = require('./../config')
|
||||||
|
|
||||||
routes.get('/check', async (req, res) => {
|
routes.get('/check', async (req, res) => {
|
||||||
@ -16,54 +16,75 @@ routes.get('/check', async (req, res) => {
|
|||||||
fileIdentifierLength: config.uploads.fileIdentifierLength,
|
fileIdentifierLength: config.uploads.fileIdentifierLength,
|
||||||
stripTags: config.uploads.stripTags
|
stripTags: config.uploads.stripTags
|
||||||
}
|
}
|
||||||
if (utilsController.retentions.enabled && utilsController.retentions.periods._) {
|
if (utils.retentions.enabled && utils.retentions.periods._) {
|
||||||
obj.temporaryUploadAges = utilsController.retentions.periods._
|
obj.temporaryUploadAges = utils.retentions.periods._
|
||||||
obj.defaultTemporaryUploadAge = utilsController.retentions.default._
|
obj.defaultTemporaryUploadAge = utils.retentions.default._
|
||||||
}
|
}
|
||||||
if (utilsController.clientVersion) {
|
if (utils.clientVersion) {
|
||||||
obj.version = utilsController.clientVersion
|
obj.version = utils.clientVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json(obj)
|
return res.json(obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
routes.post('/login', authController.verify)
|
/** ./controllers/authController.js */
|
||||||
routes.post('/register', authController.register)
|
|
||||||
routes.post('/password/change', authController.changePassword)
|
routes.post('/login', utils.assertJSON, auth.verify)
|
||||||
routes.get('/uploads', uploadController.list)
|
routes.post('/register', utils.assertJSON, auth.register)
|
||||||
routes.get('/uploads/:page', uploadController.list)
|
routes.post('/password/change', [auth.requireUser, utils.assertJSON], auth.changePassword)
|
||||||
routes.post('/upload', uploadController.upload, {
|
|
||||||
|
routes.get('/users', auth.requireUser, auth.listUsers)
|
||||||
|
routes.get('/users/:page', auth.requireUser, auth.listUsers)
|
||||||
|
|
||||||
|
routes.post('/users/create', [auth.requireUser, utils.assertJSON], auth.createUser)
|
||||||
|
routes.post('/users/delete', [auth.requireUser, utils.assertJSON], auth.deleteUser)
|
||||||
|
routes.post('/users/disable', [auth.requireUser, utils.assertJSON], auth.disableUser)
|
||||||
|
routes.post('/users/edit', [auth.requireUser, utils.assertJSON], auth.editUser)
|
||||||
|
|
||||||
|
/** ./controllers/uploadController.js */
|
||||||
|
|
||||||
// HyperExpress defaults to 250kb
|
// HyperExpress defaults to 250kb
|
||||||
// https://github.com/kartikk221/hyper-express/blob/6.2.4/docs/Server.md#server-constructor-options
|
// https://github.com/kartikk221/hyper-express/blob/6.4.4/docs/Server.md#server-constructor-options
|
||||||
max_body_length: parseInt(config.uploads.maxSize) * 1e6
|
const maxBodyLength = parseInt(config.uploads.maxSize) * 1e6
|
||||||
})
|
routes.post('/upload', { max_body_length: maxBodyLength }, auth.optionalUser, upload.upload)
|
||||||
routes.post('/upload/delete', uploadController.delete)
|
routes.post('/upload/:albumid', { max_body_length: maxBodyLength }, auth.optionalUser, upload.upload)
|
||||||
routes.post('/upload/bulkdelete', uploadController.bulkDelete)
|
routes.post('/upload/finishchunks', [auth.optionalUser, utils.assertJSON], upload.finishChunks)
|
||||||
routes.post('/upload/finishchunks', uploadController.finishChunks)
|
|
||||||
routes.get('/upload/get/:identifier', uploadController.get)
|
routes.get('/uploads', auth.requireUser, upload.list)
|
||||||
routes.post('/upload/:albumid', uploadController.upload)
|
routes.get('/uploads/:page', auth.requireUser, upload.list)
|
||||||
routes.get('/album/get/:identifier', albumsController.get)
|
routes.get('/album/:albumid/:page', auth.requireUser, upload.list)
|
||||||
routes.get('/album/zip/:identifier', albumsController.generateZip)
|
|
||||||
routes.get('/album/:identifier', albumsController.getUpstreamCompat)
|
routes.get('/upload/get/:identifier', auth.requireUser, upload.get)
|
||||||
routes.get('/album/:albumid/:page', uploadController.list)
|
routes.post('/upload/delete', [auth.requireUser, utils.assertJSON], upload.delete)
|
||||||
routes.get('/albums', albumsController.list)
|
routes.post('/upload/bulkdelete', [auth.requireUser, utils.assertJSON], upload.bulkDelete)
|
||||||
routes.get('/albums/:page', albumsController.list)
|
|
||||||
routes.post('/albums', albumsController.create)
|
/** ./controllers/albumsController.js */
|
||||||
routes.post('/albums/addfiles', albumsController.addFiles)
|
|
||||||
routes.post('/albums/delete', albumsController.delete)
|
routes.get('/albums', auth.requireUser, albums.list)
|
||||||
routes.post('/albums/disable', albumsController.disable)
|
routes.get('/albums/:page', auth.requireUser, albums.list)
|
||||||
routes.post('/albums/edit', albumsController.edit)
|
|
||||||
routes.post('/albums/rename', albumsController.rename)
|
routes.get('/album/get/:identifier', albums.get)
|
||||||
routes.get('/albums/test', albumsController.test)
|
routes.get('/album/zip/:identifier', albums.generateZip)
|
||||||
routes.get('/tokens', tokenController.list)
|
routes.get('/album/:identifier', albums.getUpstreamCompat)
|
||||||
routes.post('/tokens/verify', tokenController.verify)
|
|
||||||
routes.post('/tokens/change', tokenController.change)
|
routes.post('/albums', [auth.requireUser, utils.assertJSON], albums.create)
|
||||||
routes.get('/users', authController.listUsers)
|
routes.post('/albums/addfiles', [auth.requireUser, utils.assertJSON], albums.addFiles)
|
||||||
routes.get('/users/:page', authController.listUsers)
|
routes.post('/albums/delete', [auth.requireUser, utils.assertJSON], albums.delete)
|
||||||
routes.post('/users/create', authController.createUser)
|
routes.post('/albums/disable', [auth.requireUser, utils.assertJSON], albums.disable)
|
||||||
routes.post('/users/edit', authController.editUser)
|
routes.post('/albums/edit', [auth.requireUser, utils.assertJSON], albums.edit)
|
||||||
routes.post('/users/disable', authController.disableUser)
|
routes.post('/albums/rename', [auth.requireUser, utils.assertJSON], albums.rename)
|
||||||
routes.post('/users/delete', authController.deleteUser)
|
|
||||||
routes.get('/stats', utilsController.stats)
|
/** ./controllers/tokenController.js **/
|
||||||
|
|
||||||
|
routes.get('/tokens', auth.requireUser, tokens.list)
|
||||||
|
routes.post('/tokens/change', async (req, res) => {
|
||||||
|
// Include user's "token" field into database query
|
||||||
|
return auth.requireUser(req, res, null, 'token')
|
||||||
|
}, tokens.change)
|
||||||
|
routes.post('/tokens/verify', utils.assertJSON, tokens.verify)
|
||||||
|
|
||||||
|
/** ./controllers/utilsController.js */
|
||||||
|
|
||||||
|
routes.get('/stats', [auth.requireUser], utils.stats)
|
||||||
|
|
||||||
module.exports = routes
|
module.exports = routes
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const { Router } = require('hyper-express')
|
const { Router } = require('hyper-express')
|
||||||
const routes = new Router()
|
const routes = new Router()
|
||||||
const uploadController = require('./../controllers/uploadController')
|
const upload = require('./../controllers/uploadController')
|
||||||
const utils = require('./../controllers/utilsController')
|
const utils = require('./../controllers/utilsController')
|
||||||
const config = require('./../config')
|
const config = require('./../config')
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ routes.post('/nojs', async (req, res) => {
|
|||||||
files: result.files || [{}]
|
files: result.files || [{}]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return uploadController.upload(req, res)
|
return upload.upload(req, res)
|
||||||
}, {
|
}, {
|
||||||
// HyperExpress defaults to 250kb
|
// HyperExpress defaults to 250kb
|
||||||
// https://github.com/kartikk221/hyper-express/blob/6.2.4/docs/Server.md#server-constructor-options
|
// https://github.com/kartikk221/hyper-express/blob/6.2.4/docs/Server.md#server-constructor-options
|
||||||
|
Loading…
Reference in New Issue
Block a user