Maintenance commit.

This commit is contained in:
Evert Prants 2020-05-28 21:30:21 +03:00
parent f608316e89
commit 787cc6fc73
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
42 changed files with 4242 additions and 4093 deletions

7224
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -30,58 +30,58 @@
}, },
"homepage": "https://icynet.eu", "homepage": "https://icynet.eu",
"dependencies": { "dependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.8.3", "@babel/plugin-transform-modules-commonjs": "^7.10.1",
"@babel/register": "^7.8.3", "@babel/register": "^7.10.1",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"connect-redis": "^3.4.2", "connect-redis": "^4.0.4",
"connect-session-knex": "^1.5.0", "connect-session-knex": "^1.6.0",
"email-templates": "^2.7.1", "email-templates": "^7.0.5",
"express": "^4.17.1", "express": "^4.17.1",
"express-rate-limit": "^2.14.2", "express-rate-limit": "^5.1.3",
"express-session": "^1.17.0", "express-session": "^1.17.1",
"feed": "^1.1.1", "feed": "^4.2.0",
"fs-extra": "^4.0.3", "fs-extra": "^9.0.0",
"gm": "^1.23.1", "gm": "^1.23.1",
"knex": "^0.14.6", "knex": "^0.21.1",
"multiparty": "^4.2.1", "multiparty": "^4.2.1",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"nodemailer": "^4.7.0", "nodemailer": "^6.4.8",
"notp": "^2.0.3", "notp": "^2.0.3",
"oauth-libre": "^0.9.17", "oauth-libre": "^0.9.17",
"objection": "^0.8.9", "objection": "^2.1.5",
"redis": "^2.8.0", "redis": "^3.0.2",
"serve-favicon": "^2.5.0", "serve-favicon": "^2.5.0",
"stylus": "^0.54.7", "stylus": "^0.54.7",
"thirty-two": "^1.0.2", "thirty-two": "^1.0.2",
"toml": "^2.3.6", "toml": "^3.0.0",
"uuid": "^3.4.0", "uuid": "^8.1.0",
"vue": "^2.6.11" "vue": "^2.6.11"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.8.3", "@babel/core": "^7.10.1",
"@babel/preset-env": "^7.8.3", "@babel/preset-env": "^7.10.1",
"babel-loader": "^8.0.6", "babel-loader": "^8.1.0",
"bootstrap": "^4.4.1", "bootstrap": "^4.5.0",
"concurrently": "^5.1.0", "concurrently": "^5.2.0",
"eslint-plugin-import": "^2.20.0", "eslint-plugin-import": "^2.20.2",
"jquery": "^3.4.1", "jquery": "^3.5.1",
"morgan": "^1.9.1", "morgan": "^1.10.0",
"mustache": "^2.3.2", "mustache": "^4.0.1",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"pug": "^2.0.4", "pug": "^3.0.0",
"pug-plain-loader": "^1.0.0", "pug-plain-loader": "^1.0.0",
"standard": "^10.0.3", "standard": "^14.3.4",
"terser-webpack-plugin": "^2.3.3", "terser-webpack-plugin": "^3.0.2",
"vue-clickaway": "^2.2.2", "vue-clickaway": "^2.2.2",
"vue-loader": "^15.8.3", "vue-loader": "^15.9.2",
"vue-resource": "^1.5.1", "vue-resource": "^1.5.1",
"vue-router": "^3.1.5", "vue-router": "^3.3.1",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.11",
"watch": "^1.0.2", "watch": "^1.0.2",
"webpack": "^4.41.5", "webpack": "^4.43.0",
"webpack-cli": "^3.3.10", "webpack-cli": "^3.3.11",
"webpack-merge": "^4.2.2" "webpack-merge": "^4.2.2"
}, },
"standard": { "standard": {

View File

@ -8,7 +8,7 @@ const format = util.format
module.exports = function (options) { module.exports = function (options) {
options = options || {} options = options || {}
let safe = (options.unsafe === undefined) ? true : !options.unsafe const safe = (options.unsafe === undefined) ? true : !options.unsafe
return function (req, res, next) { return function (req, res, next) {
if (req.flash && safe) { return next() } if (req.flash && safe) { return next() }
@ -20,10 +20,10 @@ module.exports = function (options) {
function _flash (type, msg) { function _flash (type, msg) {
if (this.session === undefined) throw Error('req.flash() requires sessions') if (this.session === undefined) throw Error('req.flash() requires sessions')
let msgs = this.session.flash = this.session.flash || {} const msgs = this.session.flash = this.session.flash || {}
if (type && msg) { if (type && msg) {
if (arguments.length > 2 && format) { if (arguments.length > 2 && format) {
let args = Array.prototype.slice.call(arguments, 1) const args = Array.prototype.slice.call(arguments, 1)
msg = format.apply(undefined, args) msg = format.apply(undefined, args)
} else if (Array.isArray(msg)) { } else if (Array.isArray(msg)) {
msg.forEach((val) => { msg.forEach((val) => {
@ -33,7 +33,7 @@ function _flash (type, msg) {
} }
return (msgs[type] = msgs[type] || []).push(msg) return (msgs[type] = msgs[type] || []).push(msg)
} else if (type) { } else if (type) {
let arr = msgs[type] const arr = msgs[type]
delete msgs[type] delete msgs[type]
return arr || [] return arr || []
} else { } else {

View File

@ -1,17 +1,17 @@
import url from 'url' import { URL } from 'url'
import qs from 'querystring' import qs from 'querystring'
import fs from 'fs' import fs from 'fs'
function HTTP_GET (link, headers = {}, lback) { function HTTP_GET (link, headers = {}, lback) {
if (lback && lback >= 4) throw new Error('infinite loop!') // Prevent infinite loop requests if (lback && lback >= 4) throw new Error('infinite loop!') // Prevent infinite loop requests
let parsed = url.parse(link) const parsed = new URL(link)
let opts = { const opts = {
host: parsed.hostname, host: parsed.hostname,
port: parsed.port, port: parsed.port,
path: parsed.path, path: parsed.path,
headers: { headers: {
'User-Agent': 'Icy Network Back-end (icynet.eu)', 'User-Agent': 'Icy Network Back-end (icynet.eu)',
'Accept': '*/*', Accept: '*/*',
'Accept-Language': 'en-GB,enq=0.5' 'Accept-Language': 'en-GB,enq=0.5'
} }
} }
@ -22,9 +22,9 @@ function HTTP_GET (link, headers = {}, lback) {
let reqTimeOut let reqTimeOut
let httpModule = parsed.protocol === 'https:' ? require('https') : require('http') const httpModule = parsed.protocol === 'https:' ? require('https') : require('http')
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let req = httpModule.get(opts, (res) => { const req = httpModule.get(opts, (res) => {
if (res.statusCode === 302 || res.statusCode === 301) { if (res.statusCode === 302 || res.statusCode === 301) {
if (!lback) { if (!lback) {
lback = 1 lback = 1
@ -61,10 +61,10 @@ function HTTP_GET (link, headers = {}, lback) {
} }
function HTTP_POST (link, headers = {}, data) { function HTTP_POST (link, headers = {}, data) {
let parsed = url.parse(link) const parsed = new URL(link)
let postData = qs.stringify(data) let postData = qs.stringify(data)
let opts = { const opts = {
host: parsed.host, host: parsed.host,
port: parsed.port, port: parsed.port,
path: parsed.path, path: parsed.path,
@ -85,8 +85,8 @@ function HTTP_POST (link, headers = {}, data) {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let httpModule = parsed.protocol === 'https:' ? require('https') : require('http') const httpModule = parsed.protocol === 'https:' ? require('https') : require('http')
let req = httpModule.request(opts, (res) => { const req = httpModule.request(opts, (res) => {
res.setEncoding('utf8') res.setEncoding('utf8')
let data = '' let data = ''
@ -108,8 +108,8 @@ function HTTP_POST (link, headers = {}, data) {
async function Download (url, dest) { async function Download (url, dest) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let file = fs.createWriteStream(dest) const file = fs.createWriteStream(dest)
let protocol = url.indexOf('https:') === 0 ? require('https') : require('http') const protocol = url.indexOf('https:') === 0 ? require('https') : require('http')
protocol.get(url, function (response) { protocol.get(url, function (response) {
response.pipe(file) response.pipe(file)
file.on('finish', function () { file.on('finish', function () {

View File

@ -3,8 +3,8 @@ const knex = require('knex')
const objection = require('objection') const objection = require('objection')
const knexfile = require(path.join(__dirname, '../knexfile')) const knexfile = require(path.join(__dirname, '../knexfile'))
let knexDB = knex(knexfile) const knexDB = knex(knexfile)
let objectionModel = objection.Model const objectionModel = objection.Model
objectionModel.knex(knexDB) objectionModel.knex(knexDB)

View File

@ -20,11 +20,12 @@ function dateFormat (date) {
// Console.log/error/warn "middleware" - add timestamp and write to file // Console.log/error/warn "middleware" - add timestamp and write to file
function stampAndWrite (fnc, color, prfx, message) { function stampAndWrite (fnc, color, prfx, message) {
let prefix = '[' + prfx + '] [' + dateFormat(new Date()) + '] ' const prefix = '[' + prfx + '] [' + dateFormat(new Date()) + '] '
message = color + prefix + message + ((color && color !== '') ? '\x1b[0m' : '') message = color + prefix + message + ((color && color !== '') ? '\x1b[0m' : '')
message = message.replace(/\\u001b/g, '\x1b') message = message.replace(/\\u001b/g, '\x1b')
if (lfs) { if (lfs) {
// eslint-disable-next-line no-control-regex
lfs.write(message.replace(/(\u001b\[\d\d?m)/g, '') + '\n') lfs.write(message.replace(/(\u001b\[\d\d?m)/g, '') + '\n')
} }
@ -34,24 +35,24 @@ function stampAndWrite (fnc, color, prfx, message) {
// Reassign logger functions and send them to the "middleware" // Reassign logger functions and send them to the "middleware"
const realConsoleLog = console.log const realConsoleLog = console.log
console.log = function () { console.log = function () {
let message = util.format.apply(null, arguments) const message = util.format.apply(null, arguments)
stampAndWrite.call(this, realConsoleLog, '', 'info', message) stampAndWrite.call(this, realConsoleLog, '', 'info', message)
} }
const realConsoleWarn = console.warn const realConsoleWarn = console.warn
console.warn = function () { console.warn = function () {
let message = util.format.apply(null, arguments) const message = util.format.apply(null, arguments)
stampAndWrite.call(this, realConsoleWarn, '\x1b[33m', 'warn', message) stampAndWrite.call(this, realConsoleWarn, '\x1b[33m', 'warn', message)
} }
const realConsoleError = console.error const realConsoleError = console.error
console.error = function () { console.error = function () {
let message = util.format.apply(null, arguments) const message = util.format.apply(null, arguments)
stampAndWrite.call(this, realConsoleError, '\x1b[31m', ' err', message) stampAndWrite.call(this, realConsoleError, '\x1b[31m', ' err', message)
} }
async function initializeLogger () { async function initializeLogger () {
let logPath = path.resolve(config.logger.file) const logPath = path.resolve(config.logger.file)
// Only throw bad permission errors // Only throw bad permission errors
try { try {

View File

@ -4,7 +4,7 @@ import Models from './models'
const perPage = 6 const perPage = 6
async function cleanUserObject (dbe, admin) { async function cleanUserObject (dbe, admin) {
let totp = await Users.User.Login.totpTokenRequired(dbe) const totp = await Users.User.Login.totpTokenRequired(dbe)
return { return {
id: dbe.id, id: dbe.id,
@ -25,7 +25,7 @@ async function cleanUserObject (dbe, admin) {
} }
async function cleanClientObject (dbe) { async function cleanClientObject (dbe) {
let user = await Users.User.get(dbe.user_id) const user = await Users.User.get(dbe.user_id)
return { return {
id: dbe.id, id: dbe.id,
title: dbe.title, title: dbe.title,
@ -46,8 +46,8 @@ async function cleanClientObject (dbe) {
} }
async function cleanBanObject (dbe) { async function cleanBanObject (dbe) {
let user = await Users.User.get(dbe.user_id) const user = await Users.User.get(dbe.user_id)
let admin = await Users.User.get(dbe.admin_id) const admin = await Users.User.get(dbe.admin_id)
return { return {
id: dbe.id, id: dbe.id,
reason: dbe.reason, reason: dbe.reason,
@ -68,14 +68,14 @@ async function cleanBanObject (dbe) {
function dataFilter (data, fields, optional = []) { function dataFilter (data, fields, optional = []) {
// Remove keys not listed in `fields` // Remove keys not listed in `fields`
for (let i in data) { for (const i in data) {
if (fields.indexOf(i) === -1) { if (fields.indexOf(i) === -1) {
delete data[i] delete data[i]
} }
} }
// Ensure all the entries in `fields` are present as keys in `data` or are `optional` // Ensure all the entries in `fields` are present as keys in `data` or are `optional`
for (let j in fields) { for (const j in fields) {
if (!data[fields[j]] && optional.indexOf(fields[j]) === -1) return null if (!data[fields[j]] && optional.indexOf(fields[j]) === -1) return null
} }
@ -86,18 +86,18 @@ const API = {
// List all users (paginated) // List all users (paginated)
getAllUsers: async function (page, adminId) { getAllUsers: async function (page, adminId) {
let count = await Models.User.query().count('id as ids') let count = await Models.User.query().count('id as ids')
if (!count.length || !count[0]['ids'] || isNaN(page)) { if (!count.length || !count[0].ids || isNaN(page)) {
return { error: 'No users found in database' } return { error: 'No users found in database' }
} }
count = count[0].ids count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page) const paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.User.query().offset(paginated.offset).limit(perPage) const raw = await Models.User.query().offset(paginated.offset).limit(perPage)
let admin = await Users.User.get(adminId) const admin = await Users.User.get(adminId)
let users = [] const users = []
for (let i in raw) { for (const i in raw) {
let entry = raw[i] const entry = raw[i]
users.push(await cleanUserObject(entry, admin)) users.push(await cleanUserObject(entry, admin))
} }
@ -108,16 +108,16 @@ const API = {
} }
}, },
getUser: async function (id) { getUser: async function (id) {
let user = await Users.User.get(id) const user = await Users.User.get(id)
if (!user) throw new Error('No such user') if (!user) throw new Error('No such user')
return cleanUserObject(user, null) return cleanUserObject(user, null)
}, },
editUser: async function (id, data) { editUser: async function (id, data) {
let user = await Users.User.get(id) const user = await Users.User.get(id)
if (!user) throw new Error('No such user') if (!user) throw new Error('No such user')
let fields = [ const fields = [
'username', 'display_name', 'email', 'nw_privilege', 'activated' 'username', 'display_name', 'email', 'nw_privilege', 'activated'
] ]
@ -129,7 +129,7 @@ const API = {
return {} return {}
}, },
resendActivationEmail: async function (id) { resendActivationEmail: async function (id) {
let user = await Users.User.get(id) const user = await Users.User.get(id)
if (!user) throw new Error('No such user') if (!user) throw new Error('No such user')
if (user.activated === 1) return {} if (user.activated === 1) return {}
@ -139,7 +139,7 @@ const API = {
return {} return {}
}, },
revokeTotpToken: async function (id) { revokeTotpToken: async function (id) {
let user = await Users.User.get(id) const user = await Users.User.get(id)
if (!user) throw new Error('No such user') if (!user) throw new Error('No such user')
await Models.TotpToken.query().delete().where('user_id', user.id) await Models.TotpToken.query().delete().where('user_id', user.id)
@ -147,10 +147,10 @@ const API = {
return {} return {}
}, },
sendPasswordEmail: async function (id) { sendPasswordEmail: async function (id) {
let user = await Users.User.get(id) const user = await Users.User.get(id)
if (!user) throw new Error('No such user') if (!user) throw new Error('No such user')
let token = await Users.User.Reset.reset(user.email, false, true) const token = await Users.User.Reset.reset(user.email, false, true)
return { token } return { token }
}, },
@ -167,12 +167,12 @@ const API = {
} }
} }
let rows = await qb.limit(8) const rows = await qb.limit(8)
if (!rows.length) return { error: 'No results' } if (!rows.length) return { error: 'No results' }
let cleaned = [] const cleaned = []
for (let i in rows) { for (const i in rows) {
let userRaw = rows[i] const userRaw = rows[i]
cleaned.push(await cleanUserObject(userRaw, null)) cleaned.push(await cleanUserObject(userRaw, null))
} }
@ -181,17 +181,17 @@ const API = {
// List all clients (paginated) // List all clients (paginated)
getAllClients: async function (page) { getAllClients: async function (page) {
let count = await Models.OAuth2Client.query().count('id as ids') let count = await Models.OAuth2Client.query().count('id as ids')
if (!count.length || !count[0]['ids'] || isNaN(page)) { if (!count.length || !count[0].ids || isNaN(page)) {
return { error: 'No clients' } return { error: 'No clients' }
} }
count = count[0].ids count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page) const paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage) const raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage)
let clients = [] const clients = []
for (let i in raw) { for (const i in raw) {
let entry = raw[i] const entry = raw[i]
clients.push(await cleanClientObject(entry)) clients.push(await cleanClientObject(entry))
} }
@ -202,14 +202,14 @@ const API = {
}, },
// Get information about a client via id // Get information about a client via id
getClient: async function (id) { getClient: async function (id) {
let raw = await Models.OAuth2Client.query().where('id', id) const raw = await Models.OAuth2Client.query().where('id', id)
if (!raw.length) throw new Error('No such client') if (!raw.length) throw new Error('No such client')
return cleanClientObject(raw[0]) return cleanClientObject(raw[0])
}, },
// Update a client `id` in database with `data` // Update a client `id` in database with `data`
updateClient: async function (id, data) { updateClient: async function (id, data) {
let fields = [ const fields = [
'title', 'description', 'url', 'redirect_url', 'scope', 'verified' 'title', 'description', 'url', 'redirect_url', 'scope', 'verified'
] ]
@ -228,7 +228,7 @@ const API = {
// Create a new secret for a client // Create a new secret for a client
newSecret: async function (id) { newSecret: async function (id) {
if (isNaN(id)) throw new Error('Invalid client ID') if (isNaN(id)) throw new Error('Invalid client ID')
let secret = Users.Hash(16) const secret = Users.Hash(16)
try { try {
await Models.OAuth2Client.query().patchAndFetchById(id, { secret: secret }) await Models.OAuth2Client.query().patchAndFetchById(id, { secret: secret })
@ -240,14 +240,14 @@ const API = {
}, },
// Create a new client // Create a new client
createClient: async function (data, user) { createClient: async function (data, user) {
let fields = [ const fields = [
'title', 'description', 'url', 'redirect_url', 'scope' 'title', 'description', 'url', 'redirect_url', 'scope'
] ]
data = dataFilter(data, fields, ['scope']) data = dataFilter(data, fields, ['scope'])
if (!data) throw new Error('Missing fields') if (!data) throw new Error('Missing fields')
let obj = Object.assign({ const obj = Object.assign({
secret: Users.Hash(16), secret: Users.Hash(16),
grants: 'authorization_code', grants: 'authorization_code',
created_at: new Date(), created_at: new Date(),
@ -268,17 +268,17 @@ const API = {
// List all bans (paginated) // List all bans (paginated)
getAllBans: async function (page) { getAllBans: async function (page) {
let count = await Models.Ban.query().count('id as ids') let count = await Models.Ban.query().count('id as ids')
if (!count.length || !count[0]['ids'] || isNaN(page)) { if (!count.length || !count[0].ids || isNaN(page)) {
return { error: 'No bans on record' } return { error: 'No bans on record' }
} }
count = count[0].ids count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page) const paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.Ban.query().offset(paginated.offset).limit(perPage) const raw = await Models.Ban.query().offset(paginated.offset).limit(perPage)
let bans = [] const bans = []
for (let i in raw) { for (const i in raw) {
let entry = raw[i] const entry = raw[i]
bans.push(await cleanBanObject(entry)) bans.push(await cleanBanObject(entry))
} }
@ -293,16 +293,16 @@ const API = {
}, },
// Create a ban // Create a ban
addBan: async function (data, adminId) { addBan: async function (data, adminId) {
let user = await Users.User.get(parseInt(data.user_id)) const user = await Users.User.get(parseInt(data.user_id))
if (!user) throw new Error('No such user.') if (!user) throw new Error('No such user.')
if (user.id === adminId) throw new Error('Cannot ban yourself!') if (user.id === adminId) throw new Error('Cannot ban yourself!')
let admin = await Users.User.get(adminId) const admin = await Users.User.get(adminId)
if (user.nw_privilege > admin.nw_privilege) throw new Error('Cannot ban user.') if (user.nw_privilege > admin.nw_privilege) throw new Error('Cannot ban user.')
let banAdd = { const banAdd = {
reason: data.reason || 'Unspecified ban', reason: data.reason || 'Unspecified ban',
admin_id: adminId, admin_id: adminId,
user_id: user.id, user_id: user.id,
@ -315,13 +315,13 @@ const API = {
return {} return {}
}, },
lockAccount: async function (userId) { lockAccount: async function (userId) {
let user = await Users.User.get(userId) const user = await Users.User.get(userId)
if (user.id === 1 || user.nw_privilege > 2) { if (user.id === 1 || user.nw_privilege > 2) {
throw new Error('Cannot lock this user.') throw new Error('Cannot lock this user.')
} }
let lockId = Users.Hash(4) const lockId = Users.Hash(4)
let userObf = { const userObf = {
username: lockId, username: lockId,
display_name: user.username, display_name: user.username,
email: `${lockId}@icynet.eu`, email: `${lockId}@icynet.eu`,

View File

@ -10,7 +10,7 @@ tls.DEFAULT_ECDH_CURVE = 'auto'
const templateDir = path.join(__dirname, '../../', 'templates') const templateDir = path.join(__dirname, '../../', 'templates')
let templateCache = {} const templateCache = {}
let transporter let transporter
// Send an email to `email` with `headers` // Send an email to `email` with `headers`
@ -42,7 +42,7 @@ async function pushMail (template, email, context) {
templ = templateCache[template] templ = templateCache[template]
} }
let result = await templ.render(context) const result = await templ.render(context)
console.debug('Mail being sent: %s to %s', template, email) console.debug('Mail being sent: %s to %s', template, email)

View File

@ -1,6 +1,6 @@
import qs from 'querystring' import qs from 'querystring'
import oauth from 'oauth-libre' import oauth from 'oauth-libre'
import uuidV1 from 'uuid/v1' import { v1 as uuidV1 } from 'uuid'
import crypto from 'crypto' import crypto from 'crypto'
import config from '../../scripts/load-config' import config from '../../scripts/load-config'
@ -18,7 +18,7 @@ const API = {
Common: { Common: {
// Generate a hash based on the current session // Generate a hash based on the current session
stateGenerator: (req) => { stateGenerator: (req) => {
let sessionCrypto = req.session.id + ':' + config.server.session_secret const sessionCrypto = req.session.id + ':' + config.server.session_secret
return crypto.createHash('sha256').update(sessionCrypto).digest('hex') return crypto.createHash('sha256').update(sessionCrypto).digest('hex')
}, },
// Find an user with an external ID // Find an user with an external ID
@ -29,7 +29,7 @@ const API = {
extr.user = null extr.user = null
if (extr.user_id !== null) { if (extr.user_id !== null) {
let user = await UAPI.User.get(extr.user_id) const user = await UAPI.User.get(extr.user_id)
if (user) { if (user) {
extr.user = user extr.user = user
} }
@ -39,12 +39,12 @@ const API = {
}, },
// Get user ban status // Get user ban status
getBan: async (user, ipAddress) => { getBan: async (user, ipAddress) => {
let banList = await UAPI.User.getBanStatus(ipAddress || user.id, ipAddress != null) const banList = await UAPI.User.getBanStatus(ipAddress || user.id, ipAddress != null)
return banList return banList
}, },
// Create a new `external` instance for a user // Create a new `external` instance for a user
new: async (service, identifier, user) => { new: async (service, identifier, user) => {
let data = { const data = {
user_id: user.id, user_id: user.id,
service: service, service: service,
identifier: identifier, identifier: identifier,
@ -57,7 +57,7 @@ const API = {
// Create a new user // Create a new user
newUser: async (service, identifier, data) => { newUser: async (service, identifier, data) => {
if (config.external.registrations !== true) throw new Error('Registrations from third-party websites are not allowed.') if (config.external.registrations !== true) throw new Error('Registrations from third-party websites are not allowed.')
let udataLimited = Object.assign({ const udataLimited = Object.assign({
activated: 1, activated: 1,
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
@ -83,14 +83,14 @@ const API = {
// Check if the email given to us is already registered, if so, // Check if the email given to us is already registered, if so,
// tell them to log in first. // tell them to log in first.
if (udataLimited.email && udataLimited.email !== '') { if (udataLimited.email && udataLimited.email !== '') {
let getByEmail = await UAPI.User.get(udataLimited.email) const getByEmail = await UAPI.User.get(udataLimited.email)
if (getByEmail) { if (getByEmail) {
throw new Error('An user with this email address is already registered, but this external account is are not linked. If you wish to link the account, please log in first.') throw new Error('An user with this email address is already registered, but this external account is are not linked. If you wish to link the account, please log in first.')
} }
} }
// Create a new user based on the information we got from an external service // Create a new user based on the information we got from an external service
let newUser = await models.User.query().insert(udataLimited) const newUser = await models.User.query().insert(udataLimited)
await API.Common.new(service, identifier, newUser) await API.Common.new(service, identifier, newUser)
return newUser return newUser
@ -98,7 +98,7 @@ const API = {
// Remove an `external` object (thus unlinking from a service) // Remove an `external` object (thus unlinking from a service)
remove: async (user, service) => { remove: async (user, service) => {
user = await UAPI.User.ensureObject(user, ['password']) user = await UAPI.User.ensureObject(user, ['password'])
let userExterns = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id) const userExterns = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
if (!userExterns.length) { if (!userExterns.length) {
return false return false
@ -113,11 +113,11 @@ const API = {
}, },
// Common code for all auth callbacks // Common code for all auth callbacks
callback: async (identifier, uid, user, ipAddress, remoteData, avatarFunc) => { callback: async (identifier, uid, user, ipAddress, remoteData, avatarFunc) => {
let exists = await API.Common.getExternal(identifier, uid) const exists = await API.Common.getExternal(identifier, uid)
if (user) { if (user) {
// Get bans for user // Get bans for user
let bans = await API.Common.getBan(user) const bans = await API.Common.getBan(user)
if (bans.length) return { banned: bans, ip: false } if (bans.length) return { banned: bans, ip: false }
if (exists) return { error: null, user: user } if (exists) return { error: null, user: user }
@ -129,14 +129,14 @@ const API = {
// Callback succeeded with user id and the external table exists, we log in the user // Callback succeeded with user id and the external table exists, we log in the user
if (exists) { if (exists) {
// Get bans for user // Get bans for user
let bans = await API.Common.getBan(exists.user) const bans = await API.Common.getBan(exists.user)
if (bans.length) return { banned: bans, ip: false } if (bans.length) return { banned: bans, ip: false }
return { error: null, user: exists.user } return { error: null, user: exists.user }
} }
// Get bans for IP address // Get bans for IP address
let bans = await API.Common.getBan(null, ipAddress) const bans = await API.Common.getBan(null, ipAddress)
if (bans.length) return { banned: bans, ip: true } if (bans.length) return { banned: bans, ip: true }
// Run the function for avatar fetching // Run the function for avatar fetching
@ -146,14 +146,14 @@ const API = {
} }
// Assign the data // Assign the data
let newUData = Object.assign({ const newUData = Object.assign({
email: remoteData.email || '', email: remoteData.email || '',
avatar_file: avatar, avatar_file: avatar,
ip_address: ipAddress ip_address: ipAddress
}, remoteData) }, remoteData)
// Remove unnecessary fields // Remove unnecessary fields
for (let i in newUData) { for (const i in newUData) {
if (userFields.indexOf(i) === -1) { if (userFields.indexOf(i) === -1) {
delete newUData[i] delete newUData[i]
} }
@ -175,7 +175,7 @@ const API = {
if (rawData.picture) { if (rawData.picture) {
if (rawData.picture.is_silhouette === false && rawData.picture.url) { if (rawData.picture.is_silhouette === false && rawData.picture.url) {
let imgdata = await Image.downloadImage(rawData.picture.url) const imgdata = await Image.downloadImage(rawData.picture.url)
if (imgdata && imgdata.fileName) { if (imgdata && imgdata.fileName) {
profilepic = imgdata.fileName profilepic = imgdata.fileName
} }
@ -189,14 +189,14 @@ const API = {
return { error: 'No Authorization' } return { error: 'No Authorization' }
} }
let uid = authResponse.userID const uid = authResponse.userID
if (!uid) { if (!uid) {
return { error: 'No Authorization' } return { error: 'No Authorization' }
} }
// Get facebook user information in order to create a new user or verify // Get facebook user information in order to create a new user or verify
let fbdata let fbdata
let intel = { const intel = {
access_token: authResponse.accessToken, access_token: authResponse.accessToken,
fields: 'name,email,picture,short_name' fields: 'name,email,picture,short_name'
} }
@ -212,7 +212,7 @@ const API = {
return { error: fbdata.error.message } return { error: fbdata.error.message }
} }
let cleanedData = Object.assign(fbdata, { const cleanedData = Object.assign(fbdata, {
username: fbdata.short_name || 'FB' + UAPI.Hash(4), username: fbdata.short_name || 'FB' + UAPI.Hash(4),
display_name: fbdata.name, display_name: fbdata.name,
email: fbdata.email || '' email: fbdata.email || ''
@ -226,7 +226,7 @@ const API = {
let profilepic = null let profilepic = null
if (rawData.profile_image_url_https) { if (rawData.profile_image_url_https) {
let imgdata = await Image.downloadImage(rawData.profile_image_url_https) const imgdata = await Image.downloadImage(rawData.profile_image_url_https)
if (imgdata && imgdata.fileName) { if (imgdata && imgdata.fileName) {
profilepic = imgdata.fileName profilepic = imgdata.fileName
} }
@ -236,7 +236,7 @@ const API = {
}, },
oauthApp: function () { oauthApp: function () {
if (!twitterApp) { if (!twitterApp) {
let redirectUri = config.server.domain + '/api/external/twitter/callback' const redirectUri = config.server.domain + '/api/external/twitter/callback'
twitterApp = new oauth.PromiseOAuth( twitterApp = new oauth.PromiseOAuth(
'https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token', 'https://api.twitter.com/oauth/access_token',
@ -282,7 +282,7 @@ const API = {
if (!twitterApp) API.Twitter.oauthApp() if (!twitterApp) API.Twitter.oauthApp()
let twdata let twdata
try { try {
let resp = await twitterApp.get('https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true', const resp = await twitterApp.get('https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
accessTokens.access_token, accessTokens.access_token_secret) accessTokens.access_token, accessTokens.access_token_secret)
twdata = JSON.parse(resp[0]) twdata = JSON.parse(resp[0])
} catch (e) { } catch (e) {
@ -290,9 +290,9 @@ const API = {
return { error: 'Failed to verify user credentials.' } return { error: 'Failed to verify user credentials.' }
} }
let uid = twdata.id_str const uid = twdata.id_str
let cleanedData = Object.assign(twdata, { const cleanedData = Object.assign(twdata, {
username: twdata.screen_name, username: twdata.screen_name,
display_name: twdata.name, display_name: twdata.name,
email: twdata.email || '' email: twdata.email || ''
@ -305,7 +305,7 @@ const API = {
getAvatar: async (rawData) => { getAvatar: async (rawData) => {
let profilepic = null let profilepic = null
if (rawData.image) { if (rawData.image) {
let imgdata = await Image.downloadImage(rawData.image) const imgdata = await Image.downloadImage(rawData.image)
if (imgdata && imgdata.fileName) { if (imgdata && imgdata.fileName) {
profilepic = imgdata.fileName profilepic = imgdata.fileName
} }
@ -317,10 +317,10 @@ const API = {
let uid let uid
try { try {
let test = await http.GET('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + data.id_token) const test = await http.GET('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + data.id_token)
if (!test) throw new Error('No response!') if (!test) throw new Error('No response!')
let jsondata = JSON.parse(test) const jsondata = JSON.parse(test)
if (!jsondata || !jsondata.email || !jsondata.name) throw new Error('Please allow Basic Profile and Email.') if (!jsondata || !jsondata.email || !jsondata.name) throw new Error('Please allow Basic Profile and Email.')
if (jsondata.email !== data.email || jsondata.name !== data.name) throw new Error('Conflicting data. Please try again!') if (jsondata.email !== data.email || jsondata.name !== data.name) throw new Error('Conflicting data. Please try again!')
@ -332,7 +332,7 @@ const API = {
return { error: e.message } return { error: e.message }
} }
let cleanedData = Object.assign(data, { const cleanedData = Object.assign(data, {
username: data.name, username: data.name,
display_name: data.name, display_name: data.name,
email: data.email || '' email: data.email || ''
@ -344,10 +344,10 @@ const API = {
Discord: { Discord: {
getAvatar: async (rawData) => { getAvatar: async (rawData) => {
let profilepic = null let profilepic = null
let aviSnowflake = rawData.avatar const aviSnowflake = rawData.avatar
if (aviSnowflake) { if (aviSnowflake) {
try { try {
let avpt = await Image.downloadImage('https://cdn.discordapp.com/avatars/' + rawData.id + '/' + aviSnowflake + '.png') const avpt = await Image.downloadImage('https://cdn.discordapp.com/avatars/' + rawData.id + '/' + aviSnowflake + '.png')
if (avpt && avpt.fileName) { if (avpt && avpt.fileName) {
profilepic = avpt.fileName profilepic = avpt.fileName
} }
@ -372,25 +372,25 @@ const API = {
}, },
getAuthorizeURL: function (req) { getAuthorizeURL: function (req) {
if (!discordApp) API.Discord.oauth2App() if (!discordApp) API.Discord.oauth2App()
let state = API.Common.stateGenerator(req) const state = API.Common.stateGenerator(req)
let redirectUri = config.server.domain + '/api/external/discord/callback' const redirectUri = config.server.domain + '/api/external/discord/callback'
const params = { const params = {
'client_id': config.external.discord.api, client_id: config.external.discord.api,
'redirect_uri': redirectUri, redirect_uri: redirectUri,
'scope': 'identify email', scope: 'identify email',
'response_type': 'code', response_type: 'code',
'state': state state: state
} }
let url = discordApp.getAuthorizeUrl(params) const url = discordApp.getAuthorizeUrl(params)
return { error: null, state: state, url: url } return { error: null, state: state, url: url }
}, },
getAccessToken: async function (code) { getAccessToken: async function (code) {
if (!discordApp) API.Discord.oauth2App() if (!discordApp) API.Discord.oauth2App()
let redirectUri = config.server.domain + '/api/external/discord/callback' const redirectUri = config.server.domain + '/api/external/discord/callback'
let tokens let tokens
try { try {
tokens = await discordApp.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: redirectUri }) tokens = await discordApp.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: redirectUri })
@ -409,17 +409,17 @@ const API = {
let ddata let ddata
try { try {
let resp = await discordApp.get('https://discordapp.com/api/users/@me', accessToken) const resp = await discordApp.get('https://discordapp.com/api/users/@me', accessToken)
ddata = JSON.parse(resp) ddata = JSON.parse(resp)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
return { error: 'Could not get user information' } return { error: 'Could not get user information' }
} }
let uid = ddata.id const uid = ddata.id
// Create a new user // Create a new user
let cleanedData = Object.assign(ddata, { const cleanedData = Object.assign(ddata, {
display_name: ddata.username, display_name: ddata.username,
email: ddata.email || '' email: ddata.email || ''
}) })

View File

@ -1,8 +1,8 @@
import gm from 'gm' import gm from 'gm'
import url from 'url' import { URL } from 'url'
import path from 'path' import path from 'path'
import crypto from 'crypto' import crypto from 'crypto'
import uuid from 'uuid/v4' import { v4 as uuid } from 'uuid'
import http from '../../scripts/http' import http from '../../scripts/http'
@ -20,8 +20,8 @@ const imageTypes = {
} }
function decodeBase64Image (dataString) { function decodeBase64Image (dataString) {
let matches = dataString.match(/^data:([A-Za-z-+/]+);base64,(.+)$/) const matches = dataString.match(/^data:([A-Za-z-+/]+);base64,(.+)$/)
let response = {} const response = {}
if (matches.length !== 3) { if (matches.length !== 3) {
return null return null
@ -34,10 +34,10 @@ function decodeBase64Image (dataString) {
} }
function saneFields (fields) { function saneFields (fields) {
let out = {} const out = {}
for (let i in fields) { for (const i in fields) {
let entry = fields[i] const entry = fields[i]
if (typeof entry === 'object' && entry.length === 1 && !isNaN(parseInt(entry[0]))) { if (typeof entry === 'object' && entry.length === 1 && !isNaN(parseInt(entry[0]))) {
out[i] = parseInt(entry[0]) out[i] = parseInt(entry[0])
} }
@ -53,17 +53,17 @@ async function bailOut (file, error) {
async function imageBase64 (baseObj) { async function imageBase64 (baseObj) {
if (!baseObj) return null if (!baseObj) return null
let imgData = decodeBase64Image(baseObj) const imgData = decodeBase64Image(baseObj)
if (!imgData) return null if (!imgData) return null
if (!imageTypes[imgData.type]) return null if (!imageTypes[imgData.type]) return null
let imageName = 'base64-' + uuid() let imageName = 'base64-' + uuid()
let ext = imageTypes[imgData.type] || '.png' const ext = imageTypes[imgData.type] || '.png'
imageName += ext imageName += ext
let fpath = path.join(images, imageName) const fpath = path.join(images, imageName)
try { try {
await fs.writeFile(fpath, imgData.data) await fs.writeFile(fpath, imgData.data)
@ -76,7 +76,7 @@ async function imageBase64 (baseObj) {
} }
function gravatarURL (email) { function gravatarURL (email) {
let sum = crypto.createHash('md5').update(email).digest('hex') const sum = crypto.createHash('md5').update(email).digest('hex')
return gravatar + sum + '.jpg' return gravatar + sum + '.jpg'
} }
@ -85,8 +85,8 @@ async function downloadImage (imgUrl, designation) {
if (!designation) designation = 'download' if (!designation) designation = 'download'
let imageName = designation + '-' + uuid() let imageName = designation + '-' + uuid()
let uridata = url.parse(imgUrl) const uridata = new URL(imgUrl)
let pathdata = path.parse(uridata.path) const pathdata = path.parse(uridata.path)
imageName += pathdata.ext || '.png' imageName += pathdata.ext || '.png'
@ -108,23 +108,23 @@ async function uploadImage (identifier, fields, files) {
fields = saneFields(fields) fields = saneFields(fields)
// Get file info, generate a file name // Get file info, generate a file name
let fileHash = uuid() const fileHash = uuid()
let contentType = file.headers['content-type'] const contentType = file.headers['content-type']
if (!contentType) return bailOut(file.path, 'Invalid of missing content-type header') if (!contentType) return bailOut(file.path, 'Invalid of missing content-type header')
file = file.path file = file.path
// Make sure content type is allowed // Make sure content type is allowed
let match = false let match = false
for (let i in imageTypes) { for (const i in imageTypes) {
if (i === contentType) { if (i === contentType) {
match = true match = true
break break
} }
} }
if (!match) return bailOut(file, 'Invalid image type. Only PNG, JPG and JPEG files are allowed.') if (!match) return bailOut(file, 'Invalid image type. Only PNG, JPG and JPEG files are allowed.')
let extension = imageTypes[contentType] const extension = imageTypes[contentType]
let fileName = identifier + '-' + fileHash + extension const fileName = identifier + '-' + fileHash + extension
// Check for cropping // Check for cropping
if (fields.x == null || fields.y == null || fields.width == null || fields.height == null) { if (fields.x == null || fields.y == null || fields.width == null || fields.height == null) {

View File

@ -3,7 +3,7 @@ import cprog from 'child_process'
import crypto from 'crypto' import crypto from 'crypto'
import notp from 'notp' import notp from 'notp'
import base32 from 'thirty-two' import base32 from 'thirty-two'
import uuidV1 from 'uuid/v1' import { v1 as uuidV1 } from 'uuid'
import fs from 'fs-extra' import fs from 'fs-extra'
import config from '../../scripts/load-config' import config from '../../scripts/load-config'
@ -16,12 +16,12 @@ const emailRe = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\
// Fork a bcrypt process to hash and compare passwords // Fork a bcrypt process to hash and compare passwords
function bcryptTask (data) { function bcryptTask (data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let proc = cprog.fork(path.join(__dirname, '../../scripts', 'bcrypt.js')) const proc = cprog.fork(path.join(__dirname, '../../scripts', 'bcrypt.js'))
let done = false let done = false
proc.send(data.task + ' ' + JSON.stringify(data)) proc.send(data.task + ' ' + JSON.stringify(data))
proc.on('message', (chunk) => { proc.on('message', (chunk) => {
if (chunk == null) return reject(new Error('No response')) if (chunk == null) return reject(new Error('No response'))
let line = chunk.toString().trim() const line = chunk.toString().trim()
done = true done = true
if (line === 'error') { if (line === 'error') {
return reject(new Error(line)) return reject(new Error(line))
@ -43,8 +43,8 @@ function bcryptTask (data) {
function keysAvailable (object, required) { function keysAvailable (object, required) {
let found = true let found = true
for (let i in required) { for (const i in required) {
let key = required[i] const key = required[i]
if (object[key] == null) { if (object[key] == null) {
found = false found = false
} }
@ -65,7 +65,7 @@ async function cleanUpDonation (obj, mcOnly, timeframe) {
user = await API.User.get(obj.user_id) user = await API.User.get(obj.user_id)
} }
let result = { const result = {
trackId: obj.id, trackId: obj.id,
amount: obj.amount, amount: obj.amount,
donated: obj.created_at donated: obj.created_at
@ -75,10 +75,10 @@ async function cleanUpDonation (obj, mcOnly, timeframe) {
result.name = user.display_name result.name = user.display_name
} }
let sources = obj.source.split(',') const sources = obj.source.split(',')
for (let i in sources) { for (const i in sources) {
if (sources[i].indexOf('mcu:') === 0) { if (sources[i].indexOf('mcu:') === 0) {
let mcu = sources[i].split(':')[1] const mcu = sources[i].split(':')[1]
if (mcu.match(/^([\w_]{2,16})$/i)) { if (mcu.match(/^([\w_]{2,16})$/i)) {
result.minecraft_username = mcu result.minecraft_username = mcu
} }
@ -90,7 +90,7 @@ async function cleanUpDonation (obj, mcOnly, timeframe) {
return result return result
} }
let txnStore = [] const txnStore = []
const API = { const API = {
Hash: (len) => { Hash: (len) => {
@ -101,10 +101,10 @@ const API = {
if (!ppp) ppp = 5 if (!ppp) ppp = 5
if (!dcount) return null if (!dcount) return null
let pageCount = Math.ceil(dcount / ppp) const pageCount = Math.ceil(dcount / ppp)
if (page > pageCount) page = pageCount if (page > pageCount) page = pageCount
let offset = (page - 1) * ppp const offset = (page - 1) * ppp
return { return {
page: page, page: page,
@ -135,7 +135,7 @@ const API = {
} }
} }
let user = await models.User.query().where(scope, identifier) const user = await models.User.query().where(scope, identifier)
if (!user.length) return null if (!user.length) return null
return user[0] return user[0]
@ -154,16 +154,16 @@ const API = {
socialStatus: async function (user) { socialStatus: async function (user) {
user = await API.User.ensureObject(user, ['password']) user = await API.User.ensureObject(user, ['password'])
if (!user) return null if (!user) return null
let external = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id) const external = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
let enabled = {} const enabled = {}
for (let i in external) { for (const i in external) {
let ext = external[i] const ext = external[i]
enabled[ext.service] = true enabled[ext.service] = true
} }
let accountSourceIsExternal = user.password === null || user.password === '' const accountSourceIsExternal = user.password === null || user.password === ''
let obj = { const obj = {
enabled: enabled, enabled: enabled,
password: !accountSourceIsExternal password: !accountSourceIsExternal
} }
@ -186,8 +186,8 @@ const API = {
}, },
changeAvatar: async function (user, fileName) { changeAvatar: async function (user, fileName) {
user = await API.User.ensureObject(user, ['avatar_file']) user = await API.User.ensureObject(user, ['avatar_file'])
let uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images') const uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images')
let pathOf = path.join(uploadsDir, fileName) const pathOf = path.join(uploadsDir, fileName)
if (!await fs.exists(pathOf)) { if (!await fs.exists(pathOf)) {
throw new Error('No such file') throw new Error('No such file')
@ -195,7 +195,7 @@ const API = {
// Delete previous upload // Delete previous upload
if (user.avatar_file != null) { if (user.avatar_file != null) {
let file = path.join(uploadsDir, user.avatar_file) const file = path.join(uploadsDir, user.avatar_file)
if (await fs.exists(file)) { if (await fs.exists(file)) {
await fs.unlink(file) await fs.unlink(file)
} }
@ -206,10 +206,10 @@ const API = {
}, },
removeAvatar: async function (user) { removeAvatar: async function (user) {
user = await API.User.ensureObject(user, ['avatar_file']) user = await API.User.ensureObject(user, ['avatar_file'])
let uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images') const uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images')
if (!user.avatar_file) return {} if (!user.avatar_file) return {}
let file = path.join(uploadsDir, user.avatar_file) const file = path.join(uploadsDir, user.avatar_file)
if (await fs.exists(file)) { if (await fs.exists(file)) {
await fs.unlink(file) await fs.unlink(file)
} }
@ -224,15 +224,15 @@ const API = {
bans = await models.Ban.query().where('user_id', field) bans = await models.Ban.query().where('user_id', field)
} }
let bansActive = [] const bansActive = []
for (let i in bans) { for (const i in bans) {
let ban = bans[i] const ban = bans[i]
// Check expiry // Check expiry
if (ban.expires_at && new Date(ban.expires_at).getTime() < Date.now()) continue if (ban.expires_at && new Date(ban.expires_at).getTime() < Date.now()) continue
let banInfo = { const banInfo = {
banned: ban.created_at, banned: ban.created_at,
reason: ban.reason, reason: ban.reason,
expiry: ban.expires_at expiry: ban.expires_at
@ -257,7 +257,7 @@ const API = {
if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return false if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return false
let user = await API.User.get(getToken.user_id) const user = await API.User.get(getToken.user_id)
if (!user) return false if (!user) return false
await models.User.query().patchAndFetchById(user.id, { activated: 1 }) await models.User.query().patchAndFetchById(user.id, { activated: 1 })
@ -265,7 +265,7 @@ const API = {
return true return true
}, },
totpTokenRequired: async function (user) { totpTokenRequired: async function (user) {
let getToken = await models.TotpToken.query().where('user_id', user.id) const getToken = await models.TotpToken.query().where('user_id', user.id)
if (!getToken || !getToken.length) return false if (!getToken || !getToken.length) return false
if (getToken[0].activated !== 1) return false if (getToken[0].activated !== 1) return false
@ -286,7 +286,7 @@ const API = {
return false return false
} }
let login = notp.totp.verify(code, getToken.token, {}) const login = notp.totp.verify(code, getToken.token, {})
if (login) { if (login) {
if (login.delta !== 0) { if (login.delta !== 0) {
@ -305,7 +305,7 @@ const API = {
}, },
purgeTotp: async function (user, password) { purgeTotp: async function (user, password) {
user = await API.User.ensureObject(user, ['password']) user = await API.User.ensureObject(user, ['password'])
let pwmatch = await API.User.Login.password(user, password) const pwmatch = await API.User.Login.password(user, password)
if (!pwmatch) return false if (!pwmatch) return false
// TODO: Inform user via email // TODO: Inform user via email
@ -320,12 +320,12 @@ const API = {
if (!user.password || user.password === '') return null if (!user.password || user.password === '') return null
// Get existing tokens for the user and delete them if found // Get existing tokens for the user and delete them if found
let getToken = await models.TotpToken.query().where('user_id', user.id) const getToken = await models.TotpToken.query().where('user_id', user.id)
if (getToken && getToken.length) { if (getToken && getToken.length) {
await models.TotpToken.query().delete().where('user_id', user.id) await models.TotpToken.query().delete().where('user_id', user.id)
} }
let newToken = { const newToken = {
user_id: user.id, user_id: user.id,
token: API.Hash(16), token: API.Hash(16),
recovery_code: API.Hash(8), recovery_code: API.Hash(8),
@ -333,12 +333,12 @@ const API = {
created_at: new Date() created_at: new Date()
} }
let hashed = base32.encode(newToken.token) const hashed = base32.encode(newToken.token)
let domain = 'icynet.eu' const domain = 'icynet.eu'
await models.TotpToken.query().insert(newToken) await models.TotpToken.query().insert(newToken)
let uri = encodeURIComponent('otpauth://totp/' + user.username + '@' + domain + '?secret=' + hashed) const uri = encodeURIComponent('otpauth://totp/' + user.username + '@' + domain + '?secret=' + hashed)
return uri return uri
} }
@ -351,26 +351,26 @@ const API = {
return emailRe.test(email) return emailRe.test(email)
}, },
newAccount: async function (regdata) { newAccount: async function (regdata) {
let email = config.email && config.email.enabled const email = config.email && config.email.enabled
let data = Object.assign(regdata, { const data = Object.assign(regdata, {
created_at: new Date(), created_at: new Date(),
updated_at: new Date(), updated_at: new Date(),
uuid: uuidV1(), uuid: uuidV1(),
activated: email ? 0 : 1 activated: email ? 0 : 1
}) })
let userTest = await API.User.get(regdata.username) const userTest = await API.User.get(regdata.username)
if (userTest) { if (userTest) {
throw new Error('This username is already taken!') throw new Error('This username is already taken!')
} }
let emailTest = await API.User.get(regdata.email) const emailTest = await API.User.get(regdata.email)
if (emailTest) { if (emailTest) {
throw new Error('This email address is already registered!') throw new Error('This email address is already registered!')
} }
// Create user // Create user
let user = await models.User.query().insert(data) const user = await models.User.query().insert(data)
if (email) { if (email) {
await API.User.Register.activationEmail(user, true) await API.User.Register.activationEmail(user, true)
@ -380,7 +380,7 @@ const API = {
}, },
activationEmail: async function (user, deleteOnFail = false) { activationEmail: async function (user, deleteOnFail = false) {
// Activation token // Activation token
let activationToken = API.Hash(16) const activationToken = API.Hash(16)
await models.Token.query().insert({ await models.Token.query().insert({
expires_at: new Date(Date.now() + 86400000), // 1 day expires_at: new Date(Date.now() + 86400000), // 1 day
@ -393,7 +393,7 @@ const API = {
// Send Activation Email // Send Activation Email
try { try {
let em = await emailer.pushMail('activate', user.email, { const em = await emailer.pushMail('activate', user.email, {
domain: config.server.domain, domain: config.server.domain,
display_name: user.display_name, display_name: user.display_name,
activation_token: activationToken activation_token: activationToken
@ -415,20 +415,20 @@ const API = {
}, },
Reset: { Reset: {
reset: async function (email, passRequired = true) { reset: async function (email, passRequired = true) {
let emailEnabled = config.email && config.email.enabled const emailEnabled = config.email && config.email.enabled
if (!emailEnabled) throw new Error('Cannot reset password.') if (!emailEnabled) throw new Error('Cannot reset password.')
let user = await API.User.get(email) const user = await API.User.get(email)
if (!user) throw new Error('This email address does not match any user in our database.') if (!user) throw new Error('This email address does not match any user in our database.')
if (!user.password && passRequired) throw new Error('The user associated with this email address has used an external website to log in, thus the password cannot be reset.') if (!user.password && passRequired) throw new Error('The user associated with this email address has used an external website to log in, thus the password cannot be reset.')
let recentTokens = await models.Token.query().where('user_id', user.id).andWhere('expires_at', '>', new Date()).andWhere('type', 2) const recentTokens = await models.Token.query().where('user_id', user.id).andWhere('expires_at', '>', new Date()).andWhere('type', 2)
if (recentTokens.length >= 2) { if (recentTokens.length >= 2) {
throw new Error('You\'ve made too many reset requests recently. Please slow down.') throw new Error('You\'ve made too many reset requests recently. Please slow down.')
} }
let resetToken = API.Hash(16) const resetToken = API.Hash(16)
await models.Token.query().insert({ await models.Token.query().insert({
expires_at: new Date(Date.now() + 86400000), // 1 day expires_at: new Date(Date.now() + 86400000), // 1 day
token: resetToken, token: resetToken,
@ -440,7 +440,7 @@ const API = {
console.debug('Reset token:', resetToken) console.debug('Reset token:', resetToken)
if (email) { if (email) {
try { try {
let em = await emailer.pushMail('reset_password', user.email, { const em = await emailer.pushMail('reset_password', user.email, {
domain: config.server.domain, domain: config.server.domain,
display_name: user.display_name, display_name: user.display_name,
reset_token: resetToken reset_token: resetToken
@ -463,13 +463,13 @@ const API = {
if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return null if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return null
let user = await API.User.get(getToken.user_id) const user = await API.User.get(getToken.user_id)
if (!user) return null if (!user) return null
return user return user
}, },
changePassword: async function (user, password, token) { changePassword: async function (user, password, token) {
let hashed = await API.User.Register.hashPassword(password) const hashed = await API.User.Register.hashPassword(password)
await models.User.query().patchAndFetchById(user.id, { password: hashed, updated_at: new Date() }) await models.User.query().patchAndFetchById(user.id, { password: hashed, updated_at: new Date() })
await models.Token.query().delete().where('token', token) await models.Token.query().delete().where('token', token)
@ -480,18 +480,18 @@ const API = {
OAuth2: { OAuth2: {
getUserAuthorizations: async function (user) { getUserAuthorizations: async function (user) {
user = await API.User.ensureObject(user) user = await API.User.ensureObject(user)
let auths = await models.OAuth2AuthorizedClient.query().where('user_id', user.id) const auths = await models.OAuth2AuthorizedClient.query().where('user_id', user.id)
let nicelist = [] const nicelist = []
for (let i in auths) { for (const i in auths) {
let auth = auths[i] const auth = auths[i]
let client = await models.OAuth2Client.query().where('id', auth.client_id) let client = await models.OAuth2Client.query().where('id', auth.client_id)
if (!client.length) continue if (!client.length) continue
client = client[0] client = client[0]
let obj = { const obj = {
id: client.id, id: client.id,
title: client.title, title: client.title,
description: client.description, description: client.description,
@ -508,13 +508,13 @@ const API = {
}, },
removeUserAuthorization: async function (user, clientId) { removeUserAuthorization: async function (user, clientId) {
user = await API.User.ensureObject(user) user = await API.User.ensureObject(user)
let auth = await models.OAuth2AuthorizedClient.query().where('user_id', user.id).andWhere('client_id', clientId) const auth = await models.OAuth2AuthorizedClient.query().where('user_id', user.id).andWhere('client_id', clientId)
if (!auth.length) return false if (!auth.length) return false
await models.OAuth2AccessToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id) await models.OAuth2AccessToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id)
await models.OAuth2RefreshToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id) await models.OAuth2RefreshToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id)
for (let i in auth) { for (const i in auth) {
await models.OAuth2AuthorizedClient.query().delete().where('id', auth[i].id) await models.OAuth2AuthorizedClient.query().delete().where('id', auth[i].id)
} }
@ -524,11 +524,11 @@ const API = {
}, },
Payment: { Payment: {
handleIPN: async function (body) { handleIPN: async function (body) {
let sandboxed = body.test_ipn === '1' const sandboxed = body.test_ipn === '1'
let url = 'https://ipnpb.' + (sandboxed ? 'sandbox.' : '') + 'paypal.com/cgi-bin/webscr' const url = 'https://ipnpb.' + (sandboxed ? 'sandbox.' : '') + 'paypal.com/cgi-bin/webscr'
console.debug('Incoming payment') console.debug('Incoming payment')
let verification = await http.POST(url, {}, Object.assign({ const verification = await http.POST(url, {}, Object.assign({
cmd: '_notify-validate' cmd: '_notify-validate'
}, body)) }, body))
@ -551,15 +551,15 @@ const API = {
} }
let user let user
let source = [] const source = []
if (sandboxed) { if (sandboxed) {
source.push('virtual') source.push('virtual')
} }
// TODO: add hooks // TODO: add hooks
let custom = body.custom.split(',') const custom = body.custom.split(',')
for (let i in custom) { for (const i in custom) {
let str = custom[i] const str = custom[i]
if (str.indexOf('userid:') === 0) { if (str.indexOf('userid:') === 0) {
body.user_id = parseInt(str.split(':')[1]) body.user_id = parseInt(str.split(':')[1])
} else if (str.indexOf('mcu:') === 0) { } else if (str.indexOf('mcu:') === 0) {
@ -573,7 +573,7 @@ const API = {
user = await API.User.get(body.payer_email) user = await API.User.get(body.payer_email)
} }
let donation = { const donation = {
user_id: user ? user.id : null, user_id: user ? user.id : null,
amount: (body.mc_gross || body.payment_gross || 'Unknown') + ' ' + (body.mc_currency || 'EUR'), amount: (body.mc_gross || body.payment_gross || 'Unknown') + ' ' + (body.mc_currency || 'EUR'),
source: source.join(','), source: source.join(','),
@ -589,21 +589,21 @@ const API = {
userContributions: async function (user) { userContributions: async function (user) {
user = await API.User.ensureObject(user) user = await API.User.ensureObject(user)
let dbq = await models.Donation.query().orderBy('created_at', 'desc').where('user_id', user.id) const dbq = await models.Donation.query().orderBy('created_at', 'desc').where('user_id', user.id)
let contribs = [] const contribs = []
for (let i in dbq) { for (const i in dbq) {
contribs.push(await cleanUpDonation(dbq[i])) contribs.push(await cleanUpDonation(dbq[i]))
} }
return contribs return contribs
}, },
allContributions: async function (count, mcOnly, timeframe = 0) { allContributions: async function (count, mcOnly, timeframe = 0) {
let dbq = await models.Donation.query().orderBy('created_at', 'desc').limit(count) const dbq = await models.Donation.query().orderBy('created_at', 'desc').limit(count)
let contribs = [] const contribs = []
for (let i in dbq) { for (const i in dbq) {
let obj = await cleanUpDonation(dbq[i], mcOnly, timeframe) const obj = await cleanUpDonation(dbq[i], mcOnly, timeframe)
if (!obj) continue if (!obj) continue
contribs.push(obj) contribs.push(obj)
} }

View File

@ -13,8 +13,8 @@ function slugify (title) {
} }
async function cleanArticle (entry, shortenContent = false) { async function cleanArticle (entry, shortenContent = false) {
let poster = await API.User.get(entry.user_id) const poster = await API.User.get(entry.user_id)
let article = { const article = {
id: entry.id, id: entry.id,
slug: slugify(entry.title), slug: slugify(entry.title),
title: entry.title, title: entry.title,
@ -40,13 +40,13 @@ async function cleanArticle (entry, shortenContent = false) {
const News = { const News = {
preview: async () => { preview: async () => {
// Fetch 3 latest stories // Fetch 3 latest stories
let news = await Models.News.query().orderBy('created_at', 'desc').limit(3) const news = await Models.News.query().orderBy('created_at', 'desc').limit(3)
if (!news.length) return [] if (!news.length) return []
let articles = [] const articles = []
for (let i in news) { for (const i in news) {
let entry = news[i] const entry = news[i]
articles.push(await cleanArticle(entry)) articles.push(await cleanArticle(entry))
} }
@ -56,17 +56,17 @@ const News = {
let count = await Models.News.query().count('id as ids') let count = await Models.News.query().count('id as ids')
if (page < 1) page = 1 if (page < 1) page = 1
if (!count.length || !count[0]['ids'] || isNaN(page)) { if (!count.length || !count[0].ids || isNaN(page)) {
return { error: 'No news' } return { error: 'No news' }
} }
count = count[0].ids count = count[0].ids
let paginated = API.Pagination(perPage, parseInt(count), page) const paginated = API.Pagination(perPage, parseInt(count), page)
let news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage) const news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
let articles = [] const articles = []
for (let i in news) { for (const i in news) {
let entry = news[i] const entry = news[i]
articles.push(await cleanArticle(entry)) articles.push(await cleanArticle(entry))
} }
@ -84,7 +84,7 @@ const News = {
return cleanArticle(article) return cleanArticle(article)
}, },
compose: async (user, body) => { compose: async (user, body) => {
let article = { const article = {
title: body.title, title: body.title,
content: body.content, content: body.content,
tags: body.tags || '', tags: body.tags || '',
@ -93,28 +93,28 @@ const News = {
updated_at: new Date() updated_at: new Date()
} }
let result = await Models.News.query().insert(article) const result = await Models.News.query().insert(article)
result.slug = slugify(result.title) result.slug = slugify(result.title)
return result return result
}, },
edit: async (id, body) => { edit: async (id, body) => {
let patch = { const patch = {
content: body.content, content: body.content,
updated_at: new Date() updated_at: new Date()
} }
let result = await Models.News.query().patchAndFetchById(id, patch) const result = await Models.News.query().patchAndFetchById(id, patch)
if (!result) throw new Error('Something went wrong.') if (!result) throw new Error('Something went wrong.')
return {} return {}
}, },
generateFeed: async () => { generateFeed: async () => {
if (feed && new Date(feed.options.updated).getTime() > Date.now() - 3600000) return feed // Update feed hourly if (feed && new Date(feed.options.updated).getTime() > Date.now() - 3600000) return feed // Update feed hourly
let posts = await Models.News.query().orderBy('created_at', 'desc').limit(perPage) const posts = await Models.News.query().orderBy('created_at', 'desc').limit(perPage)
let cleanNews = [] const cleanNews = []
for (let i in posts) { for (const i in posts) {
cleanNews.push(await cleanArticle(posts[i])) cleanNews.push(await cleanArticle(posts[i]))
} }
@ -138,8 +138,8 @@ const News = {
} }
}) })
for (let i in cleanNews) { for (const i in cleanNews) {
let post = cleanNews[i] const post = cleanNews[i]
feed.addItem({ feed.addItem({
title: post.title, title: post.title,

View File

@ -52,7 +52,7 @@ module.exports = wrap(async (req, res, next) => {
} }
console.debug('Parameter response_type is', responseType) console.debug('Parameter response_type is', responseType)
let client = await req.oauth2.model.client.fetchById(clientId) const client = await req.oauth2.model.client.fetchById(clientId)
if (!client) { if (!client) {
return response.error(req, res, new error.InvalidClient('Client not found'), redirectUri) return response.error(req, res, new error.InvalidClient('Client not found'), redirectUri)
} }

View File

@ -9,9 +9,9 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
} }
if (createAllowFuture) { if (createAllowFuture) {
if (!req.body || (typeof req.body['decision']) === 'undefined') { if (!req.body || (typeof req.body.decision) === 'undefined') {
throw new error.InvalidRequest('No decision parameter passed') throw new error.InvalidRequest('No decision parameter passed')
} else if (req.body['decision'] === '0') { } else if (req.body.decision === '0') {
throw new error.AccessDenied('User denied access to the resource') throw new error.AccessDenied('User denied access to the resource')
} else { } else {
console.debug('Decision check passed') console.debug('Decision check passed')

View File

@ -9,9 +9,9 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
} }
if (createAllowFuture) { if (createAllowFuture) {
if (!req.body || (typeof req.body['decision']) === 'undefined') { if (!req.body || (typeof req.body.decision) === 'undefined') {
throw new error.InvalidRequest('No decision parameter passed') throw new error.InvalidRequest('No decision parameter passed')
} else if (req.body['decision'] === '0') { } else if (req.body.decision === '0') {
throw new error.AccessDenied('User denied access to the resource') throw new error.AccessDenied('User denied access to the resource')
} else { } else {
console.debug('Decision check passed') console.debug('Decision check passed')

View File

@ -38,13 +38,13 @@ module.exports = wrap(async function (req, res) {
return response.error(req, res, new error.InvalidRequest('Token not provided in request body')) return response.error(req, res, new error.InvalidRequest('Token not provided in request body'))
} }
let token = await req.oauth2.model.accessToken.fetchByToken(req.body.token) const token = await req.oauth2.model.accessToken.fetchByToken(req.body.token)
if (!token) { if (!token) {
return response.error(req, res, new error.InvalidRequest('Token does not exist')) return response.error(req, res, new error.InvalidRequest('Token does not exist'))
} }
let ttl = req.oauth2.model.accessToken.getTTL(token) const ttl = req.oauth2.model.accessToken.getTTL(token)
let resObj = { const resObj = {
token_type: 'bearer', token_type: 'bearer',
token: token.token, token: token.token,
expires_in: Math.floor(ttl / 1000) expires_in: Math.floor(ttl / 1000)

View File

@ -43,13 +43,13 @@ module.exports = wrap(async (req, res) => {
grantType = req.body.grant_type grantType = req.body.grant_type
console.debug('Parameter grant_type is', grantType) console.debug('Parameter grant_type is', grantType)
let client = await req.oauth2.model.client.fetchById(clientId) const client = await req.oauth2.model.client.fetchById(clientId)
if (!client) { if (!client) {
return response.error(req, res, new error.InvalidClient('Client not found')) return response.error(req, res, new error.InvalidClient('Client not found'))
} }
let valid = req.oauth2.model.client.checkSecret(client, clientSecret) const valid = req.oauth2.model.client.checkSecret(client, clientSecret)
if (!valid) { if (!valid) {
return response.error(req, res, new error.UnauthorizedClient('Invalid client secret')) return response.error(req, res, new error.UnauthorizedClient('Invalid client secret'))
} }

View File

@ -1,7 +1,7 @@
import error from '../../error' import error from '../../error'
module.exports = async (oauth2, client, providedCode, redirectUri) => { module.exports = async (oauth2, client, providedCode, redirectUri) => {
let respObj = { const respObj = {
token_type: 'bearer' token_type: 'bearer'
} }

View File

@ -3,7 +3,7 @@ import error from '../../error'
module.exports = async (oauth2, client, wantScope) => { module.exports = async (oauth2, client, wantScope) => {
let scope = null let scope = null
let resObj = { const resObj = {
token_type: 'bearer' token_type: 'bearer'
} }

View File

@ -2,7 +2,7 @@ import error from '../../error'
module.exports = async (oauth2, client, username, password, scope) => { module.exports = async (oauth2, client, username, password, scope) => {
let user = null let user = null
let resObj = { const resObj = {
token_type: 'bearer' token_type: 'bearer'
} }
@ -32,7 +32,7 @@ module.exports = async (oauth2, client, username, password, scope) => {
throw new error.InvalidClient('User not found') throw new error.InvalidClient('User not found')
} }
let valid = await oauth2.model.user.checkPassword(user, password) const valid = await oauth2.model.user.checkPassword(user, password)
if (!valid) { if (!valid) {
throw new error.InvalidClient('Wrong password') throw new error.InvalidClient('Wrong password')
} }

View File

@ -6,7 +6,7 @@ module.exports = async (oauth2, client, pRefreshToken, scope) => {
let refreshToken = null let refreshToken = null
let accessToken = null let accessToken = null
let resObj = { const resObj = {
token_type: 'bearer' token_type: 'bearer'
} }

View File

@ -22,18 +22,18 @@ const middleware = wrap(async function (req, res, next) {
token = pieces[1] token = pieces[1]
console.debug('Bearer token parsed from authorization header:', token) console.debug('Bearer token parsed from authorization header:', token)
} else if (req.query && req.query['access_token']) { } else if (req.query && req.query.access_token) {
token = req.query['access_token'] token = req.query.access_token
console.debug('Bearer token parsed from query params:', token) console.debug('Bearer token parsed from query params:', token)
} else if (req.body && req.body['access_token']) { } else if (req.body && req.body.access_token) {
token = req.body['access_token'] token = req.body.access_token
console.debug('Bearer token parsed from body params:', token) console.debug('Bearer token parsed from body params:', token)
} else { } else {
return response.error(req, res, new error.AccessDenied('Bearer token not found')) return response.error(req, res, new error.AccessDenied('Bearer token not found'))
} }
// Try to fetch access token // Try to fetch access token
let object = await req.oauth2.model.accessToken.fetchByToken(token) const object = await req.oauth2.model.accessToken.fetchByToken(token)
if (!object) { if (!object) {
response.error(req, res, new error.Forbidden('Token not found or has expired')) response.error(req, res, new error.Forbidden('Token not found or has expired'))
} else if (!req.oauth2.model.accessToken.checkTTL(object)) { } else if (!req.oauth2.model.accessToken.checkTTL(object)) {

View File

@ -32,7 +32,7 @@ const OAuthDB = {
created_at: new Date() created_at: new Date()
} }
let res = await Models.OAuth2AccessToken.query().insert(obj) const res = await Models.OAuth2AccessToken.query().insert(obj)
if (!res) return null if (!res) return null
return res.token return res.token
@ -54,7 +54,7 @@ const OAuthDB = {
return (object.expires_at - Date.now()) return (object.expires_at - Date.now())
}, },
fetchByUserIdClientId: async (userId, clientId) => { fetchByUserIdClientId: async (userId, clientId) => {
let tkn = await Models.OAuth2AccessToken.query().where('user_id', userId).andWhere('client_id', clientId) const tkn = await Models.OAuth2AccessToken.query().where('user_id', userId).andWhere('client_id', clientId)
if (!tkn.length) return null if (!tkn.length) return null
@ -66,7 +66,7 @@ const OAuthDB = {
return client.id return client.id
}, },
fetchById: async (id) => { fetchById: async (id) => {
let client = await Models.OAuth2Client.query().where('id', id) const client = await Models.OAuth2Client.query().where('id', id)
if (!client.length) return null if (!client.length) return null
@ -110,9 +110,9 @@ const OAuthDB = {
scope = OAuthDB.client.transformScope(scope) scope = OAuthDB.client.transformScope(scope)
} }
let clientScopes = client.scope.split(' ') const clientScopes = client.scope.split(' ')
for (let i in scope) { for (const i in scope) {
if (clientScopes.indexOf(scope[i]) === -1) { if (clientScopes.indexOf(scope[i]) === -1) {
return false return false
} }
@ -228,7 +228,7 @@ const OAuthDB = {
checkPassword: Users.User.Login.password, checkPassword: Users.User.Login.password,
fetchFromRequest: async (req) => { fetchFromRequest: async (req) => {
if (!req.session.user) return null if (!req.session.user) return null
let banStatus = await Users.User.getBanStatus(req.session.user.id) const banStatus = await Users.User.getBanStatus(req.session.user.id)
if (banStatus.length) { if (banStatus.length) {
delete req.session.user delete req.session.user
@ -242,11 +242,11 @@ const OAuthDB = {
scope = scope.join(' ') scope = scope.join(' ')
} }
let authorized = await Models.OAuth2AuthorizedClient.query().where('user_id', userId) const authorized = await Models.OAuth2AuthorizedClient.query().where('user_id', userId)
if (!authorized.length) return false if (!authorized.length) return false
let correct = false let correct = false
for (let i in authorized) { for (const i in authorized) {
if (authorized[i].client_id === clientId) { if (authorized[i].client_id === clientId) {
correct = authorized[i] correct = authorized[i]
} }
@ -271,7 +271,7 @@ const OAuthDB = {
scope = scope.join(' ') scope = scope.join(' ')
} }
let obj = { const obj = {
scope, scope,
user_id: userId, user_id: userId,
client_id: clientId, client_id: clientId,

View File

@ -24,7 +24,7 @@ module.exports.error = function (req, res, err, redirectUri) {
} }
if (redirectUri) { if (redirectUri) {
let obj = { const obj = {
error: err.code, error: err.code,
error_description: err.message error_description: err.message
} }

View File

@ -14,7 +14,7 @@ const args = {
} }
function spawnWorkers () { function spawnWorkers () {
let workerCount = config.server.workers === 0 ? cpuCount : config.server.workers const workerCount = config.server.workers === 0 ? cpuCount : config.server.workers
console.log('Spinning up %d worker process%s', workerCount, (workerCount !== 1 ? 'es' : '')) console.log('Spinning up %d worker process%s', workerCount, (workerCount !== 1 ? 'es' : ''))
for (let i = 0; i < workerCount; i++) { for (let i = 0; i < workerCount; i++) {
@ -52,7 +52,7 @@ function watchFileTree () {
console.log('[WatchTask] %s changed, restarting workers', f) console.log('[WatchTask] %s changed, restarting workers', f)
if (workers.length) { if (workers.length) {
for (let i in workers) { for (const i in workers) {
workers[i].send('stop') workers[i].send('stop')
} }
} }
@ -97,11 +97,11 @@ cluster.setupMaster({
}) })
cluster.on('exit', (worker, code, signal) => { cluster.on('exit', (worker, code, signal) => {
let extra = ((code || '') + ' ' + (signal || '')).trim() const extra = ((code || '') + ' ' + (signal || '')).trim()
console.error('Worker process %d exited %s', worker.process.pid, (extra ? '(' + extra + ')' : '')) console.error('Worker process %d exited %s', worker.process.pid, (extra ? '(' + extra + ')' : ''))
let index = workers.indexOf(worker) const index = workers.indexOf(worker)
if (index !== -1) workers.splice(index, 1) if (index !== -1) workers.splice(index, 1)
if (code === 0) return if (code === 0) return
@ -119,7 +119,7 @@ process.on('SIGUSR2', () => {
console.log('Received SIGUSR2. Restarting workers.') console.log('Received SIGUSR2. Restarting workers.')
if (workers.length) { if (workers.length) {
for (let i in workers) { for (const i in workers) {
workers[i].send('stop') workers[i].send('stop')
} }
} }

View File

@ -12,7 +12,7 @@ const apiRouter = express.Router()
// Check for privilege required to access the admin panel // Check for privilege required to access the admin panel
router.use(ensureLogin, wrap(async (req, res, next) => { router.use(ensureLogin, wrap(async (req, res, next) => {
if (!req.session.privilege) { if (!req.session.privilege) {
let u = await User.get(req.session.user) const u = await User.get(req.session.user)
req.session.privilege = u.nw_privilege req.session.privilege = u.nw_privilege
} }
@ -46,7 +46,7 @@ router.post('/', wrap(async (req, res, next) => {
return next() return next()
} }
let passReady = await User.Login.password(req.session.user, req.body.password) const passReady = await User.Login.password(req.session.user, req.body.password)
if (passReady) { if (passReady) {
req.session.accesstime = Date.now() + 600000 // 10 minutes req.session.accesstime = Date.now() + 600000 // 10 minutes
return res.redirect('/admin') return res.redirect('/admin')
@ -108,12 +108,12 @@ apiRouter.get('/users', wrap(async (req, res) => {
page = 1 page = 1
} }
let users = await API.getAllUsers(page, req.session.user.id) const users = await API.getAllUsers(page, req.session.user.id)
res.jsonp(users) res.jsonp(users)
})) }))
apiRouter.get('/user/:id', wrap(async (req, res) => { apiRouter.get('/user/:id', wrap(async (req, res) => {
let id = parseInt(req.params.id) const id = parseInt(req.params.id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid number') throw new Error('Invalid number')
} }
@ -122,7 +122,7 @@ apiRouter.get('/user/:id', wrap(async (req, res) => {
})) }))
apiRouter.post('/user', csrfVerify, wrap(async (req, res) => { apiRouter.post('/user', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.user_id) const id = parseInt(req.body.user_id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid or missing user ID') throw new Error('Invalid or missing user ID')
} }
@ -131,7 +131,7 @@ apiRouter.post('/user', csrfVerify, wrap(async (req, res) => {
})) }))
apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => { apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.user_id) const id = parseInt(req.body.user_id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid or missing user ID') throw new Error('Invalid or missing user ID')
} }
@ -140,7 +140,7 @@ apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => {
})) }))
apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => { apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.user_id) const id = parseInt(req.body.user_id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid or missing user ID') throw new Error('Invalid or missing user ID')
} }
@ -149,7 +149,7 @@ apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => {
})) }))
apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => { apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.user_id) const id = parseInt(req.body.user_id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid or missing user ID') throw new Error('Invalid or missing user ID')
} }
@ -158,7 +158,7 @@ apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => {
})) }))
apiRouter.post('/user/lock', csrfVerify, wrap(async (req, res) => { apiRouter.post('/user/lock', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.user_id) const id = parseInt(req.body.user_id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid or missing user ID') throw new Error('Invalid or missing user ID')
} }
@ -170,11 +170,11 @@ const availableScopes = ['uuid', 'email', 'username', 'display_name']
apiRouter.get('/search/users', wrap(async (req, res) => { apiRouter.get('/search/users', wrap(async (req, res) => {
if (!req.query.terms) throw new Error('Please specify search terms!') if (!req.query.terms) throw new Error('Please specify search terms!')
let scopes = [] const scopes = []
if (req.query.scopes) { if (req.query.scopes) {
let scq = req.query.scopes.split(',') const scq = req.query.scopes.split(',')
for (let i in scq) { for (const i in scq) {
scq[i] = scq[i].trim() scq[i] = scq[i].trim()
if (availableScopes.indexOf(scq[i]) !== -1) { if (availableScopes.indexOf(scq[i]) !== -1) {
@ -187,7 +187,7 @@ apiRouter.get('/search/users', wrap(async (req, res) => {
scopes.push('email') scopes.push('email')
} }
let results = await API.searchUsers(req.query.terms, scopes) const results = await API.searchUsers(req.query.terms, scopes)
res.jsonp(results) res.jsonp(results)
})) }))
@ -202,17 +202,17 @@ apiRouter.get('/clients', wrap(async (req, res) => {
page = 1 page = 1
} }
let clients = await API.getAllClients(page) const clients = await API.getAllClients(page)
res.jsonp(clients) res.jsonp(clients)
})) }))
apiRouter.get('/client/:id', wrap(async (req, res) => { apiRouter.get('/client/:id', wrap(async (req, res) => {
let id = parseInt(req.params.id) const id = parseInt(req.params.id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid number') throw new Error('Invalid number')
} }
let client = await API.getClient(id) const client = await API.getClient(id)
res.jsonp(client) res.jsonp(client)
})) }))
@ -224,7 +224,7 @@ apiRouter.post('/client/new', csrfVerify, wrap(async (req, res) => {
})) }))
apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => { apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.id) const id = parseInt(req.body.id)
if (!id || isNaN(id)) throw new Error('ID missing') if (!id || isNaN(id)) throw new Error('ID missing')
@ -234,23 +234,23 @@ apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => {
})) }))
apiRouter.post('/client/new_secret', csrfVerify, wrap(async (req, res) => { apiRouter.post('/client/new_secret', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.id) const id = parseInt(req.body.id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid client ID') throw new Error('Invalid client ID')
} }
let client = await API.newSecret(id) const client = await API.newSecret(id)
res.jsonp(client) res.jsonp(client)
})) }))
apiRouter.post('/client/delete', csrfVerify, wrap(async (req, res) => { apiRouter.post('/client/delete', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.id) const id = parseInt(req.body.id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid client ID') throw new Error('Invalid client ID')
} }
let client = await API.removeClient(id) const client = await API.removeClient(id)
res.jsonp(client) res.jsonp(client)
})) }))
@ -266,17 +266,17 @@ apiRouter.get('/bans', wrap(async (req, res) => {
page = 1 page = 1
} }
let bans = await API.getAllBans(page) const bans = await API.getAllBans(page)
res.jsonp(bans) res.jsonp(bans)
})) }))
apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => { apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.id) const id = parseInt(req.body.id)
if (isNaN(id)) { if (isNaN(id)) {
throw new Error('Invalid number') throw new Error('Invalid number')
} }
let ban = await API.removeBan(id) const ban = await API.removeBan(id)
res.jsonp(ban) res.jsonp(ban)
})) }))
@ -284,7 +284,7 @@ apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
apiRouter.post('/ban', csrfVerify, wrap(async (req, res) => { apiRouter.post('/ban', csrfVerify, wrap(async (req, res) => {
if (!req.body.user_id) throw new Error('ID missing') if (!req.body.user_id) throw new Error('ID missing')
let result = await API.addBan(req.body, req.session.user.id) const result = await API.addBan(req.body, req.session.user.id)
res.jsonp(result) res.jsonp(result)
})) }))
@ -299,7 +299,7 @@ apiRouter.post('/email', csrfVerify, wrap(async (req, res) => {
html: req.body.content html: req.body.content
} }
let result = await Emailer.sendMail(req.body.email, message) const result = await Emailer.sendMail(req.body.email, message)
res.jsonp(result) res.jsonp(result)
})) }))

View File

@ -9,18 +9,18 @@ import Image from '../api/image'
import News from '../api/news' import News from '../api/news'
import API from '../api' import API from '../api'
let router = express.Router() const router = express.Router()
let dev = process.env.NODE_ENV !== 'production' const dev = process.env.NODE_ENV !== 'production'
// Restrict API usage // Restrict API usage
let apiLimiter = new RateLimit({ const apiLimiter = new RateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes windowMs: 5 * 60 * 1000, // 5 minutes
max: 100, max: 100,
delayMs: 0 delayMs: 0
}) })
// Restrict image uploads // Restrict image uploads
let uploadLimiter = new RateLimit({ const uploadLimiter = new RateLimit({
windowMs: 60 * 60 * 1000, // 1 hour windowMs: 60 * 60 * 1000, // 1 hour
max: 10, max: 10,
delayMs: 0 delayMs: 0
@ -30,11 +30,11 @@ router.use(apiLimiter)
// Turn things like 'key1[key2]': 'value' into key1: {key2: 'value'} because facebook // Turn things like 'key1[key2]': 'value' into key1: {key2: 'value'} because facebook
function objectAssembler (insane) { function objectAssembler (insane) {
let object = {} const object = {}
for (let key in insane) { for (const key in insane) {
let value = insane[key] const value = insane[key]
if (key.indexOf('[') !== -1) { if (key.indexOf('[') !== -1) {
let subKey = key.match(/^([\w]+)\[(\w+)\]$/) const subKey = key.match(/^([\w]+)\[(\w+)\]$/)
if (subKey[1] && subKey[2]) { if (subKey[1] && subKey[2]) {
if (!object[subKey[1]]) { if (!object[subKey[1]]) {
object[subKey[1]] = {} object[subKey[1]] = {}
@ -63,7 +63,7 @@ function createSession (req, user) {
// Get either `uuid` or `id` from `:id` parameter // Get either `uuid` or `id` from `:id` parameter
function idParam (req) { function idParam (req) {
let id = req.params.id const id = req.params.id
if (id.length === 36 && id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)) { if (id.length === 36 && id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)) {
return id return id
} }
@ -84,7 +84,7 @@ function JsonData (req, res, error, redirect = '/') {
function removeAuthMiddleware (identifier) { function removeAuthMiddleware (identifier) {
return wrap(async (req, res) => { return wrap(async (req, res) => {
if (!req.session.user) return res.redirect('/login') if (!req.session.user) return res.redirect('/login')
let done = await APIExtern.Common.remove(req.session.user, identifier) const done = await APIExtern.Common.remove(req.session.user, identifier)
if (!done) { if (!done) {
req.flash('message', { error: true, text: 'Unable to unlink social media account' }) req.flash('message', { error: true, text: 'Unable to unlink social media account' })
@ -102,12 +102,12 @@ router.post('/external/facebook/callback', wrap(async (req, res, next) => {
if (!config.external || !config.external.facebook || !config.external.facebook.client) return next() if (!config.external || !config.external.facebook || !config.external.facebook.client) return next()
// Fix up the retarded object Facebook sends us // Fix up the retarded object Facebook sends us
let sane = objectAssembler(req.body) const sane = objectAssembler(req.body)
if (!sane || !sane.authResponse) { if (!sane || !sane.authResponse) {
return next() return next()
} }
let response = await APIExtern.Facebook.callback(req.session.user, sane.authResponse, req.realIP) const response = await APIExtern.Facebook.callback(req.session.user, sane.authResponse, req.realIP)
if (response.banned) { if (response.banned) {
return JsonData(req, res, 'You are banned.') return JsonData(req, res, 'You are banned.')
@ -119,7 +119,7 @@ router.post('/external/facebook/callback', wrap(async (req, res, next) => {
// Create session // Create session
if (!req.session.user) { if (!req.session.user) {
let user = response.user const user = response.user
createSession(req, user) createSession(req, user)
} }
@ -134,7 +134,7 @@ router.get('/external/facebook/remove', removeAuthMiddleware('facebook'))
*/ */
router.get('/external/twitter/login', wrap(async (req, res) => { router.get('/external/twitter/login', wrap(async (req, res) => {
if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/') if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/')
let tokens = await APIExtern.Twitter.getRequestToken() const tokens = await APIExtern.Twitter.getRequestToken()
if (tokens.error) { if (tokens.error) {
return res.jsonp({ error: tokens.error }) return res.jsonp({ error: tokens.error })
@ -148,15 +148,15 @@ router.get('/external/twitter/login', wrap(async (req, res) => {
router.get('/external/twitter/callback', wrap(async (req, res) => { router.get('/external/twitter/callback', wrap(async (req, res) => {
if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/login') if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/login')
if (!req.session.twitter_auth) return res.redirect('/login') if (!req.session.twitter_auth) return res.redirect('/login')
let ta = req.session.twitter_auth const ta = req.session.twitter_auth
let uri = '/login' const uri = '/login'
if (!req.query.oauth_verifier) { if (!req.query.oauth_verifier) {
req.flash('message', { error: true, text: 'Couldn\'t get a verifier' }) req.flash('message', { error: true, text: 'Couldn\'t get a verifier' })
return res.redirect(uri) return res.redirect(uri)
} }
let accessTokens = await APIExtern.Twitter.getAccessTokens(ta.token, ta.token_secret, req.query.oauth_verifier) const accessTokens = await APIExtern.Twitter.getAccessTokens(ta.token, ta.token_secret, req.query.oauth_verifier)
delete req.session.twitter_auth delete req.session.twitter_auth
if (accessTokens.error) { if (accessTokens.error) {
@ -164,7 +164,7 @@ router.get('/external/twitter/callback', wrap(async (req, res) => {
return res.redirect(uri) return res.redirect(uri)
} }
let response = await APIExtern.Twitter.callback(req.session.user, accessTokens, req.realIP) const response = await APIExtern.Twitter.callback(req.session.user, accessTokens, req.realIP)
if (response.banned) { if (response.banned) {
return res.render('user/banned', { bans: response.banned, ipban: response.ip }) return res.render('user/banned', { bans: response.banned, ipban: response.ip })
} }
@ -175,7 +175,7 @@ router.get('/external/twitter/callback', wrap(async (req, res) => {
} }
if (!req.session.user) { if (!req.session.user) {
let user = response.user const user = response.user
createSession(req, user) createSession(req, user)
} }
@ -191,7 +191,7 @@ router.get('/external/twitter/remove', removeAuthMiddleware('twitter'))
router.get('/external/discord/login', wrap(async (req, res) => { router.get('/external/discord/login', wrap(async (req, res) => {
if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/') if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/')
let infos = APIExtern.Discord.getAuthorizeURL(req) const infos = APIExtern.Discord.getAuthorizeURL(req)
res.redirect(infos.url) res.redirect(infos.url)
})) }))
@ -199,9 +199,9 @@ router.get('/external/discord/login', wrap(async (req, res) => {
router.get('/external/discord/callback', wrap(async (req, res) => { router.get('/external/discord/callback', wrap(async (req, res) => {
if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/login') if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/login')
let code = req.query.code const code = req.query.code
let state = req.query.state const state = req.query.state
let uri = '/login' const uri = '/login'
if (!code) { if (!code) {
req.flash('message', { error: true, text: 'No authorization.' }) req.flash('message', { error: true, text: 'No authorization.' })
@ -215,13 +215,13 @@ router.get('/external/discord/callback', wrap(async (req, res) => {
delete req.session.discord_auth delete req.session.discord_auth
let accessToken = await APIExtern.Discord.getAccessToken(code) const accessToken = await APIExtern.Discord.getAccessToken(code)
if (accessToken.error) { if (accessToken.error) {
req.flash('message', { error: true, text: accessToken.error }) req.flash('message', { error: true, text: accessToken.error })
return res.redirect(uri) return res.redirect(uri)
} }
let response = await APIExtern.Discord.callback(req.session.user, accessToken.accessToken, req.realIP) const response = await APIExtern.Discord.callback(req.session.user, accessToken.accessToken, req.realIP)
if (response.banned) { if (response.banned) {
return res.render('user/banned', { bans: response.banned, ipban: response.ip }) return res.render('user/banned', { bans: response.banned, ipban: response.ip })
} }
@ -232,7 +232,7 @@ router.get('/external/discord/callback', wrap(async (req, res) => {
} }
if (!req.session.user) { if (!req.session.user) {
let user = response.user const user = response.user
createSession(req, user) createSession(req, user)
} }
@ -258,7 +258,7 @@ router.post('/external/google/callback', wrap(async (req, res) => {
return JsonData(req, res, 'Invalid or missing ID token!', '/login') return JsonData(req, res, 'Invalid or missing ID token!', '/login')
} }
let response = await APIExtern.Google.callback(req.session.user, req.body, req.realIP) const response = await APIExtern.Google.callback(req.session.user, req.body, req.realIP)
if (response.banned) { if (response.banned) {
return JsonData(req, res, 'You are banned.', '/login') return JsonData(req, res, 'You are banned.', '/login')
} }
@ -268,7 +268,7 @@ router.post('/external/google/callback', wrap(async (req, res) => {
} }
if (!req.session.user) { if (!req.session.user) {
let user = response.user const user = response.user
createSession(req, user) createSession(req, user)
} }
@ -294,8 +294,8 @@ router.get('/news/all/:page', wrap(async (req, res) => {
return res.status(400).jsonp({ error: 'Invalid page number.' }) return res.status(400).jsonp({ error: 'Invalid page number.' })
} }
let page = parseInt(req.params.page) const page = parseInt(req.params.page)
let articles = await News.listNews(page) const articles = await News.listNews(page)
res.jsonp(articles) res.jsonp(articles)
})) }))
@ -306,7 +306,7 @@ router.get('/news/all/', (req, res) => {
}) })
router.post('/news/edit/:id', wrap(async (req, res, next) => { router.post('/news/edit/:id', wrap(async (req, res, next) => {
let id = parseInt(req.params.id) const id = parseInt(req.params.id)
if (!req.session.user || req.session.user.privilege < 1) return next() if (!req.session.user || req.session.user.privilege < 1) return next()
@ -333,15 +333,15 @@ router.get('/news/:id', wrap(async (req, res) => {
return res.status(400).jsonp({ error: 'Invalid ID number.' }) return res.status(400).jsonp({ error: 'Invalid ID number.' })
} }
let id = parseInt(req.params.id) const id = parseInt(req.params.id)
let article = await News.article(id) const article = await News.article(id)
res.jsonp(article) res.jsonp(article)
})) }))
// Preview endpoint // Preview endpoint
router.get('/news', wrap(async (req, res) => { router.get('/news', wrap(async (req, res) => {
let articles = await News.preview() const articles = await News.preview()
res.jsonp(articles) res.jsonp(articles)
})) }))
@ -352,7 +352,7 @@ router.get('/news', wrap(async (req, res) => {
*/ */
// Promisify multiparty form parser // Promisify multiparty form parser
async function promiseForm (req) { async function promiseForm (req) {
let form = new multiparty.Form() const form = new multiparty.Form()
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
form.parse(req, async (err, fields, files) => { form.parse(req, async (err, fields, files) => {
if (err) return reject(err) if (err) return reject(err)
@ -364,12 +364,12 @@ async function promiseForm (req) {
// Upload avatar image // Upload avatar image
router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => { router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let data = await promiseForm(req) const data = await promiseForm(req)
let avatarFile let avatarFile
try { try {
let result = await Image.uploadImage(req.session.user.username, data.fields, data.files) const result = await Image.uploadImage(req.session.user.username, data.fields, data.files)
avatarFile = await API.User.changeAvatar(req.session.user, result.file) avatarFile = await API.User.changeAvatar(req.session.user, result.file)
} catch (e) { } catch (e) {
@ -397,7 +397,7 @@ router.post('/avatar/remove', wrap(async (req, res, next) => {
// Get latest avatar of logged in user // Get latest avatar of logged in user
router.get('/avatar', wrap(async (req, res, next) => { router.get('/avatar', wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let user = req.session.user const user = req.session.user
if (!user.avatar_file) return next() if (!user.avatar_file) return next()
@ -407,10 +407,10 @@ router.get('/avatar', wrap(async (req, res, next) => {
// Get latest avatar of user by id // Get latest avatar of user by id
router.get('/avatar/:id', wrap(async (req, res, next) => { router.get('/avatar/:id', wrap(async (req, res, next) => {
let id = idParam(req) const id = idParam(req)
if (!id) return next() if (!id) return next()
let user = await API.User.get(id) const user = await API.User.get(id)
if (!user || !user.avatar_file) return next() if (!user || !user.avatar_file) return next()
@ -420,7 +420,7 @@ router.get('/avatar/:id', wrap(async (req, res, next) => {
router.get('/avatar/gravatar', (req, res, next) => { router.get('/avatar/gravatar', (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let email = req.session.user.email const email = req.session.user.email
res.set('Content-Type', 'text/plain') res.set('Content-Type', 'text/plain')
res.end(Image.gravatarURL(email)) res.end(Image.gravatarURL(email))
@ -428,11 +428,11 @@ router.get('/avatar/gravatar', (req, res, next) => {
router.post('/avatar/gravatar', wrap(async (req, res, next) => { router.post('/avatar/gravatar', wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let user = req.session.user const user = req.session.user
try { try {
let gravURL = await Image.downloadImage(Image.gravatarURL(user.email), 'GRAV-' + user.username) const gravURL = await Image.downloadImage(Image.gravatarURL(user.email), 'GRAV-' + user.username)
let file = await API.User.changeAvatar(user, gravURL) const file = await API.User.changeAvatar(user, gravURL)
req.session.user.avatar_file = file req.session.user.avatar_file = file
@ -458,7 +458,7 @@ router.use('/avatar', (req, res) => {
router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => { router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let list = await API.User.OAuth2.getUserAuthorizations(req.session.user) const list = await API.User.OAuth2.getUserAuthorizations(req.session.user)
if (!list) return next() if (!list) return next()
res.jsonp(list) res.jsonp(list)
@ -468,10 +468,10 @@ router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) => { router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let clientId = parseInt(req.body.client_id) const clientId = parseInt(req.body.client_id)
if (isNaN(clientId)) return res.status(400).jsonp({ error: 'Missing Client ID parameter' }) if (isNaN(clientId)) return res.status(400).jsonp({ error: 'Missing Client ID parameter' })
let done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId) const done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId)
if (!done) return res.status(400).jsonp({ error: 'Failed to remove client authorization' }) if (!done) return res.status(400).jsonp({ error: 'Failed to remove client authorization' })
res.status(204).end() res.status(204).end()
@ -483,7 +483,7 @@ router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) =>
*/ */
router.post('/paypal/ipn', wrap(async (req, res) => { router.post('/paypal/ipn', wrap(async (req, res) => {
let content = req.body const content = req.body
if (content && content.payment_status && content.payment_status === 'Completed') { if (content && content.payment_status && content.payment_status === 'Completed') {
await API.Payment.handleIPN(content) await API.Payment.handleIPN(content)
@ -495,7 +495,7 @@ router.post('/paypal/ipn', wrap(async (req, res) => {
router.get('/donations/user', wrap(async (req, res, next) => { router.get('/donations/user', wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let contribs = await API.Payment.userContributions(req.session.user) const contribs = await API.Payment.userContributions(req.session.user)
res.jsonp(contribs) res.jsonp(contribs)
})) }))
@ -509,11 +509,11 @@ router.get('/donations', wrap(async (req, res, next) => {
count = 10 count = 10
} }
let mcu = req.query.mcu === '1' || req.query.mcu === 'true' const mcu = req.query.mcu === '1' || req.query.mcu === 'true'
let timeFrame = parseInt(req.query.timeFrame) let timeFrame = parseInt(req.query.timeFrame)
if (isNaN(timeFrame)) timeFrame = 0 if (isNaN(timeFrame)) timeFrame = 0
let contribs = await API.Payment.allContributions(count, mcu, timeFrame) const contribs = await API.Payment.allContributions(count, mcu, timeFrame)
res.jsonp(contribs) res.jsonp(contribs)
})) }))

View File

@ -15,10 +15,10 @@ import apiRouter from './api'
import oauthRouter from './oauth2' import oauthRouter from './oauth2'
import adminRouter from './admin' import adminRouter from './admin'
let router = express.Router() const router = express.Router()
// Restrict account creation // Restrict account creation
let accountLimiter = new RateLimit({ const accountLimiter = new RateLimit({
windowMs: 60 * 60 * 1000, // 1 hour windowMs: 60 * 60 * 1000, // 1 hour
max: 10, max: 10,
delayMs: 0, delayMs: 0,
@ -70,7 +70,7 @@ router.use(wrap(async (req, res, next) => {
console.debug('User session update') console.debug('User session update')
// Check for bans // Check for bans
let banStatus = await API.User.getBanStatus(req.session.user.id) const banStatus = await API.User.getBanStatus(req.session.user.id)
if (banStatus.length) { if (banStatus.length) {
delete req.session.user delete req.session.user
@ -78,7 +78,7 @@ router.use(wrap(async (req, res, next) => {
} }
// Update user session // Update user session
let udata = await API.User.get(req.session.user) const udata = await API.User.get(req.session.user)
setSession(req, udata) setSession(req, udata)
// Update IP address // Update IP address
@ -103,7 +103,7 @@ router.get('/', (req, res) => {
// Add social media login buttons // Add social media login buttons
function extraButtons (recheck) { function extraButtons (recheck) {
let et = config.external const et = config.external
return function (req, res, next) { return function (req, res, next) {
if (!et) return next() if (!et) return next()
res.locals.auth = { res.locals.auth = {
@ -155,8 +155,8 @@ router.get('/login/reset', extraButtons(false), (req, res) => {
// Password reset endpoint (emailed link) // Password reset endpoint (emailed link)
router.get('/reset/:token', wrap(async (req, res) => { router.get('/reset/:token', wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login') if (req.session.user) return res.redirect('/login')
let token = req.params.token const token = req.params.token
let success = await API.User.Reset.resetToken(token) const success = await API.User.Reset.resetToken(token)
if (!success) { if (!success) {
req.flash('message', { error: true, text: 'Invalid or expired reset token.' }) req.flash('message', { error: true, text: 'Invalid or expired reset token.' })
@ -190,8 +190,8 @@ router.get('/register', extraButtons(true), formKeep, (req, res) => {
// User activation endpoint (emailed link) // User activation endpoint (emailed link)
router.get('/activate/:token', wrap(async (req, res) => { router.get('/activate/:token', wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login') if (req.session.user) return res.redirect('/login')
let token = req.params.token const token = req.params.token
let success = await API.User.Login.activationToken(token) const success = await API.User.Login.activationToken(token)
if (!success) { if (!success) {
req.flash('message', { error: true, text: 'Invalid or expired activation token.' }) req.flash('message', { error: true, text: 'Invalid or expired activation token.' })
@ -204,10 +204,10 @@ router.get('/activate/:token', wrap(async (req, res) => {
// View for enabling Two-Factor Authentication // View for enabling Two-Factor Authentication
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => { router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user) const twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
if (twoFaEnabled) return res.redirect('/') if (twoFaEnabled) return res.redirect('/')
let newToken = await API.User.Login.totpAquire(req.session.user) const newToken = await API.User.Login.totpAquire(req.session.user)
if (!newToken) return res.redirect('/') if (!newToken) return res.redirect('/')
res.render('user/totp', { uri: newToken }) res.render('user/totp', { uri: newToken })
@ -215,7 +215,7 @@ router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
// View for disabling Two-Factor Authentication // View for disabling Two-Factor Authentication
router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => { router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => {
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user) const twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
if (!twoFaEnabled) return res.redirect('/') if (!twoFaEnabled) return res.redirect('/')
res.render('user/password') res.render('user/password')
@ -229,13 +229,13 @@ router.get('/login/verify', (req, res) => {
// User settings page // User settings page
router.get('/user/manage', ensureLogin, wrap(async (req, res) => { router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
let totpEnabled = false let totpEnabled = false
let socialStatus = await API.User.socialStatus(req.session.user) const socialStatus = await API.User.socialStatus(req.session.user)
if (socialStatus.password) { if (socialStatus.password) {
totpEnabled = await API.User.Login.totpTokenRequired(req.session.user) totpEnabled = await API.User.Login.totpTokenRequired(req.session.user)
} }
let et = config.external const et = config.external
if (et) { if (et) {
res.locals.auth = {} res.locals.auth = {}
// Decide whether we need a disconnect or a log in with button for social account logins // Decide whether we need a disconnect or a log in with button for social account logins
@ -277,7 +277,7 @@ router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
// Change password // Change password
router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => { router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
let socialStatus = await API.User.socialStatus(req.session.user) const socialStatus = await API.User.socialStatus(req.session.user)
res.render('user/password_new', { token: !socialStatus.password }) res.render('user/password_new', { token: !socialStatus.password })
})) }))
@ -286,12 +286,12 @@ router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => { router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
let obfuscated = req.session.user.email let obfuscated = req.session.user.email
if (obfuscated) { if (obfuscated) {
let split = obfuscated.split('@') const split = obfuscated.split('@')
let rep = split[0].charAt(0) + '***' + split[0].charAt(split[0].length - 1) const rep = split[0].charAt(0) + '***' + split[0].charAt(split[0].length - 1)
obfuscated = rep + '@' + split[1] obfuscated = rep + '@' + split[1]
} }
let socialStatus = await API.User.socialStatus(req.session.user) const socialStatus = await API.User.socialStatus(req.session.user)
res.render('user/email_change', { email: obfuscated, password: socialStatus.password }) res.render('user/email_change', { email: obfuscated, password: socialStatus.password })
})) }))
@ -311,7 +311,7 @@ router.get('/donate', wrap(async (req, res, next) => {
// Used to display errors on forms and save data // Used to display errors on forms and save data
function formError (req, res, error, redirect) { function formError (req, res, error, redirect) {
// Security measures: never store any passwords in any session // Security measures: never store any passwords in any session
for (let key in req.body) { for (const key in req.body) {
if (key.indexOf('password') !== -1) { if (key.indexOf('password') !== -1) {
delete req.body[key] delete req.body[key]
} }
@ -350,7 +350,7 @@ router.post('/user/two-factor', csrfValidation, wrap(async (req, res, next) => {
return formError(req, res, 'You need to enter the code.') return formError(req, res, 'You need to enter the code.')
} }
let verified = await API.User.Login.totpCheck(req.session.user, req.body.code) const verified = await API.User.Login.totpCheck(req.session.user, req.body.code)
if (!verified) { if (!verified) {
return formError(req, res, 'Something went wrong! Try scanning the code again.') return formError(req, res, 'Something went wrong! Try scanning the code again.')
} }
@ -366,7 +366,7 @@ router.post('/user/two-factor/disable', csrfValidation, wrap(async (req, res, ne
return formError(req, res, 'Please enter your password.') return formError(req, res, 'Please enter your password.')
} }
let purge = await API.User.Login.purgeTotp(req.session.user, req.body.password) const purge = await API.User.Login.purgeTotp(req.session.user, req.body.password)
if (!purge) { if (!purge) {
return formError(req, res, 'Invalid password.') return formError(req, res, 'Invalid password.')
} }
@ -384,12 +384,12 @@ router.post('/login/verify', csrfValidation, wrap(async (req, res, next) => {
return formError(req, res, 'You need to enter the code.') return formError(req, res, 'You need to enter the code.')
} }
let totpCheck = await API.User.Login.totpCheck(req.session.totp_check, req.body.code, req.body.recovery || false) const totpCheck = await API.User.Login.totpCheck(req.session.totp_check, req.body.code, req.body.recovery || false)
if (!totpCheck) { if (!totpCheck) {
return formError(req, res, 'Invalid code!') return formError(req, res, 'Invalid code!')
} }
let user = await API.User.get(req.session.totp_check) const user = await API.User.get(req.session.totp_check)
delete req.session.totp_check delete req.session.totp_check
setSession(req, user) setSession(req, user)
@ -404,12 +404,12 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
return res.redirect('/login') return res.redirect('/login')
} }
let user = await API.User.get(req.body.username) const 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.') 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) const 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.')
if (user.activated === 0) return formError(req, res, 'Please activate your account first. If you did not receive an email, please contact an administrator.') if (user.activated === 0) return formError(req, res, 'Please activate your account first. If you did not receive an email, please contact an administrator.')
@ -417,13 +417,13 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
if (user.locked === 1) return formError(req, res, 'This account has been locked. Please contact an administrator for more information.') if (user.locked === 1) return formError(req, res, 'This account has been locked. Please contact an administrator for more information.')
// Check if the user is banned // Check if the user is banned
let banStatus = await API.User.getBanStatus(user.id) const banStatus = await API.User.getBanStatus(user.id)
if (banStatus.length) { if (banStatus.length) {
return res.render('user/banned', { bans: banStatus, ipban: false }) return res.render('user/banned', { bans: banStatus, ipban: false })
} }
// Redirect to the verification dialog if 2FA is enabled // Redirect to the verification dialog if 2FA is enabled
let totpRequired = await API.User.Login.totpTokenRequired(user) const totpRequired = await API.User.Login.totpTokenRequired(user)
if (totpRequired) { if (totpRequired) {
req.session.totp_check = user.id req.session.totp_check = user.id
return res.redirect('/login/verify') return res.redirect('/login/verify')
@ -453,8 +453,8 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
return formError(req, res, 'You need to enter your email address.') return formError(req, res, 'You need to enter your email address.')
} }
let email = req.body.email const email = req.body.email
let validEmail = await API.User.Register.validateEmail(email) const validEmail = await API.User.Register.validateEmail(email)
if (!validEmail) { if (!validEmail) {
return formError(req, res, 'You need to enter a valid email address.') return formError(req, res, 'You need to enter a valid email address.')
} }
@ -472,8 +472,8 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
// Password reset endpoint (emailed link) // Password reset endpoint (emailed link)
router.post('/reset/:token', csrfValidation, wrap(async (req, res) => { router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login') if (req.session.user) return res.redirect('/login')
let token = req.params.token const token = req.params.token
let user = await API.User.Reset.resetToken(token) const user = await API.User.Reset.resetToken(token)
if (!user) { if (!user) {
req.flash('message', { error: true, text: 'Invalid or expired reset token.' }) req.flash('message', { error: true, text: 'Invalid or expired reset token.' })
@ -482,13 +482,13 @@ router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
} }
// 4th Check: Password length // 4th Check: Password length
let password = req.body.password const password = req.body.password
if (!password || password.length < 8) { if (!password || password.length < 8) {
return formError(req, res, 'Invalid password! Please use at least 8 characters!') return formError(req, res, 'Invalid password! Please use at least 8 characters!')
} }
// 5th Check: Password match // 5th Check: Password match
let passwordAgain = req.body.password_repeat const passwordAgain = req.body.password_repeat
if (!passwordAgain || password !== passwordAgain) { if (!passwordAgain || password !== passwordAgain) {
return formError(req, res, 'Passwords do not match!') return formError(req, res, 'Passwords do not match!')
} }
@ -514,37 +514,37 @@ router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, n
} }
// Ban check // Ban check
let banStatus = await API.User.getBanStatus(req.realIP, true) const banStatus = await API.User.getBanStatus(req.realIP, true)
if (banStatus.length) { if (banStatus.length) {
return res.render('user/banned', { bans: banStatus, ipban: true }) return res.render('user/banned', { bans: banStatus, ipban: true })
} }
// 1st Check: Username Characters and length // 1st Check: Username Characters and length
let username = req.body.username const 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!')
} }
// 2nd Check: Display Name // 2nd Check: Display Name
let displayName = req.body.display_name const displayName = req.body.display_name
if (!displayName || !displayName.match(/^([^\\`]{3,32})$/i)) { if (!displayName || !displayName.match(/^([^\\`]{3,32})$/i)) {
return formError(req, res, 'Invalid display name!') return formError(req, res, 'Invalid display name!')
} }
// 3rd Check: Email Address // 3rd Check: Email Address
let email = req.body.email const email = req.body.email
if (!email || !API.User.Register.validateEmail(email)) { if (!email || !API.User.Register.validateEmail(email)) {
return formError(req, res, 'Invalid email address!') return formError(req, res, 'Invalid email address!')
} }
// 4th Check: Password length // 4th Check: Password length
let password = req.body.password const password = req.body.password
if (!password || password.length < 8) { if (!password || password.length < 8) {
return formError(req, res, 'Invalid password! Please use at least 8 characters!') return formError(req, res, 'Invalid password! Please use at least 8 characters!')
} }
// 5th Check: Password match // 5th Check: Password match
let passwordAgain = req.body.password_repeat const passwordAgain = req.body.password_repeat
if (!passwordAgain || password !== passwordAgain) { if (!passwordAgain || password !== passwordAgain) {
return formError(req, res, 'Passwords do not match!') return formError(req, res, 'Passwords do not match!')
} }
@ -570,7 +570,7 @@ router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, n
} }
// Hash the password // Hash the password
let hash = await API.User.Register.hashPassword(password) const hash = await API.User.Register.hashPassword(password)
let newUser let newUser
// Attempt to create the user // Attempt to create the user
@ -633,15 +633,15 @@ router.post('/user/manage', csrfValidation, wrap(async (req, res, next) => {
// Change user password // Change user password
router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async (req, res, next) => { router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let user = req.session.user const user = req.session.user
let socialStatus = await API.User.socialStatus(user) const socialStatus = await API.User.socialStatus(user)
if (!req.body.password_old && socialStatus.password) { if (!req.body.password_old && socialStatus.password) {
return formError(req, res, 'Please enter your current password.') return formError(req, res, 'Please enter your current password.')
} }
if (socialStatus.password) { if (socialStatus.password) {
let passwordMatch = await API.User.Login.password(user, req.body.password_old) const 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.')
} }
@ -652,7 +652,7 @@ router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async
return formError(req, res, 'Invalid password! Please use at least 8 characters!') return formError(req, res, 'Invalid password! Please use at least 8 characters!')
} }
let passwordAgain = req.body.password_repeat const passwordAgain = req.body.password_repeat
if (!passwordAgain || password !== passwordAgain) { if (!passwordAgain || password !== passwordAgain) {
return formError(req, res, 'The passwords do not match!') return formError(req, res, 'The passwords do not match!')
} }
@ -684,10 +684,10 @@ router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async
router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (req, res, next) => { router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
if (!req.session.user) return next() if (!req.session.user) return next()
let user = await API.User.get(req.session.user) const user = await API.User.get(req.session.user)
let email = req.body.email const email = req.body.email
let newEmail = req.body.email_new const newEmail = req.body.email_new
let password = req.body.password const password = req.body.password
if (!newEmail || (!email && user.email !== '')) { if (!newEmail || (!email && user.email !== '')) {
return formError(req, res, 'Please fill in all of the fields.') return formError(req, res, 'Please fill in all of the fields.')
@ -702,18 +702,18 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
return formError(req, res, 'Enter a password.') return formError(req, res, 'Enter a password.')
} }
let passwordMatch = await API.User.Login.password(user, password) const passwordMatch = await API.User.Login.password(user, password)
if (!passwordMatch) { if (!passwordMatch) {
return formError(req, res, 'The password you provided is incorrect.') return formError(req, res, 'The password you provided is incorrect.')
} }
} }
let emailValid = API.User.Register.validateEmail(newEmail) const emailValid = API.User.Register.validateEmail(newEmail)
if (!emailValid) { if (!emailValid) {
return formError(req, res, 'Invalid email address.') return formError(req, res, 'Invalid email address.')
} }
let emailTaken = await API.User.get(newEmail) const emailTaken = await API.User.get(newEmail)
if (emailTaken) { if (emailTaken) {
return formError(req, res, 'This email is already taken.') return formError(req, res, 'This email is already taken.')
} }
@ -743,7 +743,7 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
// Serve a document form the documents directory, cache it. // Serve a document form the documents directory, cache it.
const docsDir = path.join(__dirname, '../../documents') const docsDir = path.join(__dirname, '../../documents')
router.get('/docs/:name', wrap(async (req, res, next) => { router.get('/docs/:name', wrap(async (req, res, next) => {
let doc = path.join(docsDir, req.params.name + '.html') const doc = path.join(docsDir, req.params.name + '.html')
if (!await fs.exists(docsDir) || !await fs.exists(doc)) { if (!await fs.exists(docsDir) || !await fs.exists(doc)) {
return next() return next()
} }
@ -788,12 +788,12 @@ router.post('/news/compose', newsPrivilege, csrfValidation, wrap(async (req, res
// Serve news // Serve news
router.get('/news/:id?-*', wrap(async (req, res) => { router.get('/news/:id?-*', wrap(async (req, res) => {
let id = parseInt(req.params.id) const id = parseInt(req.params.id)
if (isNaN(id)) { if (isNaN(id)) {
return res.status(404).render('article', { article: null }) return res.status(404).render('article', { article: null })
} }
let article = await News.article(id) const article = await News.article(id)
if (!article.id) { if (!article.id) {
return res.status(404).render('article', { article: null }) return res.status(404).render('article', { article: null })
} }
@ -812,20 +812,20 @@ router.get('/news/', wrap(async (req, res) => {
page = 1 page = 1
} }
let news = await News.listNews(page) const news = await News.listNews(page)
res.render('news/news', { news: news }) res.render('news/news', { news: news })
})) }))
router.get('/news/atom.xml', wrap(async (req, res) => { router.get('/news/atom.xml', wrap(async (req, res) => {
let feed = await News.generateFeed() const feed = await News.generateFeed()
res.set('Content-Type', 'application/atom+xml') res.set('Content-Type', 'application/atom+xml')
res.send(feed.atom1()) res.send(feed.atom1())
})) }))
router.get('/news/feed.json', wrap(async (req, res) => { router.get('/news/feed.json', wrap(async (req, res) => {
let feed = await News.generateFeed() const feed = await News.generateFeed()
res.set('Content-Type', 'application/json') res.set('Content-Type', 'application/json')
res.send(feed.json1()) res.send(feed.json1())

View File

@ -6,12 +6,12 @@ import RateLimit from 'express-rate-limit'
import wrap from '../../scripts/asyncRoute' import wrap from '../../scripts/asyncRoute'
import config from '../../scripts/load-config.js' import config from '../../scripts/load-config.js'
let router = express.Router() const router = express.Router()
let oauth = new OAuth2() const oauth = new OAuth2()
router.use(oauth.express()) router.use(oauth.express())
let oauthLimiter = new RateLimit({ const oauthLimiter = new RateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes windowMs: 5 * 60 * 1000, // 5 minutes
max: 10, max: 10,
delayMs: 0 delayMs: 0
@ -32,8 +32,8 @@ router.post('/introspect', oauth.controller.introspection)
// Protected user information resource // Protected user information resource
router.get('/user', oauth.bearer, wrap(async (req, res) => { router.get('/user', oauth.bearer, wrap(async (req, res) => {
let accessToken = req.oauth2.accessToken const accessToken = req.oauth2.accessToken
let user = await UAPI.User.get(accessToken.user_id) const user = await UAPI.User.get(accessToken.user_id)
if (!user) { if (!user) {
return res.status(404).jsonp({ return res.status(404).jsonp({
@ -41,7 +41,7 @@ router.get('/user', oauth.bearer, wrap(async (req, res) => {
}) })
} }
let udata = { const udata = {
id: user.id, id: user.id,
uuid: user.uuid, uuid: user.uuid,
username: user.username, username: user.username,

View File

@ -12,8 +12,8 @@ import flash from '../scripts/flash'
import config from '../scripts/load-config' import config from '../scripts/load-config'
import email from './api/emailer' import email from './api/emailer'
let app = express() const app = express()
let SessionStore = connectSession(session) const SessionStore = connectSession(session)
app.enable('trust proxy', 1) app.enable('trust proxy', 1)
@ -80,7 +80,7 @@ module.exports = (args) => {
app.use(morgan('dev')) app.use(morgan('dev'))
} }
let staticAge = args.dev ? 1000 : 7 * 24 * 60 * 60 * 1000 // 1 week of cache in production const staticAge = args.dev ? 1000 : 7 * 24 * 60 * 60 * 1000 // 1 week of cache in production
// Static content directories, cache these requests. // Static content directories, cache these requests.
// It is also a good idea to use nginx to serve these directories in order to save on computing power // It is also a good idea to use nginx to serve these directories in order to save on computing power

View File

@ -34,5 +34,5 @@ block body
input#custominfo(type="hidden", name="custom", value="") input#custominfo(type="hidden", name="custom", value="")
.buttoncont .buttoncont
a.btn.btn-primary.text-light(name="submit", onclick="$(this).closest('form').submit()") a.btn.btn-primary.text-light(name="submit", onclick="$(this).closest('form').submit()")
i.fa.fa-fw.fa-paypal i.fab.fa-fw.fa-paypal
|&nbsp;Donate |&nbsp;Donate

View File

@ -18,7 +18,7 @@ block body
input(type="hidden", name="csrf", value=csrf) input(type="hidden", name="csrf", value=csrf)
.form-group .form-group
label(for="username") Username or Email Address label(for="username") Username or Email Address
input.form-control(type="text", name="username", id="username") input.form-control(type="text", name="username", id="username", autofocus)
.form-group .form-group
label(for="password") Password label(for="password") Password
input.form-control(type="password", name="password", id="password") input.form-control(type="password", name="password", id="password")

View File

@ -17,5 +17,5 @@ block body
input(type="hidden", name="csrf", value=csrf) input(type="hidden", name="csrf", value=csrf)
.form-group .form-group
label(for="password") Password label(for="password") Password
input.form-control#password(type="password", name="password") input.form-control#password(type="password", name="password", autofocus)
button.btn.btn-primary(type="submit") Continue button.btn.btn-primary(type="submit") Continue

View File

@ -16,7 +16,7 @@ block body
input(type="hidden", name="csrf", value=csrf) input(type="hidden", name="csrf", value=csrf)
.form-group .form-group
label(for="username") Username label(for="username") Username
input.form-control#username(type="text", name="username", value=formkeep.username, aria-labelledby="usernameText") input.form-control#username(type="text", name="username", value=formkeep.username, aria-labelledby="usernameText", autofocus)
small#usernameText English characters, numbers and -_ only. small#usernameText English characters, numbers and -_ only.
.form-group .form-group
label(for="display_name") Display Name label(for="display_name") Display Name

View File

@ -18,5 +18,5 @@ block body
input(type="hidden", name="csrf", value=csrf) input(type="hidden", name="csrf", value=csrf)
.form-group .form-group
label(for="email") Email Address label(for="email") Email Address
input.form-control#email(type="email", name="email") input.form-control#email(type="email", name="email", autofocus)
button.btn.btn-primary(type="submit") Continue button.btn.btn-primary(type="submit") Continue

View File

@ -17,5 +17,5 @@ block body
input(type="hidden", name="csrf", value=csrf) input(type="hidden", name="csrf", value=csrf)
.form-group .form-group
label(for="code") Code label(for="code") Code
input.form-control#code(type="text", name="code", autocomplete="off") input.form-control#code(type="text", name="code", autocomplete="off", autofocus)
button.btn.btn-primary(type="submit") Log in button.btn.btn-primary(type="submit") Log in

View File

@ -25,7 +25,7 @@ block body
form#totpForm(method="POST", action="") form#totpForm(method="POST", action="")
input(type="hidden", name="csrf", value=csrf) input(type="hidden", name="csrf", value=csrf)
.form-group .form-group
label(for="code") Enter the Code label(for="code",autofocus) Enter the Code
input.form-control#code(type="text", name="code", autocomplete="off") input.form-control#code(type="text", name="code", autocomplete="off")
button.btn.btn-primary(type="submit") Enable Now button.btn.btn-primary(type="submit") Enable Now
aside.col aside.col

View File

@ -12,7 +12,7 @@ module.exports = {
}, },
resolve: { resolve: {
alias: { alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1 vue$: 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
} }
}, },
module: { module: {