2017-08-27 18:39:30 +00:00
|
|
|
import express from 'express'
|
2017-12-01 11:35:47 +00:00
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
import { ensureLogin } from '../../scripts/ensureLogin'
|
2017-08-27 18:39:30 +00:00
|
|
|
import wrap from '../../scripts/asyncRoute'
|
2020-12-13 14:36:07 +00:00
|
|
|
import * as API from '../api/admin'
|
|
|
|
import { sendMail } from '../api/emailer'
|
|
|
|
import { User, Login } from '../api'
|
2017-08-27 18:39:30 +00:00
|
|
|
|
|
|
|
const router = express.Router()
|
|
|
|
const apiRouter = express.Router()
|
|
|
|
|
2017-08-28 15:42:16 +00:00
|
|
|
// Check for privilege required to access the admin panel
|
2017-10-09 14:38:27 +00:00
|
|
|
router.use(ensureLogin, wrap(async (req, res, next) => {
|
2017-08-27 18:39:30 +00:00
|
|
|
if (!req.session.privilege) {
|
2020-05-28 18:30:21 +00:00
|
|
|
const u = await User.get(req.session.user)
|
2017-08-27 18:39:30 +00:00
|
|
|
req.session.privilege = u.nw_privilege
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req.session.user && req.session.privilege !== 5) {
|
|
|
|
return res.redirect('/login')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.locals.server_time = process.uptime()
|
|
|
|
next()
|
|
|
|
}))
|
|
|
|
|
2017-08-28 15:42:16 +00:00
|
|
|
/* ================
|
|
|
|
* ASK PASSWORD
|
|
|
|
* ================
|
|
|
|
*/
|
|
|
|
|
2017-08-28 17:32:54 +00:00
|
|
|
router.get('/access', (req, res) => {
|
2017-08-28 15:42:16 +00:00
|
|
|
if (!req.session.accesstime || req.session.accesstime < Date.now()) {
|
2020-05-28 18:30:21 +00:00
|
|
|
return res.status(401).jsonp({ error: 'Access expired' })
|
2017-08-28 15:42:16 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
res.jsonp({ access: req.session.accesstime - Date.now() })
|
2017-08-28 15:42:16 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// Post password to continue
|
|
|
|
router.post('/', wrap(async (req, res, next) => {
|
|
|
|
if (!req.body.password) return next()
|
|
|
|
|
|
|
|
if (req.body.csrf !== req.session.csrf) {
|
2020-05-28 18:30:21 +00:00
|
|
|
req.flash('message', { error: true, text: 'Invalid session token' })
|
2017-08-28 15:42:16 +00:00
|
|
|
return next()
|
|
|
|
}
|
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
const passReady = await Login.password(req.session.user, req.body.password)
|
2017-08-28 15:42:16 +00:00
|
|
|
if (passReady) {
|
2017-08-28 22:36:13 +00:00
|
|
|
req.session.accesstime = Date.now() + 600000 // 10 minutes
|
2017-08-28 15:42:16 +00:00
|
|
|
return res.redirect('/admin')
|
|
|
|
} else {
|
2020-05-28 18:30:21 +00:00
|
|
|
req.flash('message', { error: true, text: 'Invalid password' })
|
2017-08-28 15:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
next()
|
|
|
|
}))
|
|
|
|
|
|
|
|
// Ensure that the admin panel is not kept open for prolonged time
|
|
|
|
router.use(wrap(async (req, res, next) => {
|
|
|
|
if (req.session.accesstime) {
|
2017-08-28 17:32:54 +00:00
|
|
|
if (req.session.accesstime > Date.now()) {
|
2017-08-28 22:36:13 +00:00
|
|
|
req.session.accesstime = Date.now() + 600000
|
2017-08-28 17:32:54 +00:00
|
|
|
return next()
|
|
|
|
}
|
|
|
|
|
2017-08-28 15:42:16 +00:00
|
|
|
delete req.session.accesstime
|
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
res.render('user/password', { post: '/admin' })
|
2017-08-28 15:42:16 +00:00
|
|
|
}))
|
|
|
|
|
2017-08-27 18:39:30 +00:00
|
|
|
/* =========
|
|
|
|
* VIEWS
|
|
|
|
* =========
|
|
|
|
*/
|
|
|
|
|
|
|
|
router.get('/', (req, res) => {
|
|
|
|
res.render('admin/index')
|
|
|
|
})
|
|
|
|
|
|
|
|
router.get('/oauth2', wrap(async (req, res) => {
|
|
|
|
res.render('admin/oauth2')
|
|
|
|
}))
|
|
|
|
|
|
|
|
/* =======
|
|
|
|
* API
|
|
|
|
* =======
|
|
|
|
*/
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
function csrfVerify (req, res, next) {
|
|
|
|
if (req.body.csrf !== req.session.csrf) {
|
|
|
|
return next(new Error('Invalid session'))
|
|
|
|
}
|
|
|
|
|
|
|
|
next()
|
|
|
|
}
|
|
|
|
|
|
|
|
/* =============
|
|
|
|
* User Data
|
|
|
|
* =============
|
|
|
|
*/
|
|
|
|
|
2017-08-28 15:42:16 +00:00
|
|
|
apiRouter.get('/users', wrap(async (req, res) => {
|
|
|
|
let page = parseInt(req.query.page)
|
|
|
|
if (isNaN(page) || page < 1) {
|
|
|
|
page = 1
|
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const users = await API.getAllUsers(page, req.session.user.id)
|
2017-08-28 15:42:16 +00:00
|
|
|
res.jsonp(users)
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.get('/user/:id', wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.params.id)
|
2017-12-07 15:37:36 +00:00
|
|
|
if (isNaN(id)) {
|
|
|
|
throw new Error('Invalid number')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.jsonp(await API.getUser(id))
|
|
|
|
}))
|
|
|
|
|
|
|
|
apiRouter.post('/user', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.user_id)
|
2017-12-07 15:37:36 +00:00
|
|
|
if (isNaN(id)) {
|
|
|
|
throw new Error('Invalid or missing user ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.jsonp(await API.editUser(id, req.body))
|
|
|
|
}))
|
|
|
|
|
|
|
|
apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.user_id)
|
2017-12-07 15:37:36 +00:00
|
|
|
if (isNaN(id)) {
|
|
|
|
throw new Error('Invalid or missing user ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.jsonp(await API.resendActivationEmail(id))
|
|
|
|
}))
|
|
|
|
|
|
|
|
apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.user_id)
|
2017-12-07 15:37:36 +00:00
|
|
|
if (isNaN(id)) {
|
|
|
|
throw new Error('Invalid or missing user ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.jsonp(await API.revokeTotpToken(id))
|
|
|
|
}))
|
|
|
|
|
|
|
|
apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.user_id)
|
2017-12-07 15:37:36 +00:00
|
|
|
if (isNaN(id)) {
|
|
|
|
throw new Error('Invalid or missing user ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.jsonp(await API.sendPasswordEmail(id))
|
|
|
|
}))
|
|
|
|
|
2018-02-13 20:32:11 +00:00
|
|
|
apiRouter.post('/user/lock', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.user_id)
|
2018-02-13 20:32:11 +00:00
|
|
|
if (isNaN(id)) {
|
|
|
|
throw new Error('Invalid or missing user ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
res.jsonp(await API.lockAccount(id))
|
|
|
|
}))
|
|
|
|
|
2017-12-07 19:57:42 +00:00
|
|
|
const availableScopes = ['uuid', 'email', 'username', 'display_name']
|
|
|
|
apiRouter.get('/search/users', wrap(async (req, res) => {
|
|
|
|
if (!req.query.terms) throw new Error('Please specify search terms!')
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const scopes = []
|
2017-12-07 19:57:42 +00:00
|
|
|
if (req.query.scopes) {
|
2020-05-28 18:30:21 +00:00
|
|
|
const scq = req.query.scopes.split(',')
|
2017-12-07 19:57:42 +00:00
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
for (const i in scq) {
|
2017-12-07 19:57:42 +00:00
|
|
|
scq[i] = scq[i].trim()
|
|
|
|
|
|
|
|
if (availableScopes.indexOf(scq[i]) !== -1) {
|
|
|
|
scopes.push(scq[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!scopes.length) {
|
|
|
|
scopes.push('email')
|
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const results = await API.searchUsers(req.query.terms, scopes)
|
2017-12-07 19:57:42 +00:00
|
|
|
res.jsonp(results)
|
|
|
|
}))
|
|
|
|
|
2017-08-28 22:36:13 +00:00
|
|
|
/* ===============
|
|
|
|
* OAuth2 Data
|
|
|
|
* ===============
|
|
|
|
*/
|
2019-01-04 23:10:23 +00:00
|
|
|
|
2017-08-28 17:32:54 +00:00
|
|
|
apiRouter.get('/clients', wrap(async (req, res) => {
|
|
|
|
let page = parseInt(req.query.page)
|
|
|
|
if (isNaN(page) || page < 1) {
|
|
|
|
page = 1
|
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const clients = await API.getAllClients(page)
|
2017-08-28 17:32:54 +00:00
|
|
|
res.jsonp(clients)
|
|
|
|
}))
|
|
|
|
|
|
|
|
apiRouter.get('/client/:id', wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.params.id)
|
2017-08-28 17:32:54 +00:00
|
|
|
if (isNaN(id)) {
|
2017-12-07 15:37:36 +00:00
|
|
|
throw new Error('Invalid number')
|
2017-08-28 17:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const client = await API.getClient(id)
|
2017-08-28 17:32:54 +00:00
|
|
|
|
|
|
|
res.jsonp(client)
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.post('/client/new', csrfVerify, wrap(async (req, res) => {
|
2017-11-30 21:13:14 +00:00
|
|
|
await API.createClient(req.body, req.session.user)
|
2017-08-28 17:32:54 +00:00
|
|
|
|
|
|
|
res.status(204).end()
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.id)
|
2017-08-30 12:23:45 +00:00
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
if (!id || isNaN(id)) throw new Error('ID missing')
|
2017-08-28 17:32:54 +00:00
|
|
|
|
2017-11-30 21:13:14 +00:00
|
|
|
await API.updateClient(id, req.body)
|
2017-08-28 17:32:54 +00:00
|
|
|
|
|
|
|
res.status(204).end()
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.post('/client/new_secret', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.id)
|
2017-08-28 17:32:54 +00:00
|
|
|
if (isNaN(id)) {
|
2017-12-07 15:37:36 +00:00
|
|
|
throw new Error('Invalid client ID')
|
2017-08-28 17:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const client = await API.newSecret(id)
|
2017-08-28 17:32:54 +00:00
|
|
|
|
|
|
|
res.jsonp(client)
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.post('/client/delete', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.id)
|
2017-08-28 17:32:54 +00:00
|
|
|
if (isNaN(id)) {
|
2017-12-07 15:37:36 +00:00
|
|
|
throw new Error('Invalid client ID')
|
2017-08-28 17:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const client = await API.removeClient(id)
|
2017-08-28 17:32:54 +00:00
|
|
|
|
|
|
|
res.jsonp(client)
|
|
|
|
}))
|
|
|
|
|
2017-08-28 22:36:13 +00:00
|
|
|
/* ========
|
|
|
|
* Bans
|
|
|
|
* ========
|
|
|
|
*/
|
|
|
|
|
|
|
|
apiRouter.get('/bans', wrap(async (req, res) => {
|
|
|
|
let page = parseInt(req.query.page)
|
|
|
|
if (isNaN(page) || page < 1) {
|
|
|
|
page = 1
|
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const bans = await API.getAllBans(page)
|
2017-08-28 22:36:13 +00:00
|
|
|
res.jsonp(bans)
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
|
2020-05-28 18:30:21 +00:00
|
|
|
const id = parseInt(req.body.id)
|
2017-08-28 22:36:13 +00:00
|
|
|
if (isNaN(id)) {
|
2017-12-07 15:37:36 +00:00
|
|
|
throw new Error('Invalid number')
|
2017-08-28 22:36:13 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const ban = await API.removeBan(id)
|
2017-08-28 22:36:13 +00:00
|
|
|
|
|
|
|
res.jsonp(ban)
|
|
|
|
}))
|
|
|
|
|
2017-12-07 15:37:36 +00:00
|
|
|
apiRouter.post('/ban', csrfVerify, wrap(async (req, res) => {
|
|
|
|
if (!req.body.user_id) throw new Error('ID missing')
|
2017-08-28 22:36:13 +00:00
|
|
|
|
2020-05-28 18:30:21 +00:00
|
|
|
const result = await API.addBan(req.body, req.session.user.id)
|
2017-08-28 22:36:13 +00:00
|
|
|
|
|
|
|
res.jsonp(result)
|
|
|
|
}))
|
|
|
|
|
2018-02-13 21:02:07 +00:00
|
|
|
apiRouter.post('/email', csrfVerify, wrap(async (req, res) => {
|
|
|
|
if (!req.body.email) throw new Error('Email missing')
|
|
|
|
if (!req.body.title) throw new Error('Title missing')
|
|
|
|
if (!req.body.content) throw new Error('Content missing')
|
|
|
|
|
|
|
|
var message = {
|
|
|
|
subject: req.body.title,
|
|
|
|
html: req.body.content
|
|
|
|
}
|
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
const result = await sendMail(req.body.email, message)
|
2018-02-13 21:02:07 +00:00
|
|
|
|
|
|
|
res.jsonp(result)
|
|
|
|
}))
|
|
|
|
|
2021-02-09 16:46:04 +00:00
|
|
|
apiRouter.use((err, req, res) => {
|
2017-08-28 17:32:54 +00:00
|
|
|
console.error(err)
|
2020-05-28 18:30:21 +00:00
|
|
|
return res.status(400).jsonp({ error: err.message })
|
2017-08-28 17:32:54 +00:00
|
|
|
})
|
|
|
|
|
2017-08-27 18:39:30 +00:00
|
|
|
router.use('/api', apiRouter)
|
|
|
|
|
2020-12-13 14:36:07 +00:00
|
|
|
export default router
|