admin panel work a bit

This commit is contained in:
Evert Prants 2017-08-28 18:42:16 +03:00
parent f8990ec7a6
commit 70cbbecec2
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
12 changed files with 257 additions and 39 deletions

6
package-lock.json generated
View File

@ -3585,6 +3585,12 @@
"fd-slicer": "1.0.1"
}
},
"mustache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.0.tgz",
"integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=",
"dev": true
},
"mute-stream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",

View File

@ -61,6 +61,7 @@
"concurrently": "^3.5.0",
"eslint-plugin-import": "^2.7.0",
"jquery": "^3.2.1",
"mustache": "^2.3.0",
"standard": "^10.0.3",
"uglify-js": "^1.3.5",
"watch": "^1.0.2",

47
server/api/admin.js Normal file
View File

@ -0,0 +1,47 @@
import Users from './index'
import Models from './models'
const perPage = 6
function cleanUserObject (dbe) {
return {
id: dbe.id,
username: dbe.username,
display_name: dbe.display_name,
email: dbe.email,
avatar_file: dbe.avatar_file,
activated: dbe.activated === 1,
locked: dbe.locked === 1,
ip_addess: dbe.ip_addess,
password: dbe.password !== null,
nw_privilege: dbe.nw_privilege,
created_at: dbe.created_at
}
}
const API = {
getAllUsers: async function (page) {
let count = await Models.User.query().count('id as ids')
if (!count.length || !count[0]['ids'] || isNaN(page)) {
return {error: 'No users found'}
}
count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.User.query().offset(paginated.offset).limit(perPage)
let users = []
for (let i in raw) {
let entry = raw[i]
users.push(cleanUserObject(entry))
}
return {
page: paginated,
users: users
}
}
}
module.exports = API

View File

