some improvements, jquery dialog
This commit is contained in:
parent
9441bfc74c
commit
4c7f72f65c
@ -85,6 +85,12 @@ function extraButtons (req, res, next) {
|
|||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureLogin (req, res, next) {
|
||||||
|
if (req.session.user) return next()
|
||||||
|
req.session.redirectUri = req.originalUrl
|
||||||
|
res.redirect('/login')
|
||||||
|
}
|
||||||
|
|
||||||
router.get('/login', extraButtons, (req, res) => {
|
router.get('/login', extraButtons, (req, res) => {
|
||||||
if (req.session.user) {
|
if (req.session.user) {
|
||||||
let uri = '/'
|
let uri = '/'
|
||||||
@ -118,8 +124,7 @@ router.get('/register', extraButtons, (req, res) => {
|
|||||||
res.render('register')
|
res.render('register')
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/user/two-factor', wrap(async (req, res) => {
|
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
||||||
if (!req.session.user) return res.redirect('/login')
|
|
||||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||||
if (twoFaEnabled) return res.redirect('/')
|
if (twoFaEnabled) return res.redirect('/')
|
||||||
|
|
||||||
@ -129,8 +134,7 @@ router.get('/user/two-factor', wrap(async (req, res) => {
|
|||||||
res.render('totp', { uri: newToken })
|
res.render('totp', { uri: newToken })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/user/two-factor/disable', wrap(async (req, res) => {
|
router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => {
|
||||||
if (!req.session.user) return res.redirect('/login')
|
|
||||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||||
|
|
||||||
if (!twoFaEnabled) return res.redirect('/')
|
if (!twoFaEnabled) return res.redirect('/')
|
||||||
@ -141,9 +145,7 @@ router.get('/login/verify', (req, res) => {
|
|||||||
res.render('totp-check')
|
res.render('totp-check')
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/user/manage', wrap(async (req, res) => {
|
router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||||
if (!req.session.user) return res.redirect('/login')
|
|
||||||
|
|
||||||
let totpEnabled = false
|
let totpEnabled = false
|
||||||
let socialStatus = await API.User.socialStatus(req.session.user)
|
let socialStatus = await API.User.socialStatus(req.session.user)
|
||||||
|
|
||||||
@ -178,15 +180,11 @@ router.get('/user/manage', wrap(async (req, res) => {
|
|||||||
res.render('settings', {totp: totpEnabled, password: socialStatus.password})
|
res.render('settings', {totp: totpEnabled, password: socialStatus.password})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/user/manage/password', wrap(async (req, res) => {
|
router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
|
||||||
if (!req.session.user) return res.redirect('/login')
|
|
||||||
|
|
||||||
res.render('password_new')
|
res.render('password_new')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/user/manage/email', wrap(async (req, res) => {
|
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
|
||||||
if (!req.session.user) return res.redirect('/login')
|
|
||||||
|
|
||||||
let obfuscated = req.session.user.email
|
let obfuscated = req.session.user.email
|
||||||
if (obfuscated) {
|
if (obfuscated) {
|
||||||
let split = obfuscated.split('@')
|
let split = obfuscated.split('@')
|
||||||
@ -216,7 +214,16 @@ function formError (req, res, error, redirect) {
|
|||||||
res.redirect(redirect || parseurl(req).path)
|
res.redirect(redirect || parseurl(req).path)
|
||||||
}
|
}
|
||||||
|
|
||||||
router.post('/user/two-factor', wrap(async (req, res) => {
|
function cleanString (input) {
|
||||||
|
let output = ''
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
output += input.charCodeAt(i) <= 127 ? input.charAt(i) : ''
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post('/user/two-factor', wrap(async (req, res, next) => {
|
||||||
|
if (!req.session.user) return next()
|
||||||
if (!req.body.code) {
|
if (!req.body.code) {
|
||||||
return formError(req, res, 'You need to enter the code.')
|
return formError(req, res, 'You need to enter the code.')
|
||||||
}
|
}
|
||||||
@ -233,7 +240,8 @@ router.post('/user/two-factor', wrap(async (req, res) => {
|
|||||||
res.redirect('/')
|
res.redirect('/')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.post('/user/two-factor/disable', wrap(async (req, res) => {
|
router.post('/user/two-factor/disable', wrap(async (req, res, next) => {
|
||||||
|
if (!req.session.user) return next()
|
||||||
if (req.body.csrf !== req.session.csrf) {
|
if (req.body.csrf !== req.session.csrf) {
|
||||||
return formError(req, res, 'Invalid session! Try reloading the page.')
|
return formError(req, res, 'Invalid session! Try reloading the page.')
|
||||||
}
|
}
|
||||||
@ -250,7 +258,8 @@ router.post('/user/two-factor/disable', wrap(async (req, res) => {
|
|||||||
res.redirect('/')
|
res.redirect('/')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.post('/login/verify', wrap(async (req, res) => {
|
router.post('/login/verify', wrap(async (req, res, next) => {
|
||||||
|
if (req.session.user) return next()
|
||||||
if (req.session.totp_check === null) return res.redirect('/login')
|
if (req.session.totp_check === null) return res.redirect('/login')
|
||||||
if (!req.body.code && !req.body.recovery) {
|
if (!req.body.code && !req.body.recovery) {
|
||||||
return formError(req, res, 'You need to enter the code.')
|
return formError(req, res, 'You need to enter the code.')
|
||||||
@ -291,8 +300,9 @@ router.post('/login/verify', wrap(async (req, res) => {
|
|||||||
res.redirect(uri)
|
res.redirect(uri)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.post('/login', wrap(async (req, res) => {
|
router.post('/login', wrap(async (req, res, next) => {
|
||||||
if (!req.body.username || !req.body.password) {
|
if (req.session.user) return next()
|
||||||
|
if (!req.body.username || !req.body.password || req.body.username === '') {
|
||||||
return res.redirect('/login')
|
return res.redirect('/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +313,8 @@ router.post('/login', wrap(async (req, res) => {
|
|||||||
let user = await API.User.get(req.body.username)
|
let user = await API.User.get(req.body.username)
|
||||||
if (!user) return formError(req, res, 'Invalid username or password.')
|
if (!user) return formError(req, res, 'Invalid username or password.')
|
||||||
|
|
||||||
|
if (!user.password || user.password === '') return formError(req, res, 'Please log in using the buttons on the right.')
|
||||||
|
|
||||||
let pwMatch = await API.User.Login.password(user, req.body.password)
|
let pwMatch = await API.User.Login.password(user, req.body.password)
|
||||||
if (!pwMatch) return formError(req, res, 'Invalid username or password.')
|
if (!pwMatch) return formError(req, res, 'Invalid username or password.')
|
||||||
|
|
||||||
@ -333,7 +345,8 @@ router.post('/login', wrap(async (req, res) => {
|
|||||||
res.redirect(uri)
|
res.redirect(uri)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.post('/register', accountLimiter, wrap(async (req, res) => {
|
router.post('/register', accountLimiter, wrap(async (req, res, next) => {
|
||||||
|
if (req.session.user) return next()
|
||||||
if (!req.body.username || !req.body.display_name || !req.body.password || !req.body.email) {
|
if (!req.body.username || !req.body.display_name || !req.body.password || !req.body.email) {
|
||||||
return formError(req, res, 'Please fill in all the fields.')
|
return formError(req, res, 'Please fill in all the fields.')
|
||||||
}
|
}
|
||||||
@ -344,7 +357,7 @@ router.post('/register', accountLimiter, wrap(async (req, res) => {
|
|||||||
|
|
||||||
// 1st Check: Username Characters and length
|
// 1st Check: Username Characters and length
|
||||||
let username = req.body.username
|
let username = req.body.username
|
||||||
if (!username || !username.match(/^([\w-]{3,26})$/i)) {
|
if (!username || !username.match(/^([\w-_]{3,26})$/i)) {
|
||||||
return formError(req, res, 'Invalid username! Must be between 3-26 characters and composed of alphanumeric characters!')
|
return formError(req, res, 'Invalid username! Must be between 3-26 characters and composed of alphanumeric characters!')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +411,7 @@ router.post('/register', accountLimiter, wrap(async (req, res) => {
|
|||||||
// Attempt to create the user
|
// Attempt to create the user
|
||||||
let newUser = await API.User.Register.newAccount({
|
let newUser = await API.User.Register.newAccount({
|
||||||
username: username,
|
username: username,
|
||||||
display_name: displayName,
|
display_name: cleanString(displayName),
|
||||||
password: hash,
|
password: hash,
|
||||||
email: email,
|
email: email,
|
||||||
ip_address: req.realIP
|
ip_address: req.realIP
|
||||||
@ -428,6 +441,8 @@ router.post('/user/manage', wrap(async (req, res, next) => {
|
|||||||
return formError(req, res, 'Invalid display name!')
|
return formError(req, res, 'Invalid display name!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayName = cleanString(displayName)
|
||||||
|
|
||||||
// No change
|
// No change
|
||||||
if (displayName === req.session.user.display_name) {
|
if (displayName === req.session.user.display_name) {
|
||||||
return res.redirect('/user/manage')
|
return res.redirect('/user/manage')
|
||||||
@ -458,7 +473,8 @@ router.post('/user/manage/password', wrap(async (req, res, next) => {
|
|||||||
return formError(req, res, 'Please enter your current password.')
|
return formError(req, res, 'Please enter your current password.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let passwordMatch = await API.User.Login.password(req.session.user, req.body.password_old)
|
let user = req.session.user
|
||||||
|
let passwordMatch = await API.User.Login.password(user, req.body.password_old)
|
||||||
if (!passwordMatch) {
|
if (!passwordMatch) {
|
||||||
return formError(req, res, 'The password you provided is incorrect.')
|
return formError(req, res, 'The password you provided is incorrect.')
|
||||||
}
|
}
|
||||||
@ -475,7 +491,7 @@ router.post('/user/manage/password', wrap(async (req, res, next) => {
|
|||||||
|
|
||||||
password = await API.User.Register.hashPassword(password)
|
password = await API.User.Register.hashPassword(password)
|
||||||
|
|
||||||
let success = await API.User.update(req.session.user, {
|
let success = await API.User.update(user, {
|
||||||
password: password
|
password: password
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -483,7 +499,6 @@ router.post('/user/manage/password', wrap(async (req, res, next) => {
|
|||||||
return formError(req, res, success.error)
|
return formError(req, res, success.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = req.session.user
|
|
||||||
console.warn('[SECURITY AUDIT] User \'%s\' password has been changed from %s', user.username, req.realIP)
|
console.warn('[SECURITY AUDIT] User \'%s\' password has been changed from %s', user.username, req.realIP)
|
||||||
|
|
||||||
if (config.email && config.email.enabled) {
|
if (config.email && config.email.enabled) {
|
||||||
@ -557,7 +572,11 @@ router.get('/docs/:name', (req, res, next) => {
|
|||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
doc = fs.readFileSync(doc, {encoding: 'utf8'})
|
try {
|
||||||
|
doc = fs.readFileSync(doc, {encoding: 'utf8'})
|
||||||
|
} catch (e) {
|
||||||
|
return next(e)
|
||||||
|
}
|
||||||
|
|
||||||
res.render('document', {doc: doc})
|
res.render('document', {doc: doc})
|
||||||
})
|
})
|
||||||
@ -587,6 +606,12 @@ router.get('/news/', wrap(async (req, res) => {
|
|||||||
res.render('news', {news: news})
|
res.render('news', {news: news})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
router.get('/partials/:view', wrap(async (req, res, next) => {
|
||||||
|
if (!req.params.view) return next()
|
||||||
|
|
||||||
|
res.render('partials/' + req.params.view)
|
||||||
|
}))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=========
|
=========
|
||||||
OTHER
|
OTHER
|
||||||
@ -614,12 +639,34 @@ router.get('/activate/:token', wrap(async (req, res) => {
|
|||||||
|
|
||||||
router.use('/api', apiRouter)
|
router.use('/api', apiRouter)
|
||||||
|
|
||||||
|
/*
|
||||||
|
NO ROUTES BEYOND THIS POINT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Handle 'Failed to lookup view' errors
|
||||||
|
router.use((err, req, res, next) => {
|
||||||
|
if (err && err.stack) {
|
||||||
|
if (err.stack.indexOf('Failed to lookup view') !== -1) {
|
||||||
|
return next() // To 404
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next(err) // To error handler
|
||||||
|
})
|
||||||
|
|
||||||
|
// 404 - last route
|
||||||
router.use((req, res) => {
|
router.use((req, res) => {
|
||||||
res.status(404).render('404')
|
res.status(404).render('404')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Error handler
|
||||||
router.use((err, req, res, next) => {
|
router.use((err, req, res, next) => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
return res.end(err.stack)
|
||||||
|
}
|
||||||
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import express from 'express'
|
|||||||
import uapi from '../api'
|
import uapi from '../api'
|
||||||
import OAuth2 from '../api/oauth2'
|
import OAuth2 from '../api/oauth2'
|
||||||
import RateLimit from 'express-rate-limit'
|
import RateLimit from 'express-rate-limit'
|
||||||
import config from '../../scripts/load-config'
|
|
||||||
import wrap from '../../scripts/asyncRoute'
|
import wrap from '../../scripts/asyncRoute'
|
||||||
|
|
||||||
let router = express.Router()
|
let router = express.Router()
|
||||||
@ -19,12 +18,9 @@ let oauthLimiter = new RateLimit({
|
|||||||
router.use(oauthLimiter)
|
router.use(oauthLimiter)
|
||||||
|
|
||||||
function ensureLoggedIn (req, res, next) {
|
function ensureLoggedIn (req, res, next) {
|
||||||
if (req.session.user) {
|
if (req.session.user) return next()
|
||||||
next()
|
req.session.redirectUri = req.originalUrl
|
||||||
} else {
|
res.redirect('/login')
|
||||||
req.session.redirectUri = req.originalUrl
|
|
||||||
res.redirect('/login')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
router.use('/authorize', ensureLoggedIn, oauth.controller.authorization)
|
router.use('/authorize', ensureLoggedIn, oauth.controller.authorization)
|
||||||
|
@ -15,6 +15,37 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.Dialog = $('#dialog')
|
||||||
|
window.Dialog.open = function (title, content, pad) {
|
||||||
|
$('#dialog #title').text(title)
|
||||||
|
if (pad) {
|
||||||
|
content = '<div class="pad">' + content + '</div>'
|
||||||
|
}
|
||||||
|
$('#dialog #content').html(content)
|
||||||
|
$('#dialog').fadeIn()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Dialog.close = function () {
|
||||||
|
$('#dialog').fadeOut('fast', function () {
|
||||||
|
$('#dialog #content').html('')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Dialog.openPartial = function (title, partial) {
|
||||||
|
$.get({
|
||||||
|
url: '/partials/' + partial,
|
||||||
|
success: function (html) {
|
||||||
|
window.Dialog.open(title, html, false)
|
||||||
|
}
|
||||||
|
}).fail(function (e) {
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#dialog #close').click(function (e) {
|
||||||
|
window.Dialog.close()
|
||||||
|
})
|
||||||
|
|
||||||
if (window.location.hash) {
|
if (window.location.hash) {
|
||||||
var locha = window.location.hash
|
var locha = window.location.hash
|
||||||
if ($(locha).length) {
|
if ($(locha).length) {
|
||||||
|
@ -441,6 +441,50 @@ input.invalid
|
|||||||
.option
|
.option
|
||||||
display: block
|
display: block
|
||||||
|
|
||||||
|
.dialog-drop
|
||||||
|
display: block
|
||||||
|
position: fixed
|
||||||
|
display: none
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
|
bottom: 0
|
||||||
|
right: 0
|
||||||
|
background-color: rgba(56, 56, 56, 0.8)
|
||||||
|
z-index: 10
|
||||||
|
.dialog
|
||||||
|
min-width: 400px
|
||||||
|
width: fit-content
|
||||||
|
max-width: 50vw
|
||||||
|
min-height: 180px
|
||||||
|
height: fit-content
|
||||||
|
background-color: #fff
|
||||||
|
margin: auto
|
||||||
|
margin-top: 5%
|
||||||
|
transition: all 0.1s linear
|
||||||
|
overflow: hidden
|
||||||
|
.pad
|
||||||
|
padding: 5px
|
||||||
|
.head
|
||||||
|
height: 40px
|
||||||
|
background-color: #e6e6e6
|
||||||
|
box-shadow: 0px 3px #ddd
|
||||||
|
overflow: hidden
|
||||||
|
position: relative
|
||||||
|
#title
|
||||||
|
max-width: 80%
|
||||||
|
text-overflow: ellipsis
|
||||||
|
overflow: hidden
|
||||||
|
white-space: nowrap
|
||||||
|
margin: 12px
|
||||||
|
display: block
|
||||||
|
#close
|
||||||
|
position: absolute
|
||||||
|
top: 6px
|
||||||
|
right: 5px
|
||||||
|
color: #ff2525
|
||||||
|
cursor: pointer
|
||||||
|
padding: 5px
|
||||||
|
|
||||||
@media all and (max-width: 800px)
|
@media all and (max-width: 800px)
|
||||||
.navigator
|
.navigator
|
||||||
padding: 0 10px
|
padding: 0 10px
|
||||||
|
@ -15,6 +15,14 @@ html
|
|||||||
.logo
|
.logo
|
||||||
.part1 Icy
|
.part1 Icy
|
||||||
.part2 Network
|
.part2 Network
|
||||||
|
block dialog
|
||||||
|
.dialog-drop#dialog
|
||||||
|
.dialog
|
||||||
|
.head
|
||||||
|
#title
|
||||||
|
#close
|
||||||
|
i.fa.fa-fw.fa-times
|
||||||
|
.content#content
|
||||||
block nav
|
block nav
|
||||||
.anchor
|
.anchor
|
||||||
nav.navigator
|
nav.navigator
|
||||||
|
1
views/partials/avatar.pug
Normal file
1
views/partials/avatar.pug
Normal file
@ -0,0 +1 @@
|
|||||||
|
img(src=user.avatar_file)
|
Reference in New Issue
Block a user