Maintenance commit.
This commit is contained in:
parent
f608316e89
commit
787cc6fc73
@ -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
7242
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
@ -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": {
|
||||
|
@ -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 {
|
||||
|
@ -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 () {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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`,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 || ''
|
||||
})
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Model} from '../../scripts/load-database'
|
||||
import { Model } from '../../scripts/load-database'
|
||||
|
||||
class User extends Model {
|
||||
static get tableName () {
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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')
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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'))
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import error from '../../error'
|
||||
|
||||
module.exports = async (oauth2, client, providedCode, redirectUri) => {
|
||||
let respObj = {
|
||||
const respObj = {
|
||||
token_type: 'bearer'
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import error from '../../error'
|
||||
module.exports = async (oauth2, client, wantScope) => {
|
||||
let scope = null
|
||||
|
||||
let resObj = {
|
||||
const resObj = {
|
||||
token_type: 'bearer'
|
||||
}
|
||||
|
||||
|
@ -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')
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ module.exports = async (oauth2, client, pRefreshToken, scope) => {
|
||||
let refreshToken = null
|
||||
let accessToken = null
|
||||
|
||||
let resObj = {
|
||||
const resObj = {
|
||||
token_type: 'bearer'
|
||||
}
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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,
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
| Donate
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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: {
|
||||
|
@ -12,6 +12,6 @@ module.exports = merge(common, {
|
||||
],
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [ new TerserPlugin() ]
|
||||
minimizer: [new TerserPlugin()]
|
||||
}
|
||||
})
|
||||
|
Reference in New Issue
Block a user