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

View File

@ -6,7 +6,7 @@ if (process.argv.indexOf('-d') === -1 && process.argv.indexOf('--development') =
}
require('@babel/register')({
plugins: [ '@babel/plugin-transform-modules-commonjs' ]
plugins: ['@babel/plugin-transform-modules-commonjs']
})
require(path.join(__dirname, 'server'))

7242
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -8,7 +8,7 @@ const format = util.format
module.exports = function (options) {
options = options || {}
let safe = (options.unsafe === undefined) ? true : !options.unsafe
const safe = (options.unsafe === undefined) ? true : !options.unsafe
return function (req, res, next) {
if (req.flash && safe) { return next() }
@ -20,10 +20,10 @@ module.exports = function (options) {
function _flash (type, msg) {
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 (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)
} else if (Array.isArray(msg)) {
msg.forEach((val) => {
@ -33,7 +33,7 @@ function _flash (type, msg) {
}
return (msgs[type] = msgs[type] || []).push(msg)
} else if (type) {
let arr = msgs[type]
const arr = msgs[type]
delete msgs[type]
return arr || []
} else {

View File

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

View File

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

View File

@ -20,11 +20,12 @@ function dateFormat (date) {
// Console.log/error/warn "middleware" - add timestamp and write to file
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 = message.replace(/\\u001b/g, '\x1b')
if (lfs) {
// eslint-disable-next-line no-control-regex
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"
const realConsoleLog = console.log
console.log = function () {
let message = util.format.apply(null, arguments)
const message = util.format.apply(null, arguments)
stampAndWrite.call(this, realConsoleLog, '', 'info', message)
}
const realConsoleWarn = console.warn
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)
}
const realConsoleError = console.error
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)
}
async function initializeLogger () {
let logPath = path.resolve(config.logger.file)
const logPath = path.resolve(config.logger.file)
// Only throw bad permission errors
try {
@ -61,7 +62,7 @@ async function initializeLogger () {
}
try {
lfs = fs.createWriteStream(logPath, {flags: 'a'})
lfs = fs.createWriteStream(logPath, { flags: 'a' })
} catch (e) {
lfs = null
console.error('Failed to initiate log file write stream')

View File

@ -4,7 +4,7 @@ import Models from './models'
const perPage = 6
async function cleanUserObject (dbe, admin) {
let totp = await Users.User.Login.totpTokenRequired(dbe)
const totp = await Users.User.Login.totpTokenRequired(dbe)
return {
id: dbe.id,
@ -25,7 +25,7 @@ async function cleanUserObject (dbe, admin) {
}
async function cleanClientObject (dbe) {
let user = await Users.User.get(dbe.user_id)
const user = await Users.User.get(dbe.user_id)
return {
id: dbe.id,
title: dbe.title,
@ -46,8 +46,8 @@ async function cleanClientObject (dbe) {
}
async function cleanBanObject (dbe) {
let user = await Users.User.get(dbe.user_id)
let admin = await Users.User.get(dbe.admin_id)
const user = await Users.User.get(dbe.user_id)
const admin = await Users.User.get(dbe.admin_id)
return {
id: dbe.id,
reason: dbe.reason,
@ -68,14 +68,14 @@ async function cleanBanObject (dbe) {
function dataFilter (data, fields, optional = []) {
// Remove keys not listed in `fields`
for (let i in data) {
for (const i in data) {
if (fields.indexOf(i) === -1) {
delete data[i]
}
}
// 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
}
@ -86,18 +86,18 @@ const API = {
// List all users (paginated)
getAllUsers: async function (page, adminId) {
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' }
}
count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.User.query().offset(paginated.offset).limit(perPage)
let admin = await Users.User.get(adminId)
const paginated = Users.Pagination(perPage, parseInt(count), page)
const raw = await Models.User.query().offset(paginated.offset).limit(perPage)
const admin = await Users.User.get(adminId)
let users = []
for (let i in raw) {
let entry = raw[i]
const users = []
for (const i in raw) {
const entry = raw[i]
users.push(await cleanUserObject(entry, admin))
}
@ -108,16 +108,16 @@ const API = {
}
},
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')
return cleanUserObject(user, null)
},
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')
let fields = [
const fields = [
'username', 'display_name', 'email', 'nw_privilege', 'activated'
]
@ -129,7 +129,7 @@ const API = {
return {}
},
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.activated === 1) return {}
@ -139,7 +139,7 @@ const API = {
return {}
},
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')
await Models.TotpToken.query().delete().where('user_id', user.id)
@ -147,12 +147,12 @@ const API = {
return {}
},
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')
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 }
},
// Search for users by terms and fields
searchUsers: async function (terms, fields = ['email']) {
@ -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' }
let cleaned = []
for (let i in rows) {
let userRaw = rows[i]
const cleaned = []
for (const i in rows) {
const userRaw = rows[i]
cleaned.push(await cleanUserObject(userRaw, null))
}
@ -181,17 +181,17 @@ const API = {
// List all clients (paginated)
getAllClients: async function (page) {
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' }
}
count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage)
const paginated = Users.Pagination(perPage, parseInt(count), page)
const raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage)
let clients = []
for (let i in raw) {
let entry = raw[i]
const clients = []
for (const i in raw) {
const entry = raw[i]
clients.push(await cleanClientObject(entry))
}
@ -202,14 +202,14 @@ const API = {
},
// Get information about a client via 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')
return cleanClientObject(raw[0])
},
// Update a client `id` in database with `data`
updateClient: async function (id, data) {
let fields = [
const fields = [
'title', 'description', 'url', 'redirect_url', 'scope', 'verified'
]
@ -228,10 +228,10 @@ const API = {
// Create a new secret for a client
newSecret: async function (id) {
if (isNaN(id)) throw new Error('Invalid client ID')
let secret = Users.Hash(16)
const secret = Users.Hash(16)
try {
await Models.OAuth2Client.query().patchAndFetchById(id, {secret: secret})
await Models.OAuth2Client.query().patchAndFetchById(id, { secret: secret })
} catch (e) {
throw new Error('No such client')
}
@ -240,14 +240,14 @@ const API = {
},
// Create a new client
createClient: async function (data, user) {
let fields = [
const fields = [
'title', 'description', 'url', 'redirect_url', 'scope'
]
data = dataFilter(data, fields, ['scope'])
if (!data) throw new Error('Missing fields')
let obj = Object.assign({
const obj = Object.assign({
secret: Users.Hash(16),
grants: 'authorization_code',
created_at: new Date(),
@ -268,17 +268,17 @@ const API = {
// List all bans (paginated)
getAllBans: async function (page) {
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' }
}
count = count[0].ids
let paginated = Users.Pagination(perPage, parseInt(count), page)
let raw = await Models.Ban.query().offset(paginated.offset).limit(perPage)
const paginated = Users.Pagination(perPage, parseInt(count), page)
const raw = await Models.Ban.query().offset(paginated.offset).limit(perPage)
let bans = []
for (let i in raw) {
let entry = raw[i]
const bans = []
for (const i in raw) {
const entry = raw[i]
bans.push(await cleanBanObject(entry))
}
@ -293,16 +293,16 @@ const API = {
},
// Create a ban
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.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.')
let banAdd = {
const banAdd = {
reason: data.reason || 'Unspecified ban',
admin_id: adminId,
user_id: user.id,
@ -315,13 +315,13 @@ const API = {
return {}
},
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) {
throw new Error('Cannot lock this user.')
}
let lockId = Users.Hash(4)
let userObf = {
const lockId = Users.Hash(4)
const userObf = {
username: lockId,
display_name: user.username,
email: `${lockId}@icynet.eu`,

View File

@ -1,4 +1,4 @@
import {EmailTemplate} from 'email-templates'
import { EmailTemplate } from 'email-templates'
import path from 'path'
import nodemailer from 'nodemailer'
@ -10,7 +10,7 @@ tls.DEFAULT_ECDH_CURVE = 'auto'
const templateDir = path.join(__dirname, '../../', 'templates')
let templateCache = {}
const templateCache = {}
let transporter
// Send an email to `email` with `headers`
@ -42,7 +42,7 @@ async function pushMail (template, email, context) {
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)

View File

@ -1,6 +1,6 @@
import qs from 'querystring'
import oauth from 'oauth-libre'
import uuidV1 from 'uuid/v1'
import { v1 as uuidV1 } from 'uuid'
import crypto from 'crypto'
import config from '../../scripts/load-config'
@ -18,7 +18,7 @@ const API = {
Common: {
// Generate a hash based on the current session
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')
},
// Find an user with an external ID
@ -29,7 +29,7 @@ const API = {
extr.user = 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) {
extr.user = user
}
@ -39,12 +39,12 @@ const API = {
},
// Get user ban status
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
},
// Create a new `external` instance for a user
new: async (service, identifier, user) => {
let data = {
const data = {
user_id: user.id,
service: service,
identifier: identifier,
@ -57,7 +57,7 @@ const API = {
// Create a new user
newUser: async (service, identifier, data) => {
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,
created_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,
// tell them to log in first.
if (udataLimited.email && udataLimited.email !== '') {
let getByEmail = await UAPI.User.get(udataLimited.email)
const getByEmail = await UAPI.User.get(udataLimited.email)
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.')
}
}
// 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)
return newUser
@ -98,7 +98,7 @@ const API = {
// Remove an `external` object (thus unlinking from a service)
remove: async (user, service) => {
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) {
return false
@ -113,30 +113,30 @@ const API = {
},
// Common code for all auth callbacks
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) {
// 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 (exists) return {error: null, user: user}
if (exists) return { error: null, user: user }
await API.Common.new(identifier, uid, user)
return {error: null, user: user}
return { error: null, user: user }
}
// Callback succeeded with user id and the external table exists, we log in the user
if (exists) {
// 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 }
return {error: null, user: exists.user}
return { error: null, user: exists.user }
}
// 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 }
// Run the function for avatar fetching
@ -146,14 +146,14 @@ const API = {
}
// Assign the data
let newUData = Object.assign({
const newUData = Object.assign({
email: remoteData.email || '',
avatar_file: avatar,
ip_address: ipAddress
}, remoteData)
// Remove unnecessary fields
for (let i in newUData) {
for (const i in newUData) {
if (userFields.indexOf(i) === -1) {
delete newUData[i]
}
@ -163,10 +163,10 @@ const API = {
try {
newUser = await API.Common.newUser(identifier, uid, newUData)
} catch (e) {
return {error: e.message}
return { error: e.message }
}
return {error: null, user: newUser}
return { error: null, user: newUser }
}
},
Facebook: {
@ -175,7 +175,7 @@ const API = {
if (rawData.picture) {
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) {
profilepic = imgdata.fileName
}
@ -186,17 +186,17 @@ const API = {
},
callback: async (user, authResponse, ipAddress) => {
if (!authResponse) {
return {error: 'No Authorization'}
return { error: 'No Authorization' }
}
let uid = authResponse.userID
const uid = authResponse.userID
if (!uid) {
return {error: 'No Authorization'}
return { error: 'No Authorization' }
}
// Get facebook user information in order to create a new user or verify
let fbdata
let intel = {
const intel = {
access_token: authResponse.accessToken,
fields: 'name,email,picture,short_name'
}
@ -205,14 +205,14 @@ const API = {
fbdata = await http.GET('https://graph.facebook.com/v2.10/' + uid + '?' + qs.stringify(intel))
fbdata = JSON.parse(fbdata)
} catch (e) {
return {error: 'Could not get user information', errorObject: e}
return { error: 'Could not get user information', errorObject: e }
}
if (fbdata.error) {
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),
display_name: fbdata.name,
email: fbdata.email || ''
@ -226,7 +226,7 @@ const API = {
let profilepic = null
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) {
profilepic = imgdata.fileName
}
@ -236,7 +236,7 @@ const API = {
},
oauthApp: function () {
if (!twitterApp) {
let redirectUri = config.server.domain + '/api/external/twitter/callback'
const redirectUri = config.server.domain + '/api/external/twitter/callback'
twitterApp = new oauth.PromiseOAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
@ -256,12 +256,12 @@ const API = {
tokens = await twitterApp.getOAuthRequestToken()
} catch (e) {
console.error(e)
return {error: 'No tokens returned'}
return { error: 'No tokens returned' }
}
if (tokens[2].oauth_callback_confirmed !== 'true') return {error: 'No tokens returned.'}
if (tokens[2].oauth_callback_confirmed !== 'true') return { error: 'No tokens returned.' }
return {error: null, token: tokens[0], token_secret: tokens[1]}
return { error: null, token: tokens[0], token_secret: tokens[1] }
},
getAccessTokens: async function (token, secret, verifier) {
if (!twitterApp) API.Twitter.oauthApp()
@ -271,28 +271,28 @@ const API = {
tokens = await twitterApp.getOAuthAccessToken(token, secret, verifier)
} catch (e) {
console.error(e)
return {error: 'No tokens returned'}
return { error: 'No tokens returned' }
}
if (!tokens || !tokens.length) return {error: 'No tokens returned'}
if (!tokens || !tokens.length) return { error: 'No tokens returned' }
return {error: null, access_token: tokens[0], access_token_secret: tokens[1]}
return { error: null, access_token: tokens[0], access_token_secret: tokens[1] }
},
callback: async function (user, accessTokens, ipAddress) {
if (!twitterApp) API.Twitter.oauthApp()
let twdata
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)
twdata = JSON.parse(resp[0])
} catch (e) {
console.error(e)
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,
display_name: twdata.name,
email: twdata.email || ''
@ -305,7 +305,7 @@ const API = {
getAvatar: async (rawData) => {
let profilepic = null
if (rawData.image) {
let imgdata = await Image.downloadImage(rawData.image)
const imgdata = await Image.downloadImage(rawData.image)
if (imgdata && imgdata.fileName) {
profilepic = imgdata.fileName
}
@ -317,10 +317,10 @@ const API = {
let uid
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!')
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.email !== data.email || jsondata.name !== data.name) throw new Error('Conflicting data. Please try again!')
@ -329,10 +329,10 @@ const API = {
uid = jsondata.sub
} catch (e) {
return {error: e.message}
return { error: e.message }
}
let cleanedData = Object.assign(data, {
const cleanedData = Object.assign(data, {
username: data.name,
display_name: data.name,
email: data.email || ''
@ -344,10 +344,10 @@ const API = {
Discord: {
getAvatar: async (rawData) => {
let profilepic = null
let aviSnowflake = rawData.avatar
const aviSnowflake = rawData.avatar
if (aviSnowflake) {
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) {
profilepic = avpt.fileName
}
@ -372,54 +372,54 @@ const API = {
},
getAuthorizeURL: function (req) {
if (!discordApp) API.Discord.oauth2App()
let state = API.Common.stateGenerator(req)
let redirectUri = config.server.domain + '/api/external/discord/callback'
const state = API.Common.stateGenerator(req)
const redirectUri = config.server.domain + '/api/external/discord/callback'
const params = {
'client_id': config.external.discord.api,
'redirect_uri': redirectUri,
'scope': 'identify email',
'response_type': 'code',
'state': state
client_id: config.external.discord.api,
redirect_uri: redirectUri,
scope: 'identify email',
response_type: 'code',
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) {
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
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 })
} catch (e) {
console.error(e)
return {error: 'No Authorization'}
return { error: 'No Authorization' }
}
if (!tokens.length) return {error: 'No Tokens'}
if (!tokens.length) return { error: 'No Tokens' }
tokens = tokens[2]
return {error: null, accessToken: tokens.access_token}
return { error: null, accessToken: tokens.access_token }
},
callback: async function (user, accessToken, ipAddress) {
if (!discordApp) API.Discord.oauth2App()
let ddata
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)
} catch (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
let cleanedData = Object.assign(ddata, {
const cleanedData = Object.assign(ddata, {
display_name: ddata.username,
email: ddata.email || ''
})

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import {Model} from '../../scripts/load-database'
import { Model } from '../../scripts/load-database'
class User extends Model {
static get tableName () {

View File

@ -13,8 +13,8 @@ function slugify (title) {
}
async function cleanArticle (entry, shortenContent = false) {
let poster = await API.User.get(entry.user_id)
let article = {
const poster = await API.User.get(entry.user_id)
const article = {
id: entry.id,
slug: slugify(entry.title),
title: entry.title,
@ -40,13 +40,13 @@ async function cleanArticle (entry, shortenContent = false) {
const News = {
preview: async () => {
// 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 []
let articles = []
for (let i in news) {
let entry = news[i]
const articles = []
for (const i in news) {
const entry = news[i]
articles.push(await cleanArticle(entry))
}
@ -56,17 +56,17 @@ const News = {
let count = await Models.News.query().count('id as ids')
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' }
}
count = count[0].ids
let paginated = API.Pagination(perPage, parseInt(count), page)
let news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
const paginated = API.Pagination(perPage, parseInt(count), page)
const news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
let articles = []
for (let i in news) {
let entry = news[i]
const articles = []
for (const i in news) {
const entry = news[i]
articles.push(await cleanArticle(entry))
}
@ -84,7 +84,7 @@ const News = {
return cleanArticle(article)
},
compose: async (user, body) => {
let article = {
const article = {
title: body.title,
content: body.content,
tags: body.tags || '',
@ -93,28 +93,28 @@ const News = {
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)
return result
},
edit: async (id, body) => {
let patch = {
const patch = {
content: body.content,
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.')
return {}
},
generateFeed: async () => {
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)
let cleanNews = []
const posts = await Models.News.query().orderBy('created_at', 'desc').limit(perPage)
const cleanNews = []
for (let i in posts) {
for (const i in posts) {
cleanNews.push(await cleanArticle(posts[i]))
}
@ -138,8 +138,8 @@ const News = {
}
})
for (let i in cleanNews) {
let post = cleanNews[i]
for (const i in cleanNews) {
const post = cleanNews[i]
feed.addItem({
title: post.title,

View File

@ -52,7 +52,7 @@ module.exports = wrap(async (req, res, next) => {
}
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) {
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 (!req.body || (typeof req.body['decision']) === 'undefined') {
if (!req.body || (typeof req.body.decision) === 'undefined') {
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')
} else {
console.debug('Decision check passed')

View File

@ -9,9 +9,9 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
}
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')
} else if (req.body['decision'] === '0') {
} else if (req.body.decision === '0') {
throw new error.AccessDenied('User denied access to the resource')
} else {
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'))
}
let token = await req.oauth2.model.accessToken.fetchByToken(req.body.token)
const token = await req.oauth2.model.accessToken.fetchByToken(req.body.token)
if (!token) {
return response.error(req, res, new error.InvalidRequest('Token does not exist'))
}
let ttl = req.oauth2.model.accessToken.getTTL(token)
let resObj = {
const ttl = req.oauth2.model.accessToken.getTTL(token)
const resObj = {
token_type: 'bearer',
token: token.token,
expires_in: Math.floor(ttl / 1000)

View File

@ -43,13 +43,13 @@ module.exports = wrap(async (req, res) => {
grantType = req.body.grant_type
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) {
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) {
return response.error(req, res, new error.UnauthorizedClient('Invalid client secret'))
}

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import error from '../../error'
module.exports = async (oauth2, client, username, password, scope) => {
let user = null
let resObj = {
const resObj = {
token_type: 'bearer'
}
@ -32,7 +32,7 @@ module.exports = async (oauth2, client, username, password, scope) => {
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) {
throw new error.InvalidClient('Wrong password')
}

View File

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

View File

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

View File

@ -32,7 +32,7 @@ const OAuthDB = {
created_at: new Date()
}
let res = await Models.OAuth2AccessToken.query().insert(obj)
const res = await Models.OAuth2AccessToken.query().insert(obj)
if (!res) return null
return res.token
@ -54,7 +54,7 @@ const OAuthDB = {
return (object.expires_at - Date.now())
},
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
@ -66,7 +66,7 @@ const OAuthDB = {
return client.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
@ -110,9 +110,9 @@ const OAuthDB = {
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) {
return false
}
@ -228,7 +228,7 @@ const OAuthDB = {
checkPassword: Users.User.Login.password,
fetchFromRequest: async (req) => {
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) {
delete req.session.user
@ -242,11 +242,11 @@ const OAuthDB = {
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
let correct = false
for (let i in authorized) {
for (const i in authorized) {
if (authorized[i].client_id === clientId) {
correct = authorized[i]
}
@ -271,7 +271,7 @@ const OAuthDB = {
scope = scope.join(' ')
}
let obj = {
const obj = {
scope,
user_id: userId,
client_id: clientId,

View File

@ -24,7 +24,7 @@ module.exports.error = function (req, res, err, redirectUri) {
}
if (redirectUri) {
let obj = {
const obj = {
error: err.code,
error_description: err.message
}
@ -36,7 +36,7 @@ module.exports.error = function (req, res, err, redirectUri) {
redirectUri += '?' + query.stringify(obj)
redirect(req, res, redirectUri)
} else {
data(req, res, err.status, {error: err.code, error_description: err.message})
data(req, res, err.status, { error: err.code, error_description: err.message })
}
}

View File

@ -14,7 +14,7 @@ const args = {
}
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' : ''))
for (let i = 0; i < workerCount; i++) {
@ -52,7 +52,7 @@ function watchFileTree () {
console.log('[WatchTask] %s changed, restarting workers', f)
if (workers.length) {
for (let i in workers) {
for (const i in workers) {
workers[i].send('stop')
}
}
@ -97,11 +97,11 @@ cluster.setupMaster({
})
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 + ')' : ''))
let index = workers.indexOf(worker)
const index = workers.indexOf(worker)
if (index !== -1) workers.splice(index, 1)
if (code === 0) return
@ -119,7 +119,7 @@ process.on('SIGUSR2', () => {
console.log('Received SIGUSR2. Restarting workers.')
if (workers.length) {
for (let i in workers) {
for (const i in workers) {
workers[i].send('stop')
}
}

View File

@ -4,7 +4,7 @@ import ensureLogin from '../../scripts/ensureLogin'
import wrap from '../../scripts/asyncRoute'
import API from '../api/admin'
import Emailer from '../api/emailer'
import {User} from '../api'
import { User } from '../api'
const router = express.Router()
const apiRouter = express.Router()
@ -12,7 +12,7 @@ const apiRouter = express.Router()
// Check for privilege required to access the admin panel
router.use(ensureLogin, wrap(async (req, res, next) => {
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
}
@ -31,10 +31,10 @@ router.use(ensureLogin, wrap(async (req, res, next) => {
router.get('/access', (req, res) => {
if (!req.session.accesstime || req.session.accesstime < Date.now()) {
return res.status(401).jsonp({error: 'Access expired'})
return res.status(401).jsonp({ error: 'Access expired' })
}
res.jsonp({access: req.session.accesstime - Date.now()})
res.jsonp({ access: req.session.accesstime - Date.now() })
})
// Post password to continue
@ -42,16 +42,16 @@ router.post('/', wrap(async (req, res, next) => {
if (!req.body.password) return next()
if (req.body.csrf !== req.session.csrf) {
req.flash('message', {error: true, text: 'Invalid session token'})
req.flash('message', { error: true, text: 'Invalid session token' })
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) {
req.session.accesstime = Date.now() + 600000 // 10 minutes
return res.redirect('/admin')
} else {
req.flash('message', {error: true, text: 'Invalid password'})
req.flash('message', { error: true, text: 'Invalid password' })
}
next()
@ -68,7 +68,7 @@ router.use(wrap(async (req, res, next) => {
delete req.session.accesstime
}
res.render('user/password', {post: '/admin'})
res.render('user/password', { post: '/admin' })
}))
/* =========
@ -108,12 +108,12 @@ apiRouter.get('/users', wrap(async (req, res) => {
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)
}))
apiRouter.get('/user/:id', wrap(async (req, res) => {
let id = parseInt(req.params.id)
const id = parseInt(req.params.id)
if (isNaN(id)) {
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) => {
let id = parseInt(req.body.user_id)
const id = parseInt(req.body.user_id)
if (isNaN(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) => {
let id = parseInt(req.body.user_id)
const id = parseInt(req.body.user_id)
if (isNaN(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) => {
let id = parseInt(req.body.user_id)
const id = parseInt(req.body.user_id)
if (isNaN(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) => {
let id = parseInt(req.body.user_id)
const id = parseInt(req.body.user_id)
if (isNaN(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) => {
let id = parseInt(req.body.user_id)
const id = parseInt(req.body.user_id)
if (isNaN(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) => {
if (!req.query.terms) throw new Error('Please specify search terms!')
let scopes = []
const 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()
if (availableScopes.indexOf(scq[i]) !== -1) {
@ -187,7 +187,7 @@ apiRouter.get('/search/users', wrap(async (req, res) => {
scopes.push('email')
}
let results = await API.searchUsers(req.query.terms, scopes)
const results = await API.searchUsers(req.query.terms, scopes)
res.jsonp(results)
}))
@ -202,17 +202,17 @@ apiRouter.get('/clients', wrap(async (req, res) => {
page = 1
}
let clients = await API.getAllClients(page)
const clients = await API.getAllClients(page)
res.jsonp(clients)
}))
apiRouter.get('/client/:id', wrap(async (req, res) => {
let id = parseInt(req.params.id)
const id = parseInt(req.params.id)
if (isNaN(id)) {
throw new Error('Invalid number')
}
let client = await API.getClient(id)
const client = await API.getClient(id)
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) => {
let id = parseInt(req.body.id)
const id = parseInt(req.body.id)
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) => {
let id = parseInt(req.body.id)
const id = parseInt(req.body.id)
if (isNaN(id)) {
throw new Error('Invalid client ID')
}
let client = await API.newSecret(id)
const client = await API.newSecret(id)
res.jsonp(client)
}))
apiRouter.post('/client/delete', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.id)
const id = parseInt(req.body.id)
if (isNaN(id)) {
throw new Error('Invalid client ID')
}
let client = await API.removeClient(id)
const client = await API.removeClient(id)
res.jsonp(client)
}))
@ -266,17 +266,17 @@ apiRouter.get('/bans', wrap(async (req, res) => {
page = 1
}
let bans = await API.getAllBans(page)
const bans = await API.getAllBans(page)
res.jsonp(bans)
}))
apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
let id = parseInt(req.body.id)
const id = parseInt(req.body.id)
if (isNaN(id)) {
throw new Error('Invalid number')
}
let ban = await API.removeBan(id)
const ban = await API.removeBan(id)
res.jsonp(ban)
}))
@ -284,7 +284,7 @@ apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
apiRouter.post('/ban', csrfVerify, wrap(async (req, res) => {
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)
}))
@ -299,14 +299,14 @@ apiRouter.post('/email', csrfVerify, wrap(async (req, res) => {
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)
}))
apiRouter.use((err, req, res, next) => {
console.error(err)
return res.status(400).jsonp({error: err.message})
return res.status(400).jsonp({ error: err.message })
})
router.use('/api', apiRouter)

View File

@ -9,18 +9,18 @@ import Image from '../api/image'
import News from '../api/news'
import API from '../api'
let router = express.Router()
let dev = process.env.NODE_ENV !== 'production'
const router = express.Router()
const dev = process.env.NODE_ENV !== 'production'
// Restrict API usage
let apiLimiter = new RateLimit({
const apiLimiter = new RateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes
max: 100,
delayMs: 0
})
// Restrict image uploads
let uploadLimiter = new RateLimit({
const uploadLimiter = new RateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 10,
delayMs: 0
@ -30,11 +30,11 @@ router.use(apiLimiter)
// Turn things like 'key1[key2]': 'value' into key1: {key2: 'value'} because facebook
function objectAssembler (insane) {
let object = {}
for (let key in insane) {
let value = insane[key]
const object = {}
for (const key in insane) {
const value = insane[key]
if (key.indexOf('[') !== -1) {
let subKey = key.match(/^([\w]+)\[(\w+)\]$/)
const subKey = key.match(/^([\w]+)\[(\w+)\]$/)
if (subKey[1] && subKey[2]) {
if (!object[subKey[1]]) {
object[subKey[1]] = {}
@ -63,7 +63,7 @@ function createSession (req, user) {
// Get either `uuid` or `id` from `:id` parameter
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)) {
return id
}
@ -77,17 +77,17 @@ function idParam (req) {
// Either give JSON or make a redirect
function JsonData (req, res, error, redirect = '/') {
res.jsonp({error: error, redirect: redirect})
res.jsonp({ error: error, redirect: redirect })
}
// Common middleware for all external account unlinks
function removeAuthMiddleware (identifier) {
return wrap(async (req, res) => {
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) {
req.flash('message', {error: true, text: 'Unable to unlink social media account'})
req.flash('message', { error: true, text: 'Unable to unlink social media account' })
}
res.redirect('/user/manage')
@ -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()
// Fix up the retarded object Facebook sends us
let sane = objectAssembler(req.body)
const sane = objectAssembler(req.body)
if (!sane || !sane.authResponse) {
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) {
return JsonData(req, res, 'You are banned.')
@ -119,7 +119,7 @@ router.post('/external/facebook/callback', wrap(async (req, res, next) => {
// Create session
if (!req.session.user) {
let user = response.user
const user = response.user
createSession(req, user)
}
@ -134,10 +134,10 @@ router.get('/external/facebook/remove', removeAuthMiddleware('facebook'))
*/
router.get('/external/twitter/login', wrap(async (req, res) => {
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) {
return res.jsonp({error: tokens.error})
return res.jsonp({ error: tokens.error })
}
req.session.twitter_auth = tokens
@ -148,38 +148,38 @@ router.get('/external/twitter/login', 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 (!req.session.twitter_auth) return res.redirect('/login')
let ta = req.session.twitter_auth
let uri = '/login'
const ta = req.session.twitter_auth
const uri = '/login'
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)
}
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
if (accessTokens.error) {
req.flash('message', {error: true, text: 'Couldn\'t get an access token'})
req.flash('message', { error: true, text: 'Couldn\'t get an access token' })
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) {
return res.render('user/banned', {bans: response.banned, ipban: response.ip})
return res.render('user/banned', { bans: response.banned, ipban: response.ip })
}
if (response.error) {
req.flash('message', {error: true, text: response.error})
req.flash('message', { error: true, text: response.error })
return res.redirect(uri)
}
if (!req.session.user) {
let user = response.user
const user = response.user
createSession(req, user)
}
res.render('redirect', {url: uri})
res.render('redirect', { url: uri })
}))
router.get('/external/twitter/remove', removeAuthMiddleware('twitter'))
@ -191,7 +191,7 @@ router.get('/external/twitter/remove', removeAuthMiddleware('twitter'))
router.get('/external/discord/login', wrap(async (req, res) => {
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)
}))
@ -199,44 +199,44 @@ router.get('/external/discord/login', 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')
let code = req.query.code
let state = req.query.state
let uri = '/login'
const code = req.query.code
const state = req.query.state
const uri = '/login'
if (!code) {
req.flash('message', {error: true, text: 'No authorization.'})
req.flash('message', { error: true, text: 'No authorization.' })
return res.redirect(uri)
}
if (!state || state !== APIExtern.Common.stateGenerator(req)) {
req.flash('message', {error: true, text: 'Request got intercepted, try again.'})
req.flash('message', { error: true, text: 'Request got intercepted, try again.' })
return res.redirect(uri)
}
delete req.session.discord_auth
let accessToken = await APIExtern.Discord.getAccessToken(code)
const accessToken = await APIExtern.Discord.getAccessToken(code)
if (accessToken.error) {
req.flash('message', {error: true, text: accessToken.error})
req.flash('message', { error: true, text: accessToken.error })
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) {
return res.render('user/banned', {bans: response.banned, ipban: response.ip})
return res.render('user/banned', { bans: response.banned, ipban: response.ip })
}
if (response.error) {
req.flash('message', {error: true, text: response.error})
req.flash('message', { error: true, text: response.error })
return res.redirect(uri)
}
if (!req.session.user) {
let user = response.user
const user = response.user
createSession(req, user)
}
res.render('redirect', {url: uri})
res.render('redirect', { url: uri })
}))
router.get('/external/discord/remove', removeAuthMiddleware('discord'))
@ -258,7 +258,7 @@ router.post('/external/google/callback', wrap(async (req, res) => {
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) {
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) {
let user = response.user
const user = response.user
createSession(req, user)
}
@ -291,11 +291,11 @@ router.get('/news', (req, res, next) => {
// Get a page of articles
router.get('/news/all/:page', wrap(async (req, res) => {
if (!req.params.page || isNaN(parseInt(req.params.page))) {
return res.status(400).jsonp({error: 'Invalid page number.'})
return res.status(400).jsonp({ error: 'Invalid page number.' })
}
let page = parseInt(req.params.page)
let articles = await News.listNews(page)
const page = parseInt(req.params.page)
const articles = await News.listNews(page)
res.jsonp(articles)
}))
@ -306,22 +306,22 @@ router.get('/news/all/', (req, res) => {
})
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 (!id || isNaN(id)) {
return res.status(400).jsonp({error: 'Invalid ID number.'})
return res.status(400).jsonp({ error: 'Invalid ID number.' })
}
if (!req.body.content) {
return res.status(400).jsonp({error: 'Content is required.'})
return res.status(400).jsonp({ error: 'Content is required.' })
}
try {
await News.edit(id, req.body)
} catch (e) {
return res.status(400).jsonp({error: e.message})
return res.status(400).jsonp({ error: e.message })
}
res.status(204).end()
@ -330,18 +330,18 @@ router.post('/news/edit/:id', wrap(async (req, res, next) => {
// Fetch article
router.get('/news/:id', wrap(async (req, res) => {
if (!req.params.id || isNaN(parseInt(req.params.id))) {
return res.status(400).jsonp({error: 'Invalid ID number.'})
return res.status(400).jsonp({ error: 'Invalid ID number.' })
}
let id = parseInt(req.params.id)
let article = await News.article(id)
const id = parseInt(req.params.id)
const article = await News.article(id)
res.jsonp(article)
}))
// Preview endpoint
router.get('/news', wrap(async (req, res) => {
let articles = await News.preview()
const articles = await News.preview()
res.jsonp(articles)
}))
@ -352,11 +352,11 @@ router.get('/news', wrap(async (req, res) => {
*/
// Promisify multiparty form parser
async function promiseForm (req) {
let form = new multiparty.Form()
const form = new multiparty.Form()
return new Promise(function (resolve, reject) {
form.parse(req, async (err, fields, files) => {
if (err) return reject(err)
resolve({fields: fields, files: files})
resolve({ fields: fields, files: files })
})
})
}
@ -364,23 +364,23 @@ async function promiseForm (req) {
// Upload avatar image
router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => {
if (!req.session.user) return next()
let data = await promiseForm(req)
const data = await promiseForm(req)
let avatarFile
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)
} catch (e) {
return res.status(400).jsonp({error: e.message})
return res.status(400).jsonp({ error: e.message })
}
if (avatarFile) {
req.session.user.avatar_file = avatarFile
}
req.flash('message', {error: false, text: 'Success!'})
req.flash('message', { error: false, text: 'Success!' })
res.status(200).jsonp({})
}))
@ -391,13 +391,13 @@ router.post('/avatar/remove', wrap(async (req, res, next) => {
await API.User.removeAvatar(req.session.user)
req.session.user.avatar_file = null
res.status(200).jsonp({done: true})
res.status(200).jsonp({ done: true })
}))
// Get latest avatar of logged in user
router.get('/avatar', wrap(async (req, res, next) => {
if (!req.session.user) return next()
let user = req.session.user
const user = req.session.user
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
router.get('/avatar/:id', wrap(async (req, res, next) => {
let id = idParam(req)
const id = idParam(req)
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()
@ -420,7 +420,7 @@ router.get('/avatar/:id', wrap(async (req, res, next) => {
router.get('/avatar/gravatar', (req, res, 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.end(Image.gravatarURL(email))
@ -428,17 +428,17 @@ router.get('/avatar/gravatar', (req, res, next) => {
router.post('/avatar/gravatar', wrap(async (req, res, next) => {
if (!req.session.user) return next()
let user = req.session.user
const user = req.session.user
try {
let gravURL = await Image.downloadImage(Image.gravatarURL(user.email), 'GRAV-' + user.username)
let file = await API.User.changeAvatar(user, gravURL)
const gravURL = await Image.downloadImage(Image.gravatarURL(user.email), 'GRAV-' + user.username)
const file = await API.User.changeAvatar(user, gravURL)
req.session.user.avatar_file = file
req.flash('message', {error: false, text: 'Success!'})
req.flash('message', { error: false, text: 'Success!' })
} catch (e) {
req.flash('message', {error: true, text: 'Failed to use gravatar avatar.'})
req.flash('message', { error: true, text: 'Failed to use gravatar avatar.' })
}
res.jsonp({})
@ -458,7 +458,7 @@ router.use('/avatar', (req, res) => {
router.get('/oauth2/authorized-clients', wrap(async (req, res, 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()
res.jsonp(list)
@ -468,11 +468,11 @@ router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) => {
if (!req.session.user) return next()
let clientId = parseInt(req.body.client_id)
if (isNaN(clientId)) return res.status(400).jsonp({error: 'Missing Client ID parameter'})
const clientId = parseInt(req.body.client_id)
if (isNaN(clientId)) return res.status(400).jsonp({ error: 'Missing Client ID parameter' })
let done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId)
if (!done) return res.status(400).jsonp({error: 'Failed to remove client authorization'})
const done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId)
if (!done) return res.status(400).jsonp({ error: 'Failed to remove client authorization' })
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) => {
let content = req.body
const content = req.body
if (content && content.payment_status && content.payment_status === 'Completed') {
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) => {
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)
}))
@ -509,22 +509,22 @@ router.get('/donations', wrap(async (req, res, next) => {
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)
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)
}))
// 404
router.use((req, res) => {
res.status(404).jsonp({error: 'Not found'})
res.status(404).jsonp({ error: 'Not found' })
})
router.use((err, req, res, next) => {
console.error(err)
res.jsonp({error: 'Internal server error.'})
res.jsonp({ error: 'Internal server error.' })
})
module.exports = router

View File

@ -15,10 +15,10 @@ import apiRouter from './api'
import oauthRouter from './oauth2'
import adminRouter from './admin'
let router = express.Router()
const router = express.Router()
// Restrict account creation
let accountLimiter = new RateLimit({
const accountLimiter = new RateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 10,
delayMs: 0,
@ -70,7 +70,7 @@ router.use(wrap(async (req, res, next) => {
console.debug('User session update')
// 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) {
delete req.session.user
@ -78,11 +78,11 @@ router.use(wrap(async (req, res, next) => {
}
// Update user session
let udata = await API.User.get(req.session.user)
const udata = await API.User.get(req.session.user)
setSession(req, udata)
// Update IP address
await API.User.update(udata, {ip_address: req.realIP})
await API.User.update(udata, { ip_address: req.realIP })
}
}
@ -103,7 +103,7 @@ router.get('/', (req, res) => {
// Add social media login buttons
function extraButtons (recheck) {
let et = config.external
const et = config.external
return function (req, res, next) {
if (!et) return next()
res.locals.auth = {
@ -149,22 +149,22 @@ function formKeep (req, res, next) {
router.get('/login/reset', extraButtons(false), (req, res) => {
if (req.session.user) return redirectLogin(req, res)
res.render('user/reset_password', {sent: req.query.success != null})
res.render('user/reset_password', { sent: req.query.success != null })
})
// Password reset endpoint (emailed link)
router.get('/reset/:token', wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login')
let token = req.params.token
let success = await API.User.Reset.resetToken(token)
const token = req.params.token
const success = await API.User.Reset.resetToken(token)
if (!success) {
req.flash('message', {error: true, text: 'Invalid or expired reset token.'})
req.flash('message', { error: true, text: 'Invalid or expired reset token.' })
res.redirect('/login')
return
}
res.render('user/password_new', {token: true})
res.render('user/password_new', { token: true })
}))
router.get('/login', extraButtons(false), (req, res) => {
@ -190,13 +190,13 @@ router.get('/register', extraButtons(true), formKeep, (req, res) => {
// User activation endpoint (emailed link)
router.get('/activate/:token', wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login')
let token = req.params.token
let success = await API.User.Login.activationToken(token)
const token = req.params.token
const success = await API.User.Login.activationToken(token)
if (!success) {
req.flash('message', {error: true, text: 'Invalid or expired activation token.'})
req.flash('message', { error: true, text: 'Invalid or expired activation token.' })
} else {
req.flash('message', {error: false, text: 'Your account has been activated! You may now log in.'})
req.flash('message', { error: false, text: 'Your account has been activated! You may now log in.' })
}
res.redirect('/login')
@ -204,10 +204,10 @@ router.get('/activate/:token', wrap(async (req, res) => {
// View for enabling Two-Factor Authentication
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('/')
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('/')
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
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('/')
res.render('user/password')
@ -229,13 +229,13 @@ router.get('/login/verify', (req, res) => {
// User settings page
router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
let totpEnabled = false
let socialStatus = await API.User.socialStatus(req.session.user)
const socialStatus = await API.User.socialStatus(req.session.user)
if (socialStatus.password) {
totpEnabled = await API.User.Login.totpTokenRequired(req.session.user)
}
let et = config.external
const et = config.external
if (et) {
res.locals.auth = {}
// Decide whether we need a disconnect or a log in with button for social account logins
@ -272,28 +272,28 @@ router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
}
}
res.render('user/settings', {totp: totpEnabled, password: socialStatus.password})
res.render('user/settings', { totp: totpEnabled, password: socialStatus.password })
}))
// Change password
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 })
}))
// Change email
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
let obfuscated = req.session.user.email
if (obfuscated) {
let split = obfuscated.split('@')
let rep = split[0].charAt(0) + '***' + split[0].charAt(split[0].length - 1)
const split = obfuscated.split('@')
const rep = split[0].charAt(0) + '***' + split[0].charAt(split[0].length - 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 })
}))
router.get('/donate', wrap(async (req, res, next) => {
@ -311,14 +311,14 @@ router.get('/donate', wrap(async (req, res, next) => {
// Used to display errors on forms and save data
function formError (req, res, error, redirect) {
// 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) {
delete req.body[key]
}
}
req.flash('formkeep', req.body || {})
req.flash('message', {error: true, text: error})
req.flash('message', { error: true, text: error })
res.redirect(redirect || req.originalUrl)
}
@ -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.')
}
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) {
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.')
}
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) {
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.')
}
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) {
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
setSession(req, user)
@ -404,12 +404,12 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
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.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 (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.')
// 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) {
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
let totpRequired = await API.User.Login.totpTokenRequired(user)
const totpRequired = await API.User.Login.totpTokenRequired(user)
if (totpRequired) {
req.session.totp_check = user.id
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.')
}
let email = req.body.email
let validEmail = await API.User.Register.validateEmail(email)
const email = req.body.email
const validEmail = await API.User.Register.validateEmail(email)
if (!validEmail) {
return formError(req, res, 'You need to enter a valid email address.')
}
@ -462,7 +462,7 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
try {
await API.User.Reset.reset(email, false)
req.flash('message', {error: false, text: 'We\'ve sent a link to your email address. Please check spam folders, too!'})
req.flash('message', { error: false, text: 'We\'ve sent a link to your email address. Please check spam folders, too!' })
res.redirect('/login/reset?success=true')
} catch (e) {
return formError(req, res, e.message)
@ -472,23 +472,23 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
// Password reset endpoint (emailed link)
router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login')
let token = req.params.token
let user = await API.User.Reset.resetToken(token)
const token = req.params.token
const user = await API.User.Reset.resetToken(token)
if (!user) {
req.flash('message', {error: true, text: 'Invalid or expired reset token.'})
req.flash('message', { error: true, text: 'Invalid or expired reset token.' })
res.redirect('/login')
return
}
// 4th Check: Password length
let password = req.body.password
const password = req.body.password
if (!password || password.length < 8) {
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
}
// 5th Check: Password match
let passwordAgain = req.body.password_repeat
const passwordAgain = req.body.password_repeat
if (!passwordAgain || password !== passwordAgain) {
return formError(req, res, 'Passwords do not match!')
}
@ -498,7 +498,7 @@ router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
console.warn('[SECURITY AUDIT] User \'%s\' password has been changed from %s', user.username, req.realIP)
req.flash('message', {error: false, text: 'Your password has been changed successfully. You may now log in!'})
req.flash('message', { error: false, text: 'Your password has been changed successfully. You may now log in!' })
res.redirect('/login')
} catch (e) {
return formError(req, res, e.message)
@ -514,37 +514,37 @@ router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, n
}
// Ban check
let banStatus = await API.User.getBanStatus(req.realIP, true)
const banStatus = await API.User.getBanStatus(req.realIP, true)
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
let username = req.body.username
const username = req.body.username
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!')
}
// 2nd Check: Display Name
let displayName = req.body.display_name
const displayName = req.body.display_name
if (!displayName || !displayName.match(/^([^\\`]{3,32})$/i)) {
return formError(req, res, 'Invalid display name!')
}
// 3rd Check: Email Address
let email = req.body.email
const email = req.body.email
if (!email || !API.User.Register.validateEmail(email)) {
return formError(req, res, 'Invalid email address!')
}
// 4th Check: Password length
let password = req.body.password
const password = req.body.password
if (!password || password.length < 8) {
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
}
// 5th Check: Password match
let passwordAgain = req.body.password_repeat
const passwordAgain = req.body.password_repeat
if (!passwordAgain || password !== passwordAgain) {
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
let hash = await API.User.Register.hashPassword(password)
const hash = await API.User.Register.hashPassword(password)
let newUser
// Attempt to create the user
@ -592,7 +592,7 @@ router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, n
registerMessage += ' Please check your inbox for an activation link. Also, make sure to look into spam folders.'
}
req.flash('message', {error: false, text: registerMessage})
req.flash('message', { error: false, text: registerMessage })
res.redirect('/login')
}))
@ -626,22 +626,22 @@ router.post('/user/manage', csrfValidation, wrap(async (req, res, next) => {
req.session.user.display_name = displayName
req.flash('message', {error: false, text: 'Settings changed successfully.'})
req.flash('message', { error: false, text: 'Settings changed successfully.' })
res.redirect('/user/manage')
}))
// Change user password
router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
if (!req.session.user) return next()
let user = req.session.user
let socialStatus = await API.User.socialStatus(user)
const user = req.session.user
const socialStatus = await API.User.socialStatus(user)
if (!req.body.password_old && socialStatus.password) {
return formError(req, res, 'Please enter your current 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) {
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!')
}
let passwordAgain = req.body.password_repeat
const passwordAgain = req.body.password_repeat
if (!passwordAgain || password !== passwordAgain) {
return formError(req, res, 'The passwords do not match!')
}
@ -676,7 +676,7 @@ router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async
})
}
req.flash('message', {error: false, text: 'Password changed successfully.'})
req.flash('message', { error: false, text: 'Password changed successfully.' })
return res.redirect('/user/manage')
}))
@ -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) => {
if (!req.session.user) return next()
let user = await API.User.get(req.session.user)
let email = req.body.email
let newEmail = req.body.email_new
let password = req.body.password
const user = await API.User.get(req.session.user)
const email = req.body.email
const newEmail = req.body.email_new
const password = req.body.password
if (!newEmail || (!email && user.email !== '')) {
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.')
}
let passwordMatch = await API.User.Login.password(user, password)
const passwordMatch = await API.User.Login.password(user, password)
if (!passwordMatch) {
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) {
return formError(req, res, 'Invalid email address.')
}
let emailTaken = await API.User.get(newEmail)
const emailTaken = await API.User.get(newEmail)
if (emailTaken) {
return formError(req, res, 'This email is already taken.')
}
@ -730,7 +730,7 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
req.session.user.email = newEmail
req.flash('message', {error: false, text: 'Email changed successfully.'})
req.flash('message', { error: false, text: 'Email changed successfully.' })
return res.redirect('/user/manage')
}))
@ -743,15 +743,15 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
// Serve a document form the documents directory, cache it.
const docsDir = path.join(__dirname, '../../documents')
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)) {
return next()
}
fs.readFile(doc, {encoding: 'utf8'}, (err, contents) => {
fs.readFile(doc, { encoding: 'utf8' }, (err, contents) => {
if (err) return next(err)
res.header('Cache-Control', 'max-age=' + 7 * 24 * 60 * 60 * 1000) // 1 week
res.render('document', {doc: contents})
res.render('document', { doc: contents })
})
}))
@ -788,14 +788,14 @@ router.post('/news/compose', newsPrivilege, csrfValidation, wrap(async (req, res
// Serve news
router.get('/news/:id?-*', wrap(async (req, res) => {
let id = parseInt(req.params.id)
const id = parseInt(req.params.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) {
return res.status(404).render('article', {article: null})
return res.status(404).render('article', { article: null })
}
let editing = false
@ -803,7 +803,7 @@ router.get('/news/:id?-*', wrap(async (req, res) => {
editing = true
}
res.render('news/article', {article: article, editing: editing})
res.render('news/article', { article: article, editing: editing })
}))
router.get('/news/', wrap(async (req, res) => {
@ -812,20 +812,20 @@ router.get('/news/', wrap(async (req, res) => {
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) => {
let feed = await News.generateFeed()
const feed = await News.generateFeed()
res.set('Content-Type', 'application/atom+xml')
res.send(feed.atom1())
}))
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.send(feed.json1())

View File

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

View File

@ -12,8 +12,8 @@ import flash from '../scripts/flash'
import config from '../scripts/load-config'
import email from './api/emailer'
let app = express()
let SessionStore = connectSession(session)
const app = express()
const SessionStore = connectSession(session)
app.enable('trust proxy', 1)
@ -68,7 +68,7 @@ app.use((req, res, next) => {
app.use(favicon(path.join(__dirname, '..', 'static', 'image', 'icynet.ico')))
module.exports = (args) => {
app.set('view options', {layout: false})
app.set('view options', { layout: false })
app.set('view engine', 'pug')
app.set('views', path.join(__dirname, '../views'))
@ -80,7 +80,7 @@ module.exports = (args) => {
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.
// It is also a good idea to use nginx to serve these directories in order to save on computing power

View File

@ -4,7 +4,7 @@ const path = require('path')
const util = require('util')
require('@babel/register')({
plugins: [ '@babel/plugin-transform-modules-commonjs' ]
plugins: ['@babel/plugin-transform-modules-commonjs']
})
process.once('message', (args) => {

View File

@ -34,5 +34,5 @@ block body
input#custominfo(type="hidden", name="custom", value="")
.buttoncont
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

View File

@ -18,7 +18,7 @@ block body
input(type="hidden", name="csrf", value=csrf)
.form-group
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
label(for="password") 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)
.form-group
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

View File

@ -16,7 +16,7 @@ block body
input(type="hidden", name="csrf", value=csrf)
.form-group
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.
.form-group
label(for="display_name") Display Name

View File

@ -18,5 +18,5 @@ block body
input(type="hidden", name="csrf", value=csrf)
.form-group
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

View File

@ -17,5 +17,5 @@ block body
input(type="hidden", name="csrf", value=csrf)
.form-group
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

View File

@ -25,7 +25,7 @@ block body
form#totpForm(method="POST", action="")
input(type="hidden", name="csrf", value=csrf)
.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")
button.btn.btn-primary(type="submit") Enable Now
aside.col

View File

@ -12,7 +12,7 @@ module.exports = {
},
resolve: {
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: {

View File

@ -12,6 +12,6 @@ module.exports = merge(common, {
],
optimization: {
minimize: true,
minimizer: [ new TerserPlugin() ]
minimizer: [new TerserPlugin()]
}
})