some improvements, jquery dialog
This commit is contained in:
parent
9441bfc74c
commit
4c7f72f65c
@ -85,6 +85,12 @@ function extraButtons (req, res, 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) => {
|
||||
if (req.session.user) {
|
||||
let uri = '/'
|
||||
@ -118,8 +124,7 @@ router.get('/register', extraButtons, (req, res) => {
|
||||
res.render('register')
|
||||
})
|
||||
|
||||
router.get('/user/two-factor', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||
if (twoFaEnabled) return res.redirect('/')
|
||||
|
||||
@ -129,8 +134,7 @@ router.get('/user/two-factor', wrap(async (req, res) => {
|
||||
res.render('totp', { uri: newToken })
|
||||
}))
|
||||
|
||||
router.get('/user/two-factor/disable', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => {
|
||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||
|
||||
if (!twoFaEnabled) return res.redirect('/')
|
||||
@ -141,9 +145,7 @@ router.get('/login/verify', (req, res) => {
|
||||
res.render('totp-check')
|
||||
})
|
||||
|
||||
router.get('/user/manage', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
|
||||
router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||
let totpEnabled = false
|
||||
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})
|
||||
}))
|
||||
|
||||
router.get('/user/manage/password', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
|
||||
router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
|
||||
res.render('password_new')
|
||||
}))
|
||||
|
||||
router.get('/user/manage/email', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
|
||||
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
|
||||
let obfuscated = req.session.user.email
|
||||
if (obfuscated) {
|
||||
let split = obfuscated.split('@')
|
||||
@ -216,7 +214,16 @@ function formError (req, res, error, redirect) {
|
||||
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) {
|
||||
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('/')
|
||||
}))
|
||||
|
||||
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) {
|
||||
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('/')
|
||||
}))
|
||||
|
||||
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.body.code && !req.body.recovery) {
|
||||
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)
|
||||
}))
|
||||
|
||||
router.post('/login', wrap(async (req, res) => {
|
||||
if (!req.body.username || !req.body.password) {
|
||||
router.post('/login', wrap(async (req, res, next) => {
|
||||
if (req.session.user) return next()
|
||||
if (!req.body.username || !req.body.password || req.body.username === '') {
|
||||
return res.redirect('/login')
|
||||
}
|
||||
|
||||
@ -303,6 +313,8 @@ router.post('/login', wrap(async (req, res) => {
|
||||
let user = await API.User.get(req.body.username)
|
||||
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)
|
||||
if (!pwMatch) return formError(req, res, 'Invalid username or password.')
|
||||
|
||||
@ -333,7 +345,8 @@ router.post('/login', wrap(async (req, res) => {
|
||||
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) {
|
||||
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
|
||||
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!')
|
||||
}
|
||||
|
||||
@ -398,7 +411,7 @@ router.post('/register', accountLimiter, wrap(async (req, res) => {
|
||||
// Attempt to create the user
|
||||
let newUser = await API.User.Register.newAccount({
|
||||
username: username,
|
||||
display_name: displayName,
|
||||
display_name: cleanString(displayName),
|
||||
password: hash,
|
||||
email: email,
|
||||
ip_address: req.realIP
|
||||
@ -428,6 +441,8 @@ router.post('/user/manage', wrap(async (req, res, next) => {
|
||||
return formError(req, res, 'Invalid display name!')
|
||||
}
|
||||
|
||||
displayName = cleanString(displayName)
|
||||
|
||||
// No change
|
||||
if (displayName === req.session.user.display_name) {
|
||||
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.')
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
|
||||
let success = await API.User.update(req.session.user, {
|
||||
let success = await API.User.update(user, {
|
||||
password: password
|
||||
})
|
||||
|
||||
@ -483,7 +499,6 @@ router.post('/user/manage/password', wrap(async (req, res, next) => {
|
||||
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)
|
||||
|
||||
if (config.email && config.email.enabled) {
|
||||
@ -557,7 +572,11 @@ router.get('/docs/:name', (req, res, next) => {
|
||||
return next()
|
||||
}
|
||||
|
||||
try {
|
||||
doc = fs.readFileSync(doc, {encoding: 'utf8'})
|
||||
} catch (e) {
|
||||
return next(e)
|
||||
}
|
||||
|
||||
res.render('document', {doc: doc})
|
||||
})
|
||||
@ -587,6 +606,12 @@ router.get('/news/', wrap(async (req, res) => {
|
||||
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
|
||||
@ -614,12 +639,34 @@ router.get('/activate/:token', wrap(async (req, res) => {
|
||||
|
||||
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) => {
|
||||
res.status(404).render('404')
|
||||
})
|
||||
|
||||
// Error handler
|
||||
router.use((err, req, res, next) => {
|
||||
console.error(err)
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return res.end(err.stack)
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
|
@ -2,7 +2,6 @@ import express from 'express'
|
||||
import uapi from '../api'
|
||||
import OAuth2 from '../api/oauth2'
|
||||
import RateLimit from 'express-rate-limit'
|
||||
import config from '../../scripts/load-config'
|
||||
import wrap from '../../scripts/asyncRoute'
|
||||
|
||||
let router = express.Router()
|
||||
@ -19,12 +18,9 @@ let oauthLimiter = new RateLimit({
|
||||
router.use(oauthLimiter)
|
||||
|
||||
function ensureLoggedIn (req, res, next) {
|
||||
if (req.session.user) {
|
||||
next()
|
||||
} else {
|
||||
if (req.session.user) return next()
|
||||
req.session.redirectUri = req.originalUrl
|
||||
res.redirect('/login')
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
var locha = window.location.hash
|
||||
if ($(locha).length) {
|
||||
|
@ -441,6 +441,50 @@ input.invalid
|
||||
.option
|
||||
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)
|
||||
.navigator
|
||||
padding: 0 10px
|
||||
|
@ -15,6 +15,14 @@ html
|
||||
.logo
|
||||
.part1 Icy
|
||||
.part2 Network
|
||||
block dialog
|
||||
.dialog-drop#dialog
|
||||
.dialog
|
||||
.head
|
||||
#title
|
||||
#close
|
||||
i.fa.fa-fw.fa-times
|
||||
.content#content
|
||||
block nav
|
||||
.anchor
|
||||
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