ban management and other changes
This commit is contained in:
parent
de3f8498c2
commit
2899f48d95
@ -26,8 +26,8 @@ console.warn = function () {
|
||||
|
||||
const realConsoleError = console.error
|
||||
console.error = function () {
|
||||
process.stdout.write('\x1b[2K\r')
|
||||
process.stdout.write('[ err] [' + dateFormat(new Date()) + '] ')
|
||||
process.stderr.write('\x1b[2K\r')
|
||||
process.stderr.write('[ err] [' + dateFormat(new Date()) + '] ')
|
||||
realConsoleError.apply(this, arguments)
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import Models from './models'
|
||||
|
||||
const perPage = 6
|
||||
|
||||
function cleanUserObject (dbe) {
|
||||
function cleanUserObject (dbe, admin) {
|
||||
return {
|
||||
id: dbe.id,
|
||||
username: dbe.username,
|
||||
@ -12,10 +12,11 @@ function cleanUserObject (dbe) {
|
||||
avatar_file: dbe.avatar_file,
|
||||
activated: dbe.activated === 1,
|
||||
locked: dbe.locked === 1,
|
||||
ip_addess: dbe.ip_addess,
|
||||
ip_address: dbe.ip_address,
|
||||
password: dbe.password !== null,
|
||||
nw_privilege: dbe.nw_privilege,
|
||||
created_at: dbe.created_at
|
||||
created_at: dbe.created_at,
|
||||
bannable: dbe.nw_privilege < admin.nw_privilege && dbe.id !== admin.id
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,8 +41,29 @@ async function cleanClientObject (dbe) {
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanBanObject (dbe) {
|
||||
let user = await Users.User.get(dbe.user_id)
|
||||
let admin = await Users.User.get(dbe.admin_id)
|
||||
return {
|
||||
id: dbe.id,
|
||||
reason: dbe.reason,
|
||||
user: {
|
||||
id: user.id,
|
||||
display_name: user.display_name
|
||||
},
|
||||
admin: {
|
||||
id: admin.id,
|
||||
display_name: admin.display_name
|
||||
},
|
||||
expires_at: dbe.expires_at,
|
||||
created_at: dbe.created_at,
|
||||
ip_address: dbe.associated_ip,
|
||||
expired: dbe.expires_at && new Date(dbe.expires_at).getTime() < Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
const API = {
|
||||
getAllUsers: async function (page) {
|
||||
getAllUsers: async function (page, adminId) {
|
||||
let count = await Models.User.query().count('id as ids')
|
||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
||||
return {error: 'No users found'}
|
||||
@ -50,12 +72,13 @@ const API = {
|
||||
count = count[0].ids
|
||||
let paginated = Users.Pagination(perPage, parseInt(count), page)
|
||||
let raw = await Models.User.query().offset(paginated.offset).limit(perPage)
|
||||
let admin = await Users.User.get(adminId)
|
||||
|
||||
let users = []
|
||||
for (let i in raw) {
|
||||
let entry = raw[i]
|
||||
|
||||
users.push(cleanUserObject(entry))
|
||||
users.push(cleanUserObject(entry, admin))
|
||||
}
|
||||
|
||||
return {
|
||||
@ -110,6 +133,7 @@ const API = {
|
||||
|
||||
try {
|
||||
await Models.OAuth2Client.query().patchAndFetchById(id, data)
|
||||
await Models.OAuth2AuthorizedClient.query().delete().where('client_id', id)
|
||||
} catch (e) {
|
||||
return {error: 'No such client'}
|
||||
}
|
||||
@ -159,6 +183,54 @@ const API = {
|
||||
await Models.OAuth2AccessToken.query().delete().where('client_id', id)
|
||||
await Models.OAuth2RefreshToken.query().delete().where('client_id', id)
|
||||
return true
|
||||
},
|
||||
getAllBans: async function (page) {
|
||||
let count = await Models.Ban.query().count('id as ids')
|
||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
||||
return {error: 'No bans on record'}
|
||||
}
|
||||
|
||||
count = count[0].ids
|
||||
let paginated = Users.Pagination(perPage, parseInt(count), page)
|
||||
let raw = await Models.Ban.query().offset(paginated.offset).limit(perPage)
|
||||
|
||||
let bans = []
|
||||
for (let i in raw) {
|
||||
let entry = raw[i]
|
||||
|
||||
bans.push(await cleanBanObject(entry))
|
||||
}
|
||||
|
||||
return {
|
||||
page: paginated,
|
||||
bans: bans
|
||||
}
|
||||
},
|
||||
removeBan: async function (banId) {
|
||||
if (isNaN(banId)) return {error: 'Invalid number'}
|
||||
return Models.Ban.query().delete().where('id', banId)
|
||||
},
|
||||
addBan: async function (data, adminId) {
|
||||
let user = await Users.User.get(parseInt(data.user_id))
|
||||
|
||||
if (!user) return {error: 'No such user.'}
|
||||
if (user.id === adminId) return {error: 'Cannot ban yourself!'}
|
||||
|
||||
let admin = await Users.User.get(adminId)
|
||||
|
||||
if (user.nw_privilege > admin.nw_privilege) return {error: 'Cannot ban user.'}
|
||||
|
||||
let banAdd = {
|
||||
reason: data.reason || 'Unspecified ban',
|
||||
admin_id: adminId,
|
||||
user_id: user.id,
|
||||
expires_at: data.expires_at != null ? new Date(data.expires_at) : null,
|
||||
created_at: new Date(),
|
||||
associated_ip: data.ip_address || user.ip_address || null
|
||||
}
|
||||
|
||||
await Models.Ban.query().insert(banAdd)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ router.post('/', wrap(async (req, res, next) => {
|
||||
|
||||
let passReady = await User.Login.password(req.session.user, req.body.password)
|
||||
if (passReady) {
|
||||
req.session.accesstime = Date.now() + 300000 // 5 minutes
|
||||
req.session.accesstime = Date.now() + 600000 // 10 minutes
|
||||
return res.redirect('/admin')
|
||||
} else {
|
||||
req.flash('message', {error: true, text: 'Invalid password'})
|
||||
@ -62,7 +62,7 @@ router.post('/', wrap(async (req, res, next) => {
|
||||
router.use(wrap(async (req, res, next) => {
|
||||
if (req.session.accesstime) {
|
||||
if (req.session.accesstime > Date.now()) {
|
||||
req.session.accesstime = Date.now() + 300000
|
||||
req.session.accesstime = Date.now() + 600000
|
||||
return next()
|
||||
}
|
||||
|
||||
@ -96,10 +96,14 @@ apiRouter.get('/users', wrap(async (req, res) => {
|
||||
page = 1
|
||||
}
|
||||
|
||||
let users = await API.getAllUsers(page)
|
||||
let users = await API.getAllUsers(page, req.session.user.id)
|
||||
res.jsonp(users)
|
||||
}))
|
||||
|
||||
/* ===============
|
||||
* OAuth2 Data
|
||||
* ===============
|
||||
*/
|
||||
apiRouter.get('/clients', wrap(async (req, res) => {
|
||||
let page = parseInt(req.query.page)
|
||||
if (isNaN(page) || page < 1) {
|
||||
@ -177,6 +181,49 @@ apiRouter.post('/client/delete/:id', wrap(async (req, res) => {
|
||||
res.jsonp(client)
|
||||
}))
|
||||
|
||||
/* ========
|
||||
* Bans
|
||||
* ========
|
||||
*/
|
||||
|
||||
apiRouter.get('/bans', wrap(async (req, res) => {
|
||||
let page = parseInt(req.query.page)
|
||||
if (isNaN(page) || page < 1) {
|
||||
page = 1
|
||||
}
|
||||
|
||||
let bans = await API.getAllBans(page)
|
||||
res.jsonp(bans)
|
||||
}))
|
||||
|
||||
apiRouter.post('/ban/pardon/:id', wrap(async (req, res) => {
|
||||
let id = parseInt(req.params.id)
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).jsonp({error: 'Invalid number'})
|
||||
}
|
||||
|
||||
let ban = await API.removeBan(id)
|
||||
if (ban.error) {
|
||||
return res.status(400).jsonp({error: ban.error})
|
||||
}
|
||||
|
||||
res.jsonp(ban)
|
||||
}))
|
||||
|
||||
apiRouter.post('/ban', wrap(async (req, res) => {
|
||||
if (!req.body.user_id) return res.status(400).jsonp({error: 'ID missing'})
|
||||
if (req.body.csrf !== req.session.csrf) {
|
||||
return res.status(400).jsonp({error: 'Invalid session'})
|
||||
}
|
||||
|
||||
let result = await API.addBan(req.body, req.session.user.id)
|
||||
if (result.error) {
|
||||
return res.status(400).jsonp({error: result.error})
|
||||
}
|
||||
|
||||
res.jsonp(result)
|
||||
}))
|
||||
|
||||
apiRouter.use((err, req, res, next) => {
|
||||
console.error(err)
|
||||
return res.status(500).jsonp({error: 'Internal server error'})
|
||||
|
@ -53,7 +53,9 @@ router.use(wrap(async (req, res, next) => {
|
||||
}
|
||||
|
||||
if (req.session.user.session_refresh < Date.now()) {
|
||||
// Check for ban
|
||||
console.debug('User session update')
|
||||
|
||||
// Check for bans
|
||||
let banStatus = await API.User.getBanStatus(req.session.user.id)
|
||||
|
||||
if (banStatus.length) {
|
||||
@ -63,6 +65,9 @@ router.use(wrap(async (req, res, next) => {
|
||||
|
||||
// Update user session
|
||||
let udata = await API.User.get(req.session.user.id)
|
||||
|
||||
// Update IP address
|
||||
await API.User.update(udata, {ip_address: req.realIP})
|
||||
setSession(req, udata)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,67 @@ function paginationButton (pages) {
|
||||
return html
|
||||
}
|
||||
|
||||
function banUser (id) {
|
||||
window.Dialog.openTemplate('Ban User', 'banNew', {id: id})
|
||||
$('#fnsubmit').submit(function (e) {
|
||||
e.preventDefault()
|
||||
$.post({
|
||||
url: '/admin/api/ban',
|
||||
data: $(this).serialize(),
|
||||
success: function (data) {
|
||||
window.Dialog.close()
|
||||
loadBans(1)
|
||||
},
|
||||
error: function (e) {
|
||||
if (e.responseJSON && e.responseJSON.error) {
|
||||
$('form .message').show()
|
||||
$('form .message').text(e.responseJSON.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function loadBans (page) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: '/admin/api/bans',
|
||||
data: {page: page},
|
||||
success: function (data) {
|
||||
$('#banlist').html('')
|
||||
if (data.error) {
|
||||
$('#banlist').html('<div class="message">' + data.error + '</div>')
|
||||
return
|
||||
}
|
||||
|
||||
var pgbtn = paginationButton(data.page)
|
||||
$('#banlist').append(pgbtn)
|
||||
$('#banlist .pgn .button').click(function (e) {
|
||||
var pgnum = $(this).data('page')
|
||||
if (pgnum == null) return
|
||||
loadBans(parseInt(pgnum))
|
||||
})
|
||||
|
||||
for (var u in data.bans) {
|
||||
var ban = data.bans[u]
|
||||
ban.created_at = new Date(ban.created_at)
|
||||
ban.expires_at = ban.expires_at === null ? 'Never' : new Date(ban.expires_at)
|
||||
var tmp = buildTemplateScript('ban', ban)
|
||||
$('#banlist').append(tmp)
|
||||
}
|
||||
|
||||
$('#banlist .remove').click(function (e) {
|
||||
$.post({
|
||||
url: '/admin/api/ban/pardon/' + parseInt($(this).data('id')),
|
||||
success: function (data) {
|
||||
loadBans(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function loadUsers (page) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
@ -39,7 +100,7 @@ function loadUsers (page) {
|
||||
|
||||
var pgbtn = paginationButton(data.page)
|
||||
$('#userlist').append(pgbtn)
|
||||
$('.pgn .button').click(function (e) {
|
||||
$('#userlist .pgn .button').click(function (e) {
|
||||
var pgnum = $(this).data('page')
|
||||
if (pgnum == null) return
|
||||
loadUsers(parseInt(pgnum))
|
||||
@ -51,6 +112,10 @@ function loadUsers (page) {
|
||||
var tmp = buildTemplateScript('user', user)
|
||||
$('#userlist').append(tmp)
|
||||
}
|
||||
|
||||
$('#userlist .ban').click(function (e) {
|
||||
banUser(parseInt($(this).data('id')))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -110,7 +175,7 @@ function loadClients (page) {
|
||||
|
||||
var pgbtn = paginationButton(data.page)
|
||||
$('#clientlist').append(pgbtn)
|
||||
$('.pgn .button').click(function (e) {
|
||||
$('#clientlist .pgn .button').click(function (e) {
|
||||
var pgnum = $(this).data('page')
|
||||
if (pgnum == null) return
|
||||
loadClients(parseInt(pgnum))
|
||||
@ -123,17 +188,17 @@ function loadClients (page) {
|
||||
$('#clientlist').append(tmp)
|
||||
}
|
||||
|
||||
$('.edit').click(function (e) {
|
||||
$('#clientlist .edit').click(function (e) {
|
||||
var client = $(this).data('client')
|
||||
editClient(parseInt(client))
|
||||
})
|
||||
|
||||
$('.delete').click(function (e) {
|
||||
$('#clientlist .delete').click(function (e) {
|
||||
var client = $(this).data('client')
|
||||
deleteClient(parseInt(client))
|
||||
})
|
||||
|
||||
$('.newsecret').click(function (e) {
|
||||
$('#clientlist .newsecret').click(function (e) {
|
||||
var client = $(this).data('client')
|
||||
$.post({
|
||||
url: '/admin/api/client/new_secret/' + parseInt(client),
|
||||
@ -175,6 +240,10 @@ $(document).ready(function () {
|
||||
loadUsers(1)
|
||||
}
|
||||
|
||||
if ($('#banlist').length) {
|
||||
loadBans(1)
|
||||
}
|
||||
|
||||
if ($('#clientlist').length) {
|
||||
loadClients(1)
|
||||
|
||||
|
@ -40,6 +40,8 @@ nav
|
||||
display: inline-block
|
||||
.right
|
||||
float: right
|
||||
.stamps
|
||||
float: right
|
||||
|
||||
.user
|
||||
min-height: 180px
|
||||
|
@ -8,6 +8,10 @@ block body
|
||||
.users
|
||||
h3 Registered Users
|
||||
#userlist
|
||||
.right
|
||||
.users
|
||||
h3 Bans
|
||||
#banlist
|
||||
.templates
|
||||
script(type="x-tmpl-mustache" id="user").
|
||||
<div class="user" id="user-{{id}}">
|
||||
@ -22,7 +26,7 @@ block body
|
||||
<div class="info">
|
||||
<div class="stamps">
|
||||
{{^activated}}
|
||||
<div class="noactive"><i class="fa fa-fw fa-envelope"></i></div>
|
||||
<div class="noactive" title="Not activated"><i class="fa fa-fw fa-envelope"></i></div>
|
||||
{{/activated}}
|
||||
</div>
|
||||
<div class="display_name">{{display_name}}</div>
|
||||
@ -33,5 +37,33 @@ block body
|
||||
{{^password}}
|
||||
<div class="external"><b>Used external login</b></div>
|
||||
{{/password}}
|
||||
{{#bannable}}
|
||||
<div class="button ban" data-id="{{id}}"><i class="fa fa-fw fa-ban"></i>Ban User</div>
|
||||
{{/bannable}}
|
||||
</div>
|
||||
</div>
|
||||
script(type="x-tmpl-mustache" id="ban").
|
||||
<div class="ban" id="ban-{{user.id}}">
|
||||
<div class="stamps">
|
||||
{{#expired}}
|
||||
<div class="noactive" title="Expired"><i class="fa fa-fw fa-ban"></i></div>
|
||||
{{/expired}}
|
||||
</div>
|
||||
<div class="display_name">User: {{user.display_name}}</div>
|
||||
<div class="display_name">Admin: {{admin.display_name}}</div>
|
||||
<div class="description">Reason: {{reason}}</div>
|
||||
<div class="timestamp">Placed {{created_at}}</div>
|
||||
<div class="timestamp">Expires {{expires_at}}</div>
|
||||
<div class="button remove" data-id="{{id}}">Pardon</div>
|
||||
</div>
|
||||
script(type="x-tmpl-mustache" id="banNew").
|
||||
<form id="fnsubmit">
|
||||
<div class="message error"></div>
|
||||
<input type="hidden" name="csrf" value="#{csrf}">
|
||||
<input type="hidden" name="user_id" value="{{id}}">
|
||||
<label for="reason">Reason</label>
|
||||
<input type="text" id="reason" name="reason">
|
||||
<label for="expires_at">Expires</label>
|
||||
<input type="date" id="expires_at" name="expires_at">
|
||||
<input type="submit" value="Create">
|
||||
</form>
|
||||
|
@ -5,6 +5,7 @@ html
|
||||
link(rel="stylesheet", type="text/css", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css")
|
||||
link(rel="stylesheet", type="text/css", href="/style/main.css")
|
||||
link(rel="stylesheet", type="text/css", href="/style/admin.css")
|
||||
block links
|
||||
script.
|
||||
window.variables = {
|
||||
server_time: parseInt('#{server_time}')
|
||||
|
@ -1,7 +1,7 @@
|
||||
extends layout.pug
|
||||
|
||||
block title
|
||||
|Icy Network - Legal Notice
|
||||
|Icy Network
|
||||
|
||||
block body
|
||||
.document
|
||||
|
Reference in New Issue
Block a user