@ -55,6 +55,24 @@ const API = {
Hash: (len) => {
return crypto.randomBytes(len).toString('hex')
},
/* ppp - Posts Per Page; dcount - Post Count; page - number of current page */
Pagination: (ppp, dcount, page) => {
if (!ppp) ppp = 5
if (!dcount) return null
let pageCount = Math.ceil(dcount / ppp)
if (page > pageCount) page = pageCount
let offset = (page - 1) * ppp
return {
page: page,
perPage: ppp,
pages: pageCount,
offset: offset,
total: dcount
}
},
User: {
get: async function (identifier) {
let scope = 'id'

View File

@ -7,25 +7,6 @@ function slugify (title) {
return title.toLowerCase().replace(/\W/g, '-').substring(0, 16)
}
/* ppp - Posts Per Page; dcount - Post Count; page - number of current page */
function Pagination (ppp, dcount, page) {
if (!ppp) ppp = 5
if (!dcount) return null
let pageCount = Math.ceil(dcount / ppp)
if (page > pageCount) page = pageCount
let offset = (page - 1) * ppp
return {
page: page,
perPage: ppp,
pages: pageCount,
offset: offset,
total: dcount
}
}
async function cleanArticle (entry, shortenContent = false) {
let poster = await API.User.get(entry.user_id)
let article = {
@ -76,7 +57,7 @@ const News = {
}
count = count[0].ids
let paginated = Pagination(perPage, parseInt(count), page)
let paginated = API.Pagination(perPage, parseInt(count), page)
let news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
let articles = []

View File

@ -1,20 +1,19 @@
import express from 'express'
import multiparty from 'multiparty'
import config from '../../scripts/load-config'
import wrap from '../../scripts/asyncRoute'
import API from '../api'
import {User} from '../api'
import API from '../api/admin'
import News from '../api/news'
import Image from '../api/image'
import APIExtern from '../api/external'
const router = express.Router()
const apiRouter = express.Router()
// Check for privilege required to access the admin panel
router.use(wrap(async (req, res, next) => {
if (!req.session.user) return res.redirect('/login')
if (!req.session.privilege) {
let u = await API.User.get(req.session.user)
let u = await User.get(req.session.user)
req.session.privilege = u.nw_privilege
}
@ -26,6 +25,49 @@ router.use(wrap(async (req, res, next) => {
next()
}))
/* ================
* ASK PASSWORD
* ================
*/
apiRouter.get('/access', (req, res) => {
if (!req.session.accesstime || req.session.accesstime < Date.now()) {
return res.status(401).jsonp({error: 'Access expired'})
}
res.jsonp({access: req.session.accesstime - Date.now()})
})
// Post password to continue
router.post('/', wrap(async (req, res, next) => {
if (!req.body.password) return next()
if (req.body.csrf !== req.session.csrf) {
req.flash('message', {error: true, text: 'Invalid session token'})
return next()
}
let passReady = await User.Login.password(req.session.user, req.body.password)
if (passReady) {
req.session.accesstime = Date.now() + 300000 // 5 minutes
return res.redirect('/admin')
} else {
req.flash('message', {error: true, text: 'Invalid password'})
}
next()
}))
// Ensure that the admin panel is not kept open for prolonged time
router.use(wrap(async (req, res, next) => {
if (req.session.accesstime) {
if (req.session.accesstime > Date.now()) return next()
delete req.session.accesstime
}
res.render('user/password', {post: '/admin'})
}))
/* =========
* VIEWS
* =========
@ -44,6 +86,16 @@ router.get('/oauth2', wrap(async (req, res) => {
* =======
*/
apiRouter.get('/users', wrap(async (req, res) => {
let page = parseInt(req.query.page)
if (isNaN(page) || page < 1) {
page = 1
}
let users = await API.getAllUsers(page)
res.jsonp(users)
}))
router.use('/api', apiRouter)
module.exports = router

View File

@ -684,10 +684,7 @@ router.get('/partials/:view', wrap(async (req, res, next) => {
*/
router.get('/logout', wrap(async (req, res) => {
if (req.session.user) {
delete req.session.user
}
req.session.destroy()
res.redirect('/')
}))

View File

@ -1,3 +1,72 @@
window.$ = require('jquery')
var Mustache = require('mustache')
$(window).ready(function () {})
function buildTemplateScript (id, ctx) {
var tmpl = $('#' + id)
if (!tmpl.length) return null
var data = tmpl.html()
Mustache.parse(data)
return Mustache.render(data, ctx)
}
function paginationButton (pages) {
var html = '<div class="pgn">'
html += '<span class="pagenum">Page ' + pages.page + ' of ' + pages.pages + '</span>'
if (pages.page > 1) {
html += '<div class="button" data-page="' + (pages.page - 1) + '">Previous</div>'
}
for (var i = 0; i < pages.pages; i++) {
html += '<div class="button" data-page="' + (i + 1) + '">' + (i + 1) + '</div>'
}
if (pages.pages > pages.page) {
html += '<div class="button" data-page="' + (pages.page + 1) + '">Next</div>'
}
html += '</div>'
return html
}
function loadUsers (page) {
$.ajax({
type: 'get',
url: '/admin/api/users',
data: {page: page},
success: function (data) {
$('#userlist').html('')
if (data.error) {
$('#userlist').html('<div class="message error">' + data.error + '</div>')
return
}
var pgbtn = paginationButton(data.page)
$('#userlist').append(pgbtn)
$('.pgn .button').click(function (e) {
var pgnum = $(this).data('page')
if (pgnum == null) return
loadUsers(parseInt(pgnum))
})
for (var u in data.users) {
var user = data.users[u]
user.created_at = new Date(user.created_at)
var tmp = buildTemplateScript('user', user)
$('#userlist').append(tmp)
}
}
})
}
$(document).ready(function () {
if ($('#userlist').length) {
loadUsers(1)
}
setInterval(function () {
$.get({
url: '/admin/api/access',
success: function (data) {
if (data && data.access) return
window.location.reload()
}
})
}, 30000)
})

View File

@ -1,13 +1,11 @@
body
margin: 0
background-color: #f5f5f5
font-family: Helvetica
nav
display: block
height: 60px
background-color: #6b6b6b
border-bottom: 5px solid #383838
background-color: #00BCD4
border-bottom: 5px solid #0096a9
ul
display: inline-block
margin: 0
@ -20,14 +18,14 @@ nav
display: inline-block
a
font-size: 180%
padding: 15px
padding: 16px
line-height: 2
color: #ddd
color: #fff
text-decoration: none
text-transform: uppercase
font-weight: bold
&:hover
background-color: #868686
background-color: #00c9e2
.wrapper
min-height: 100vh
@ -35,3 +33,17 @@ nav
.container
overflow: hidden
padding: 10px
background-color: #fff
min-height: 100vh
.user
min-height: 180px
.avatar
float: left
.info
margin-left: 170px
.display_name
font-weight: bold
font-size: 120%
.username
font-size: 80%

View File

@ -4,3 +4,8 @@ block body
.container
.content
h1 Welcome to the Admin Panel
.left
.users
h3 Registered Users
#userlist
.right

View File

@ -1,6 +1,9 @@
html
head
meta(charset="utf8")
link(rel="stylesheet", type="text/css", href="https://fonts.googleapis.com/css?family=Open+Sans")
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")
script.
window.variables = {
@ -15,7 +18,7 @@ html
nav
ul
li
a.logo(href="/") Icy Network
a.navlogo(href="/") Icy Network
li
a(href="/admin/") Home
li
@ -25,3 +28,30 @@ html
a(href="/user/manage") #{user.display_name}
.wrapper
block body
.templates
script(type="x-tmpl-mustache" id="user").
<div class="user" id="user-{{id}}">
<div class="avatar">
{{#avatar_file}}
<img src="/usercontent/images/{{avatar_file}}">
{{/avatar_file}}
{{^avatar_file}}
<img src="/static/image/avatar.png">
{{/avatar_file}}
</div>
<div class="info">
<div class="stamps">
{{^activated}}
<div class="noactive"><i class="fa fa-fw fa-envelope"></i></div>
{{/activated}}
</div>
<div class="display_name">{{display_name}}</div>
<div class="username">{{username}}</div>
<div class="email">{{email}}</div>
<div class="privilege">Privilege: {{nw_privilege}} points</div>
<div class="timestamp">{{created_at}}</div>
{{^password}}
<div class="external"><b>Used external login</b></div>
{{/password}}
</div>
</div>

View File

@ -15,7 +15,7 @@ block body
else
.message
span #{message.text}
form#loginForm(method="POST", action="")
form#loginForm(method="POST", action=post)
input(type="hidden", name="csrf", value=csrf)
label(for="password") Password
input(type="password", name="password", id="password")