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')({
|
require('@babel/register')({
|
||||||
plugins: [ '@babel/plugin-transform-modules-commonjs' ]
|
plugins: ['@babel/plugin-transform-modules-commonjs']
|
||||||
})
|
})
|
||||||
|
|
||||||
require(path.join(__dirname, 'server'))
|
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",
|
"homepage": "https://icynet.eu",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.8.3",
|
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
|
||||||
"@babel/register": "^7.8.3",
|
"@babel/register": "^7.10.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"bluebird": "^3.7.2",
|
"bluebird": "^3.7.2",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"connect-redis": "^3.4.2",
|
"connect-redis": "^4.0.4",
|
||||||
"connect-session-knex": "^1.5.0",
|
"connect-session-knex": "^1.6.0",
|
||||||
"email-templates": "^2.7.1",
|
"email-templates": "^7.0.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-rate-limit": "^2.14.2",
|
"express-rate-limit": "^5.1.3",
|
||||||
"express-session": "^1.17.0",
|
"express-session": "^1.17.1",
|
||||||
"feed": "^1.1.1",
|
"feed": "^4.2.0",
|
||||||
"fs-extra": "^4.0.3",
|
"fs-extra": "^9.0.0",
|
||||||
"gm": "^1.23.1",
|
"gm": "^1.23.1",
|
||||||
"knex": "^0.14.6",
|
"knex": "^0.21.1",
|
||||||
"multiparty": "^4.2.1",
|
"multiparty": "^4.2.1",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"nodemailer": "^4.7.0",
|
"nodemailer": "^6.4.8",
|
||||||
"notp": "^2.0.3",
|
"notp": "^2.0.3",
|
||||||
"oauth-libre": "^0.9.17",
|
"oauth-libre": "^0.9.17",
|
||||||
"objection": "^0.8.9",
|
"objection": "^2.1.5",
|
||||||
"redis": "^2.8.0",
|
"redis": "^3.0.2",
|
||||||
"serve-favicon": "^2.5.0",
|
"serve-favicon": "^2.5.0",
|
||||||
"stylus": "^0.54.7",
|
"stylus": "^0.54.7",
|
||||||
"thirty-two": "^1.0.2",
|
"thirty-two": "^1.0.2",
|
||||||
"toml": "^2.3.6",
|
"toml": "^3.0.0",
|
||||||
"uuid": "^3.4.0",
|
"uuid": "^8.1.0",
|
||||||
"vue": "^2.6.11"
|
"vue": "^2.6.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.8.3",
|
"@babel/core": "^7.10.1",
|
||||||
"@babel/preset-env": "^7.8.3",
|
"@babel/preset-env": "^7.10.1",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.1.0",
|
||||||
"bootstrap": "^4.4.1",
|
"bootstrap": "^4.5.0",
|
||||||
"concurrently": "^5.1.0",
|
"concurrently": "^5.2.0",
|
||||||
"eslint-plugin-import": "^2.20.0",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"jquery": "^3.4.1",
|
"jquery": "^3.5.1",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.10.0",
|
||||||
"mustache": "^2.3.2",
|
"mustache": "^4.0.1",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"pug": "^2.0.4",
|
"pug": "^3.0.0",
|
||||||
"pug-plain-loader": "^1.0.0",
|
"pug-plain-loader": "^1.0.0",
|
||||||
"standard": "^10.0.3",
|
"standard": "^14.3.4",
|
||||||
"terser-webpack-plugin": "^2.3.3",
|
"terser-webpack-plugin": "^3.0.2",
|
||||||
"vue-clickaway": "^2.2.2",
|
"vue-clickaway": "^2.2.2",
|
||||||
"vue-loader": "^15.8.3",
|
"vue-loader": "^15.9.2",
|
||||||
"vue-resource": "^1.5.1",
|
"vue-resource": "^1.5.1",
|
||||||
"vue-router": "^3.1.5",
|
"vue-router": "^3.3.1",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"watch": "^1.0.2",
|
"watch": "^1.0.2",
|
||||||
"webpack": "^4.41.5",
|
"webpack": "^4.43.0",
|
||||||
"webpack-cli": "^3.3.10",
|
"webpack-cli": "^3.3.11",
|
||||||
"webpack-merge": "^4.2.2"
|
"webpack-merge": "^4.2.2"
|
||||||
},
|
},
|
||||||
"standard": {
|
"standard": {
|
||||||
|
@ -8,7 +8,7 @@ const format = util.format
|
|||||||
|
|
||||||
module.exports = function (options) {
|
module.exports = function (options) {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
let safe = (options.unsafe === undefined) ? true : !options.unsafe
|
const safe = (options.unsafe === undefined) ? true : !options.unsafe
|
||||||
|
|
||||||
return function (req, res, next) {
|
return function (req, res, next) {
|
||||||
if (req.flash && safe) { return next() }
|
if (req.flash && safe) { return next() }
|
||||||
@ -20,10 +20,10 @@ module.exports = function (options) {
|
|||||||
function _flash (type, msg) {
|
function _flash (type, msg) {
|
||||||
if (this.session === undefined) throw Error('req.flash() requires sessions')
|
if (this.session === undefined) throw Error('req.flash() requires sessions')
|
||||||
|
|
||||||
let msgs = this.session.flash = this.session.flash || {}
|
const msgs = this.session.flash = this.session.flash || {}
|
||||||
if (type && msg) {
|
if (type && msg) {
|
||||||
if (arguments.length > 2 && format) {
|
if (arguments.length > 2 && format) {
|
||||||
let args = Array.prototype.slice.call(arguments, 1)
|
const args = Array.prototype.slice.call(arguments, 1)
|
||||||
msg = format.apply(undefined, args)
|
msg = format.apply(undefined, args)
|
||||||
} else if (Array.isArray(msg)) {
|
} else if (Array.isArray(msg)) {
|
||||||
msg.forEach((val) => {
|
msg.forEach((val) => {
|
||||||
@ -33,7 +33,7 @@ function _flash (type, msg) {
|
|||||||
}
|
}
|
||||||
return (msgs[type] = msgs[type] || []).push(msg)
|
return (msgs[type] = msgs[type] || []).push(msg)
|
||||||
} else if (type) {
|
} else if (type) {
|
||||||
let arr = msgs[type]
|
const arr = msgs[type]
|
||||||
delete msgs[type]
|
delete msgs[type]
|
||||||
return arr || []
|
return arr || []
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import url from 'url'
|
import { URL } from 'url'
|
||||||
import qs from 'querystring'
|
import qs from 'querystring'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
function HTTP_GET (link, headers = {}, lback) {
|
function HTTP_GET (link, headers = {}, lback) {
|
||||||
if (lback && lback >= 4) throw new Error('infinite loop!') // Prevent infinite loop requests
|
if (lback && lback >= 4) throw new Error('infinite loop!') // Prevent infinite loop requests
|
||||||
let parsed = url.parse(link)
|
const parsed = new URL(link)
|
||||||
let opts = {
|
const opts = {
|
||||||
host: parsed.hostname,
|
host: parsed.hostname,
|
||||||
port: parsed.port,
|
port: parsed.port,
|
||||||
path: parsed.path,
|
path: parsed.path,
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'Icy Network Back-end (icynet.eu)',
|
'User-Agent': 'Icy Network Back-end (icynet.eu)',
|
||||||
'Accept': '*/*',
|
Accept: '*/*',
|
||||||
'Accept-Language': 'en-GB,enq=0.5'
|
'Accept-Language': 'en-GB,enq=0.5'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,9 +22,9 @@ function HTTP_GET (link, headers = {}, lback) {
|
|||||||
|
|
||||||
let reqTimeOut
|
let reqTimeOut
|
||||||
|
|
||||||
let httpModule = parsed.protocol === 'https:' ? require('https') : require('http')
|
const httpModule = parsed.protocol === 'https:' ? require('https') : require('http')
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let req = httpModule.get(opts, (res) => {
|
const req = httpModule.get(opts, (res) => {
|
||||||
if (res.statusCode === 302 || res.statusCode === 301) {
|
if (res.statusCode === 302 || res.statusCode === 301) {
|
||||||
if (!lback) {
|
if (!lback) {
|
||||||
lback = 1
|
lback = 1
|
||||||
@ -61,10 +61,10 @@ function HTTP_GET (link, headers = {}, lback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function HTTP_POST (link, headers = {}, data) {
|
function HTTP_POST (link, headers = {}, data) {
|
||||||
let parsed = url.parse(link)
|
const parsed = new URL(link)
|
||||||
let postData = qs.stringify(data)
|
let postData = qs.stringify(data)
|
||||||
|
|
||||||
let opts = {
|
const opts = {
|
||||||
host: parsed.host,
|
host: parsed.host,
|
||||||
port: parsed.port,
|
port: parsed.port,
|
||||||
path: parsed.path,
|
path: parsed.path,
|
||||||
@ -85,8 +85,8 @@ function HTTP_POST (link, headers = {}, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let httpModule = parsed.protocol === 'https:' ? require('https') : require('http')
|
const httpModule = parsed.protocol === 'https:' ? require('https') : require('http')
|
||||||
let req = httpModule.request(opts, (res) => {
|
const req = httpModule.request(opts, (res) => {
|
||||||
res.setEncoding('utf8')
|
res.setEncoding('utf8')
|
||||||
let data = ''
|
let data = ''
|
||||||
|
|
||||||
@ -108,8 +108,8 @@ function HTTP_POST (link, headers = {}, data) {
|
|||||||
|
|
||||||
async function Download (url, dest) {
|
async function Download (url, dest) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let file = fs.createWriteStream(dest)
|
const file = fs.createWriteStream(dest)
|
||||||
let protocol = url.indexOf('https:') === 0 ? require('https') : require('http')
|
const protocol = url.indexOf('https:') === 0 ? require('https') : require('http')
|
||||||
protocol.get(url, function (response) {
|
protocol.get(url, function (response) {
|
||||||
response.pipe(file)
|
response.pipe(file)
|
||||||
file.on('finish', function () {
|
file.on('finish', function () {
|
||||||
|
@ -3,8 +3,8 @@ const knex = require('knex')
|
|||||||
const objection = require('objection')
|
const objection = require('objection')
|
||||||
const knexfile = require(path.join(__dirname, '../knexfile'))
|
const knexfile = require(path.join(__dirname, '../knexfile'))
|
||||||
|
|
||||||
let knexDB = knex(knexfile)
|
const knexDB = knex(knexfile)
|
||||||
let objectionModel = objection.Model
|
const objectionModel = objection.Model
|
||||||
|
|
||||||
objectionModel.knex(knexDB)
|
objectionModel.knex(knexDB)
|
||||||
|
|
||||||
|
@ -20,11 +20,12 @@ function dateFormat (date) {
|
|||||||
|
|
||||||
// Console.log/error/warn "middleware" - add timestamp and write to file
|
// Console.log/error/warn "middleware" - add timestamp and write to file
|
||||||
function stampAndWrite (fnc, color, prfx, message) {
|
function stampAndWrite (fnc, color, prfx, message) {
|
||||||
let prefix = '[' + prfx + '] [' + dateFormat(new Date()) + '] '
|
const prefix = '[' + prfx + '] [' + dateFormat(new Date()) + '] '
|
||||||
message = color + prefix + message + ((color && color !== '') ? '\x1b[0m' : '')
|
message = color + prefix + message + ((color && color !== '') ? '\x1b[0m' : '')
|
||||||
message = message.replace(/\\u001b/g, '\x1b')
|
message = message.replace(/\\u001b/g, '\x1b')
|
||||||
|
|
||||||
if (lfs) {
|
if (lfs) {
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
lfs.write(message.replace(/(\u001b\[\d\d?m)/g, '') + '\n')
|
lfs.write(message.replace(/(\u001b\[\d\d?m)/g, '') + '\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,24 +35,24 @@ function stampAndWrite (fnc, color, prfx, message) {
|
|||||||
// Reassign logger functions and send them to the "middleware"
|
// Reassign logger functions and send them to the "middleware"
|
||||||
const realConsoleLog = console.log
|
const realConsoleLog = console.log
|
||||||
console.log = function () {
|
console.log = function () {
|
||||||
let message = util.format.apply(null, arguments)
|
const message = util.format.apply(null, arguments)
|
||||||
stampAndWrite.call(this, realConsoleLog, '', 'info', message)
|
stampAndWrite.call(this, realConsoleLog, '', 'info', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const realConsoleWarn = console.warn
|
const realConsoleWarn = console.warn
|
||||||
console.warn = function () {
|
console.warn = function () {
|
||||||
let message = util.format.apply(null, arguments)
|
const message = util.format.apply(null, arguments)
|
||||||
stampAndWrite.call(this, realConsoleWarn, '\x1b[33m', 'warn', message)
|
stampAndWrite.call(this, realConsoleWarn, '\x1b[33m', 'warn', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const realConsoleError = console.error
|
const realConsoleError = console.error
|
||||||
console.error = function () {
|
console.error = function () {
|
||||||
let message = util.format.apply(null, arguments)
|
const message = util.format.apply(null, arguments)
|
||||||
stampAndWrite.call(this, realConsoleError, '\x1b[31m', ' err', message)
|
stampAndWrite.call(this, realConsoleError, '\x1b[31m', ' err', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initializeLogger () {
|
async function initializeLogger () {
|
||||||
let logPath = path.resolve(config.logger.file)
|
const logPath = path.resolve(config.logger.file)
|
||||||
|
|
||||||
// Only throw bad permission errors
|
// Only throw bad permission errors
|
||||||
try {
|
try {
|
||||||
@ -61,7 +62,7 @@ async function initializeLogger () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
lfs = fs.createWriteStream(logPath, {flags: 'a'})
|
lfs = fs.createWriteStream(logPath, { flags: 'a' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
lfs = null
|
lfs = null
|
||||||
console.error('Failed to initiate log file write stream')
|
console.error('Failed to initiate log file write stream')
|
||||||
|
@ -4,7 +4,7 @@ import Models from './models'
|
|||||||
const perPage = 6
|
const perPage = 6
|
||||||
|
|
||||||
async function cleanUserObject (dbe, admin) {
|
async function cleanUserObject (dbe, admin) {
|
||||||
let totp = await Users.User.Login.totpTokenRequired(dbe)
|
const totp = await Users.User.Login.totpTokenRequired(dbe)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: dbe.id,
|
id: dbe.id,
|
||||||
@ -25,7 +25,7 @@ async function cleanUserObject (dbe, admin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function cleanClientObject (dbe) {
|
async function cleanClientObject (dbe) {
|
||||||
let user = await Users.User.get(dbe.user_id)
|
const user = await Users.User.get(dbe.user_id)
|
||||||
return {
|
return {
|
||||||
id: dbe.id,
|
id: dbe.id,
|
||||||
title: dbe.title,
|
title: dbe.title,
|
||||||
@ -46,8 +46,8 @@ async function cleanClientObject (dbe) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function cleanBanObject (dbe) {
|
async function cleanBanObject (dbe) {
|
||||||
let user = await Users.User.get(dbe.user_id)
|
const user = await Users.User.get(dbe.user_id)
|
||||||
let admin = await Users.User.get(dbe.admin_id)
|
const admin = await Users.User.get(dbe.admin_id)
|
||||||
return {
|
return {
|
||||||
id: dbe.id,
|
id: dbe.id,
|
||||||
reason: dbe.reason,
|
reason: dbe.reason,
|
||||||
@ -68,14 +68,14 @@ async function cleanBanObject (dbe) {
|
|||||||
|
|
||||||
function dataFilter (data, fields, optional = []) {
|
function dataFilter (data, fields, optional = []) {
|
||||||
// Remove keys not listed in `fields`
|
// Remove keys not listed in `fields`
|
||||||
for (let i in data) {
|
for (const i in data) {
|
||||||
if (fields.indexOf(i) === -1) {
|
if (fields.indexOf(i) === -1) {
|
||||||
delete data[i]
|
delete data[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all the entries in `fields` are present as keys in `data` or are `optional`
|
// Ensure all the entries in `fields` are present as keys in `data` or are `optional`
|
||||||
for (let j in fields) {
|
for (const j in fields) {
|
||||||
if (!data[fields[j]] && optional.indexOf(fields[j]) === -1) return null
|
if (!data[fields[j]] && optional.indexOf(fields[j]) === -1) return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,18 +86,18 @@ const API = {
|
|||||||
// List all users (paginated)
|
// List all users (paginated)
|
||||||
getAllUsers: async function (page, adminId) {
|
getAllUsers: async function (page, adminId) {
|
||||||
let count = await Models.User.query().count('id as ids')
|
let count = await Models.User.query().count('id as ids')
|
||||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
if (!count.length || !count[0].ids || isNaN(page)) {
|
||||||
return { error: 'No users found in database' }
|
return { error: 'No users found in database' }
|
||||||
}
|
}
|
||||||
|
|
||||||
count = count[0].ids
|
count = count[0].ids
|
||||||
let paginated = Users.Pagination(perPage, parseInt(count), page)
|
const paginated = Users.Pagination(perPage, parseInt(count), page)
|
||||||
let raw = await Models.User.query().offset(paginated.offset).limit(perPage)
|
const raw = await Models.User.query().offset(paginated.offset).limit(perPage)
|
||||||
let admin = await Users.User.get(adminId)
|
const admin = await Users.User.get(adminId)
|
||||||
|
|
||||||
let users = []
|
const users = []
|
||||||
for (let i in raw) {
|
for (const i in raw) {
|
||||||
let entry = raw[i]
|
const entry = raw[i]
|
||||||
|
|
||||||
users.push(await cleanUserObject(entry, admin))
|
users.push(await cleanUserObject(entry, admin))
|
||||||
}
|
}
|
||||||
@ -108,16 +108,16 @@ const API = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getUser: async function (id) {
|
getUser: async function (id) {
|
||||||
let user = await Users.User.get(id)
|
const user = await Users.User.get(id)
|
||||||
if (!user) throw new Error('No such user')
|
if (!user) throw new Error('No such user')
|
||||||
|
|
||||||
return cleanUserObject(user, null)
|
return cleanUserObject(user, null)
|
||||||
},
|
},
|
||||||
editUser: async function (id, data) {
|
editUser: async function (id, data) {
|
||||||
let user = await Users.User.get(id)
|
const user = await Users.User.get(id)
|
||||||
if (!user) throw new Error('No such user')
|
if (!user) throw new Error('No such user')
|
||||||
|
|
||||||
let fields = [
|
const fields = [
|
||||||
'username', 'display_name', 'email', 'nw_privilege', 'activated'
|
'username', 'display_name', 'email', 'nw_privilege', 'activated'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ const API = {
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
resendActivationEmail: async function (id) {
|
resendActivationEmail: async function (id) {
|
||||||
let user = await Users.User.get(id)
|
const user = await Users.User.get(id)
|
||||||
if (!user) throw new Error('No such user')
|
if (!user) throw new Error('No such user')
|
||||||
|
|
||||||
if (user.activated === 1) return {}
|
if (user.activated === 1) return {}
|
||||||
@ -139,7 +139,7 @@ const API = {
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
revokeTotpToken: async function (id) {
|
revokeTotpToken: async function (id) {
|
||||||
let user = await Users.User.get(id)
|
const user = await Users.User.get(id)
|
||||||
if (!user) throw new Error('No such user')
|
if (!user) throw new Error('No such user')
|
||||||
|
|
||||||
await Models.TotpToken.query().delete().where('user_id', user.id)
|
await Models.TotpToken.query().delete().where('user_id', user.id)
|
||||||
@ -147,12 +147,12 @@ const API = {
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
sendPasswordEmail: async function (id) {
|
sendPasswordEmail: async function (id) {
|
||||||
let user = await Users.User.get(id)
|
const user = await Users.User.get(id)
|
||||||
if (!user) throw new Error('No such user')
|
if (!user) throw new Error('No such user')
|
||||||
|
|
||||||
let token = await Users.User.Reset.reset(user.email, false, true)
|
const token = await Users.User.Reset.reset(user.email, false, true)
|
||||||
|
|
||||||
return {token}
|
return { token }
|
||||||
},
|
},
|
||||||
// Search for users by terms and fields
|
// Search for users by terms and fields
|
||||||
searchUsers: async function (terms, fields = ['email']) {
|
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' }
|
if (!rows.length) return { error: 'No results' }
|
||||||
|
|
||||||
let cleaned = []
|
const cleaned = []
|
||||||
for (let i in rows) {
|
for (const i in rows) {
|
||||||
let userRaw = rows[i]
|
const userRaw = rows[i]
|
||||||
cleaned.push(await cleanUserObject(userRaw, null))
|
cleaned.push(await cleanUserObject(userRaw, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,17 +181,17 @@ const API = {
|
|||||||
// List all clients (paginated)
|
// List all clients (paginated)
|
||||||
getAllClients: async function (page) {
|
getAllClients: async function (page) {
|
||||||
let count = await Models.OAuth2Client.query().count('id as ids')
|
let count = await Models.OAuth2Client.query().count('id as ids')
|
||||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
if (!count.length || !count[0].ids || isNaN(page)) {
|
||||||
return { error: 'No clients' }
|
return { error: 'No clients' }
|
||||||
}
|
}
|
||||||
|
|
||||||
count = count[0].ids
|
count = count[0].ids
|
||||||
let paginated = Users.Pagination(perPage, parseInt(count), page)
|
const paginated = Users.Pagination(perPage, parseInt(count), page)
|
||||||
let raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage)
|
const raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage)
|
||||||
|
|
||||||
let clients = []
|
const clients = []
|
||||||
for (let i in raw) {
|
for (const i in raw) {
|
||||||
let entry = raw[i]
|
const entry = raw[i]
|
||||||
|
|
||||||
clients.push(await cleanClientObject(entry))
|
clients.push(await cleanClientObject(entry))
|
||||||
}
|
}
|
||||||
@ -202,14 +202,14 @@ const API = {
|
|||||||
},
|
},
|
||||||
// Get information about a client via id
|
// Get information about a client via id
|
||||||
getClient: async function (id) {
|
getClient: async function (id) {
|
||||||
let raw = await Models.OAuth2Client.query().where('id', id)
|
const raw = await Models.OAuth2Client.query().where('id', id)
|
||||||
if (!raw.length) throw new Error('No such client')
|
if (!raw.length) throw new Error('No such client')
|
||||||
|
|
||||||
return cleanClientObject(raw[0])
|
return cleanClientObject(raw[0])
|
||||||
},
|
},
|
||||||
// Update a client `id` in database with `data`
|
// Update a client `id` in database with `data`
|
||||||
updateClient: async function (id, data) {
|
updateClient: async function (id, data) {
|
||||||
let fields = [
|
const fields = [
|
||||||
'title', 'description', 'url', 'redirect_url', 'scope', 'verified'
|
'title', 'description', 'url', 'redirect_url', 'scope', 'verified'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -228,10 +228,10 @@ const API = {
|
|||||||
// Create a new secret for a client
|
// Create a new secret for a client
|
||||||
newSecret: async function (id) {
|
newSecret: async function (id) {
|
||||||
if (isNaN(id)) throw new Error('Invalid client ID')
|
if (isNaN(id)) throw new Error('Invalid client ID')
|
||||||
let secret = Users.Hash(16)
|
const secret = Users.Hash(16)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Models.OAuth2Client.query().patchAndFetchById(id, {secret: secret})
|
await Models.OAuth2Client.query().patchAndFetchById(id, { secret: secret })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('No such client')
|
throw new Error('No such client')
|
||||||
}
|
}
|
||||||
@ -240,14 +240,14 @@ const API = {
|
|||||||
},
|
},
|
||||||
// Create a new client
|
// Create a new client
|
||||||
createClient: async function (data, user) {
|
createClient: async function (data, user) {
|
||||||
let fields = [
|
const fields = [
|
||||||
'title', 'description', 'url', 'redirect_url', 'scope'
|
'title', 'description', 'url', 'redirect_url', 'scope'
|
||||||
]
|
]
|
||||||
|
|
||||||
data = dataFilter(data, fields, ['scope'])
|
data = dataFilter(data, fields, ['scope'])
|
||||||
if (!data) throw new Error('Missing fields')
|
if (!data) throw new Error('Missing fields')
|
||||||
|
|
||||||
let obj = Object.assign({
|
const obj = Object.assign({
|
||||||
secret: Users.Hash(16),
|
secret: Users.Hash(16),
|
||||||
grants: 'authorization_code',
|
grants: 'authorization_code',
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
@ -268,17 +268,17 @@ const API = {
|
|||||||
// List all bans (paginated)
|
// List all bans (paginated)
|
||||||
getAllBans: async function (page) {
|
getAllBans: async function (page) {
|
||||||
let count = await Models.Ban.query().count('id as ids')
|
let count = await Models.Ban.query().count('id as ids')
|
||||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
if (!count.length || !count[0].ids || isNaN(page)) {
|
||||||
return { error: 'No bans on record' }
|
return { error: 'No bans on record' }
|
||||||
}
|
}
|
||||||
|
|
||||||
count = count[0].ids
|
count = count[0].ids
|
||||||
let paginated = Users.Pagination(perPage, parseInt(count), page)
|
const paginated = Users.Pagination(perPage, parseInt(count), page)
|
||||||
let raw = await Models.Ban.query().offset(paginated.offset).limit(perPage)
|
const raw = await Models.Ban.query().offset(paginated.offset).limit(perPage)
|
||||||
|
|
||||||
let bans = []
|
const bans = []
|
||||||
for (let i in raw) {
|
for (const i in raw) {
|
||||||
let entry = raw[i]
|
const entry = raw[i]
|
||||||
|
|
||||||
bans.push(await cleanBanObject(entry))
|
bans.push(await cleanBanObject(entry))
|
||||||
}
|
}
|
||||||
@ -293,16 +293,16 @@ const API = {
|
|||||||
},
|
},
|
||||||
// Create a ban
|
// Create a ban
|
||||||
addBan: async function (data, adminId) {
|
addBan: async function (data, adminId) {
|
||||||
let user = await Users.User.get(parseInt(data.user_id))
|
const user = await Users.User.get(parseInt(data.user_id))
|
||||||
|
|
||||||
if (!user) throw new Error('No such user.')
|
if (!user) throw new Error('No such user.')
|
||||||
if (user.id === adminId) throw new Error('Cannot ban yourself!')
|
if (user.id === adminId) throw new Error('Cannot ban yourself!')
|
||||||
|
|
||||||
let admin = await Users.User.get(adminId)
|
const admin = await Users.User.get(adminId)
|
||||||
|
|
||||||
if (user.nw_privilege > admin.nw_privilege) throw new Error('Cannot ban user.')
|
if (user.nw_privilege > admin.nw_privilege) throw new Error('Cannot ban user.')
|
||||||
|
|
||||||
let banAdd = {
|
const banAdd = {
|
||||||
reason: data.reason || 'Unspecified ban',
|
reason: data.reason || 'Unspecified ban',
|
||||||
admin_id: adminId,
|
admin_id: adminId,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
@ -315,13 +315,13 @@ const API = {
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
lockAccount: async function (userId) {
|
lockAccount: async function (userId) {
|
||||||
let user = await Users.User.get(userId)
|
const user = await Users.User.get(userId)
|
||||||
if (user.id === 1 || user.nw_privilege > 2) {
|
if (user.id === 1 || user.nw_privilege > 2) {
|
||||||
throw new Error('Cannot lock this user.')
|
throw new Error('Cannot lock this user.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let lockId = Users.Hash(4)
|
const lockId = Users.Hash(4)
|
||||||
let userObf = {
|
const userObf = {
|
||||||
username: lockId,
|
username: lockId,
|
||||||
display_name: user.username,
|
display_name: user.username,
|
||||||
email: `${lockId}@icynet.eu`,
|
email: `${lockId}@icynet.eu`,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {EmailTemplate} from 'email-templates'
|
import { EmailTemplate } from 'email-templates'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer'
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ tls.DEFAULT_ECDH_CURVE = 'auto'
|
|||||||
|
|
||||||
const templateDir = path.join(__dirname, '../../', 'templates')
|
const templateDir = path.join(__dirname, '../../', 'templates')
|
||||||
|
|
||||||
let templateCache = {}
|
const templateCache = {}
|
||||||
let transporter
|
let transporter
|
||||||
|
|
||||||
// Send an email to `email` with `headers`
|
// Send an email to `email` with `headers`
|
||||||
@ -42,7 +42,7 @@ async function pushMail (template, email, context) {
|
|||||||
templ = templateCache[template]
|
templ = templateCache[template]
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await templ.render(context)
|
const result = await templ.render(context)
|
||||||
|
|
||||||
console.debug('Mail being sent: %s to %s', template, email)
|
console.debug('Mail being sent: %s to %s', template, email)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import qs from 'querystring'
|
import qs from 'querystring'
|
||||||
import oauth from 'oauth-libre'
|
import oauth from 'oauth-libre'
|
||||||
import uuidV1 from 'uuid/v1'
|
import { v1 as uuidV1 } from 'uuid'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
|
||||||
import config from '../../scripts/load-config'
|
import config from '../../scripts/load-config'
|
||||||
@ -18,7 +18,7 @@ const API = {
|
|||||||
Common: {
|
Common: {
|
||||||
// Generate a hash based on the current session
|
// Generate a hash based on the current session
|
||||||
stateGenerator: (req) => {
|
stateGenerator: (req) => {
|
||||||
let sessionCrypto = req.session.id + ':' + config.server.session_secret
|
const sessionCrypto = req.session.id + ':' + config.server.session_secret
|
||||||
return crypto.createHash('sha256').update(sessionCrypto).digest('hex')
|
return crypto.createHash('sha256').update(sessionCrypto).digest('hex')
|
||||||
},
|
},
|
||||||
// Find an user with an external ID
|
// Find an user with an external ID
|
||||||
@ -29,7 +29,7 @@ const API = {
|
|||||||
extr.user = null
|
extr.user = null
|
||||||
|
|
||||||
if (extr.user_id !== null) {
|
if (extr.user_id !== null) {
|
||||||
let user = await UAPI.User.get(extr.user_id)
|
const user = await UAPI.User.get(extr.user_id)
|
||||||
if (user) {
|
if (user) {
|
||||||
extr.user = user
|
extr.user = user
|
||||||
}
|
}
|
||||||
@ -39,12 +39,12 @@ const API = {
|
|||||||
},
|
},
|
||||||
// Get user ban status
|
// Get user ban status
|
||||||
getBan: async (user, ipAddress) => {
|
getBan: async (user, ipAddress) => {
|
||||||
let banList = await UAPI.User.getBanStatus(ipAddress || user.id, ipAddress != null)
|
const banList = await UAPI.User.getBanStatus(ipAddress || user.id, ipAddress != null)
|
||||||
return banList
|
return banList
|
||||||
},
|
},
|
||||||
// Create a new `external` instance for a user
|
// Create a new `external` instance for a user
|
||||||
new: async (service, identifier, user) => {
|
new: async (service, identifier, user) => {
|
||||||
let data = {
|
const data = {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
service: service,
|
service: service,
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
@ -57,7 +57,7 @@ const API = {
|
|||||||
// Create a new user
|
// Create a new user
|
||||||
newUser: async (service, identifier, data) => {
|
newUser: async (service, identifier, data) => {
|
||||||
if (config.external.registrations !== true) throw new Error('Registrations from third-party websites are not allowed.')
|
if (config.external.registrations !== true) throw new Error('Registrations from third-party websites are not allowed.')
|
||||||
let udataLimited = Object.assign({
|
const udataLimited = Object.assign({
|
||||||
activated: 1,
|
activated: 1,
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
updated_at: new Date(),
|
updated_at: new Date(),
|
||||||
@ -83,14 +83,14 @@ const API = {
|
|||||||
// Check if the email given to us is already registered, if so,
|
// Check if the email given to us is already registered, if so,
|
||||||
// tell them to log in first.
|
// tell them to log in first.
|
||||||
if (udataLimited.email && udataLimited.email !== '') {
|
if (udataLimited.email && udataLimited.email !== '') {
|
||||||
let getByEmail = await UAPI.User.get(udataLimited.email)
|
const getByEmail = await UAPI.User.get(udataLimited.email)
|
||||||
if (getByEmail) {
|
if (getByEmail) {
|
||||||
throw new Error('An user with this email address is already registered, but this external account is are not linked. If you wish to link the account, please log in first.')
|
throw new Error('An user with this email address is already registered, but this external account is are not linked. If you wish to link the account, please log in first.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new user based on the information we got from an external service
|
// Create a new user based on the information we got from an external service
|
||||||
let newUser = await models.User.query().insert(udataLimited)
|
const newUser = await models.User.query().insert(udataLimited)
|
||||||
await API.Common.new(service, identifier, newUser)
|
await API.Common.new(service, identifier, newUser)
|
||||||
|
|
||||||
return newUser
|
return newUser
|
||||||
@ -98,7 +98,7 @@ const API = {
|
|||||||
// Remove an `external` object (thus unlinking from a service)
|
// Remove an `external` object (thus unlinking from a service)
|
||||||
remove: async (user, service) => {
|
remove: async (user, service) => {
|
||||||
user = await UAPI.User.ensureObject(user, ['password'])
|
user = await UAPI.User.ensureObject(user, ['password'])
|
||||||
let userExterns = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
|
const userExterns = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
|
||||||
|
|
||||||
if (!userExterns.length) {
|
if (!userExterns.length) {
|
||||||
return false
|
return false
|
||||||
@ -113,30 +113,30 @@ const API = {
|
|||||||
},
|
},
|
||||||
// Common code for all auth callbacks
|
// Common code for all auth callbacks
|
||||||
callback: async (identifier, uid, user, ipAddress, remoteData, avatarFunc) => {
|
callback: async (identifier, uid, user, ipAddress, remoteData, avatarFunc) => {
|
||||||
let exists = await API.Common.getExternal(identifier, uid)
|
const exists = await API.Common.getExternal(identifier, uid)
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
// Get bans for user
|
// Get bans for user
|
||||||
let bans = await API.Common.getBan(user)
|
const bans = await API.Common.getBan(user)
|
||||||
if (bans.length) return { banned: bans, ip: false }
|
if (bans.length) return { banned: bans, ip: false }
|
||||||
|
|
||||||
if (exists) return {error: null, user: user}
|
if (exists) return { error: null, user: user }
|
||||||
|
|
||||||
await API.Common.new(identifier, uid, 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
|
// Callback succeeded with user id and the external table exists, we log in the user
|
||||||
if (exists) {
|
if (exists) {
|
||||||
// Get bans for user
|
// Get bans for user
|
||||||
let bans = await API.Common.getBan(exists.user)
|
const bans = await API.Common.getBan(exists.user)
|
||||||
|
|
||||||
if (bans.length) return { banned: bans, ip: false }
|
if (bans.length) return { banned: bans, ip: false }
|
||||||
return {error: null, user: exists.user}
|
return { error: null, user: exists.user }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get bans for IP address
|
// Get bans for IP address
|
||||||
let bans = await API.Common.getBan(null, ipAddress)
|
const bans = await API.Common.getBan(null, ipAddress)
|
||||||
if (bans.length) return { banned: bans, ip: true }
|
if (bans.length) return { banned: bans, ip: true }
|
||||||
|
|
||||||
// Run the function for avatar fetching
|
// Run the function for avatar fetching
|
||||||
@ -146,14 +146,14 @@ const API = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assign the data
|
// Assign the data
|
||||||
let newUData = Object.assign({
|
const newUData = Object.assign({
|
||||||
email: remoteData.email || '',
|
email: remoteData.email || '',
|
||||||
avatar_file: avatar,
|
avatar_file: avatar,
|
||||||
ip_address: ipAddress
|
ip_address: ipAddress
|
||||||
}, remoteData)
|
}, remoteData)
|
||||||
|
|
||||||
// Remove unnecessary fields
|
// Remove unnecessary fields
|
||||||
for (let i in newUData) {
|
for (const i in newUData) {
|
||||||
if (userFields.indexOf(i) === -1) {
|
if (userFields.indexOf(i) === -1) {
|
||||||
delete newUData[i]
|
delete newUData[i]
|
||||||
}
|
}
|
||||||
@ -163,10 +163,10 @@ const API = {
|
|||||||
try {
|
try {
|
||||||
newUser = await API.Common.newUser(identifier, uid, newUData)
|
newUser = await API.Common.newUser(identifier, uid, newUData)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {error: e.message}
|
return { error: e.message }
|
||||||
}
|
}
|
||||||
|
|
||||||
return {error: null, user: newUser}
|
return { error: null, user: newUser }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Facebook: {
|
Facebook: {
|
||||||
@ -175,7 +175,7 @@ const API = {
|
|||||||
|
|
||||||
if (rawData.picture) {
|
if (rawData.picture) {
|
||||||
if (rawData.picture.is_silhouette === false && rawData.picture.url) {
|
if (rawData.picture.is_silhouette === false && rawData.picture.url) {
|
||||||
let imgdata = await Image.downloadImage(rawData.picture.url)
|
const imgdata = await Image.downloadImage(rawData.picture.url)
|
||||||
if (imgdata && imgdata.fileName) {
|
if (imgdata && imgdata.fileName) {
|
||||||
profilepic = imgdata.fileName
|
profilepic = imgdata.fileName
|
||||||
}
|
}
|
||||||
@ -186,17 +186,17 @@ const API = {
|
|||||||
},
|
},
|
||||||
callback: async (user, authResponse, ipAddress) => {
|
callback: async (user, authResponse, ipAddress) => {
|
||||||
if (!authResponse) {
|
if (!authResponse) {
|
||||||
return {error: 'No Authorization'}
|
return { error: 'No Authorization' }
|
||||||
}
|
}
|
||||||
|
|
||||||
let uid = authResponse.userID
|
const uid = authResponse.userID
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
return {error: 'No Authorization'}
|
return { error: 'No Authorization' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get facebook user information in order to create a new user or verify
|
// Get facebook user information in order to create a new user or verify
|
||||||
let fbdata
|
let fbdata
|
||||||
let intel = {
|
const intel = {
|
||||||
access_token: authResponse.accessToken,
|
access_token: authResponse.accessToken,
|
||||||
fields: 'name,email,picture,short_name'
|
fields: 'name,email,picture,short_name'
|
||||||
}
|
}
|
||||||
@ -205,14 +205,14 @@ const API = {
|
|||||||
fbdata = await http.GET('https://graph.facebook.com/v2.10/' + uid + '?' + qs.stringify(intel))
|
fbdata = await http.GET('https://graph.facebook.com/v2.10/' + uid + '?' + qs.stringify(intel))
|
||||||
fbdata = JSON.parse(fbdata)
|
fbdata = JSON.parse(fbdata)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {error: 'Could not get user information', errorObject: e}
|
return { error: 'Could not get user information', errorObject: e }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fbdata.error) {
|
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),
|
username: fbdata.short_name || 'FB' + UAPI.Hash(4),
|
||||||
display_name: fbdata.name,
|
display_name: fbdata.name,
|
||||||
email: fbdata.email || ''
|
email: fbdata.email || ''
|
||||||
@ -226,7 +226,7 @@ const API = {
|
|||||||
let profilepic = null
|
let profilepic = null
|
||||||
|
|
||||||
if (rawData.profile_image_url_https) {
|
if (rawData.profile_image_url_https) {
|
||||||
let imgdata = await Image.downloadImage(rawData.profile_image_url_https)
|
const imgdata = await Image.downloadImage(rawData.profile_image_url_https)
|
||||||
if (imgdata && imgdata.fileName) {
|
if (imgdata && imgdata.fileName) {
|
||||||
profilepic = imgdata.fileName
|
profilepic = imgdata.fileName
|
||||||
}
|
}
|
||||||
@ -236,7 +236,7 @@ const API = {
|
|||||||
},
|
},
|
||||||
oauthApp: function () {
|
oauthApp: function () {
|
||||||
if (!twitterApp) {
|
if (!twitterApp) {
|
||||||
let redirectUri = config.server.domain + '/api/external/twitter/callback'
|
const redirectUri = config.server.domain + '/api/external/twitter/callback'
|
||||||
twitterApp = new oauth.PromiseOAuth(
|
twitterApp = new oauth.PromiseOAuth(
|
||||||
'https://api.twitter.com/oauth/request_token',
|
'https://api.twitter.com/oauth/request_token',
|
||||||
'https://api.twitter.com/oauth/access_token',
|
'https://api.twitter.com/oauth/access_token',
|
||||||
@ -256,12 +256,12 @@ const API = {
|
|||||||
tokens = await twitterApp.getOAuthRequestToken()
|
tokens = await twitterApp.getOAuthRequestToken()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(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) {
|
getAccessTokens: async function (token, secret, verifier) {
|
||||||
if (!twitterApp) API.Twitter.oauthApp()
|
if (!twitterApp) API.Twitter.oauthApp()
|
||||||
@ -271,28 +271,28 @@ const API = {
|
|||||||
tokens = await twitterApp.getOAuthAccessToken(token, secret, verifier)
|
tokens = await twitterApp.getOAuthAccessToken(token, secret, verifier)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(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) {
|
callback: async function (user, accessTokens, ipAddress) {
|
||||||
if (!twitterApp) API.Twitter.oauthApp()
|
if (!twitterApp) API.Twitter.oauthApp()
|
||||||
let twdata
|
let twdata
|
||||||
try {
|
try {
|
||||||
let resp = await twitterApp.get('https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
|
const resp = await twitterApp.get('https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
|
||||||
accessTokens.access_token, accessTokens.access_token_secret)
|
accessTokens.access_token, accessTokens.access_token_secret)
|
||||||
twdata = JSON.parse(resp[0])
|
twdata = JSON.parse(resp[0])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(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,
|
username: twdata.screen_name,
|
||||||
display_name: twdata.name,
|
display_name: twdata.name,
|
||||||
email: twdata.email || ''
|
email: twdata.email || ''
|
||||||
@ -305,7 +305,7 @@ const API = {
|
|||||||
getAvatar: async (rawData) => {
|
getAvatar: async (rawData) => {
|
||||||
let profilepic = null
|
let profilepic = null
|
||||||
if (rawData.image) {
|
if (rawData.image) {
|
||||||
let imgdata = await Image.downloadImage(rawData.image)
|
const imgdata = await Image.downloadImage(rawData.image)
|
||||||
if (imgdata && imgdata.fileName) {
|
if (imgdata && imgdata.fileName) {
|
||||||
profilepic = imgdata.fileName
|
profilepic = imgdata.fileName
|
||||||
}
|
}
|
||||||
@ -317,10 +317,10 @@ const API = {
|
|||||||
let uid
|
let uid
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let test = await http.GET('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + data.id_token)
|
const test = await http.GET('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + data.id_token)
|
||||||
if (!test) throw new Error('No response!')
|
if (!test) throw new Error('No response!')
|
||||||
|
|
||||||
let jsondata = JSON.parse(test)
|
const jsondata = JSON.parse(test)
|
||||||
if (!jsondata || !jsondata.email || !jsondata.name) throw new Error('Please allow Basic Profile and Email.')
|
if (!jsondata || !jsondata.email || !jsondata.name) throw new Error('Please allow Basic Profile and Email.')
|
||||||
|
|
||||||
if (jsondata.email !== data.email || jsondata.name !== data.name) throw new Error('Conflicting data. Please try again!')
|
if (jsondata.email !== data.email || jsondata.name !== data.name) throw new Error('Conflicting data. Please try again!')
|
||||||
@ -329,10 +329,10 @@ const API = {
|
|||||||
|
|
||||||
uid = jsondata.sub
|
uid = jsondata.sub
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {error: e.message}
|
return { error: e.message }
|
||||||
}
|
}
|
||||||
|
|
||||||
let cleanedData = Object.assign(data, {
|
const cleanedData = Object.assign(data, {
|
||||||
username: data.name,
|
username: data.name,
|
||||||
display_name: data.name,
|
display_name: data.name,
|
||||||
email: data.email || ''
|
email: data.email || ''
|
||||||
@ -344,10 +344,10 @@ const API = {
|
|||||||
Discord: {
|
Discord: {
|
||||||
getAvatar: async (rawData) => {
|
getAvatar: async (rawData) => {
|
||||||
let profilepic = null
|
let profilepic = null
|
||||||
let aviSnowflake = rawData.avatar
|
const aviSnowflake = rawData.avatar
|
||||||
if (aviSnowflake) {
|
if (aviSnowflake) {
|
||||||
try {
|
try {
|
||||||
let avpt = await Image.downloadImage('https://cdn.discordapp.com/avatars/' + rawData.id + '/' + aviSnowflake + '.png')
|
const avpt = await Image.downloadImage('https://cdn.discordapp.com/avatars/' + rawData.id + '/' + aviSnowflake + '.png')
|
||||||
if (avpt && avpt.fileName) {
|
if (avpt && avpt.fileName) {
|
||||||
profilepic = avpt.fileName
|
profilepic = avpt.fileName
|
||||||
}
|
}
|
||||||
@ -372,54 +372,54 @@ const API = {
|
|||||||
},
|
},
|
||||||
getAuthorizeURL: function (req) {
|
getAuthorizeURL: function (req) {
|
||||||
if (!discordApp) API.Discord.oauth2App()
|
if (!discordApp) API.Discord.oauth2App()
|
||||||
let state = API.Common.stateGenerator(req)
|
const state = API.Common.stateGenerator(req)
|
||||||
let redirectUri = config.server.domain + '/api/external/discord/callback'
|
const redirectUri = config.server.domain + '/api/external/discord/callback'
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
'client_id': config.external.discord.api,
|
client_id: config.external.discord.api,
|
||||||
'redirect_uri': redirectUri,
|
redirect_uri: redirectUri,
|
||||||
'scope': 'identify email',
|
scope: 'identify email',
|
||||||
'response_type': 'code',
|
response_type: 'code',
|
||||||
'state': state
|
state: state
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = discordApp.getAuthorizeUrl(params)
|
const url = discordApp.getAuthorizeUrl(params)
|
||||||
|
|
||||||
return {error: null, state: state, url: url}
|
return { error: null, state: state, url: url }
|
||||||
},
|
},
|
||||||
getAccessToken: async function (code) {
|
getAccessToken: async function (code) {
|
||||||
if (!discordApp) API.Discord.oauth2App()
|
if (!discordApp) API.Discord.oauth2App()
|
||||||
|
|
||||||
let redirectUri = config.server.domain + '/api/external/discord/callback'
|
const redirectUri = config.server.domain + '/api/external/discord/callback'
|
||||||
let tokens
|
let tokens
|
||||||
try {
|
try {
|
||||||
tokens = await discordApp.getOAuthAccessToken(code, {grant_type: 'authorization_code', redirect_uri: redirectUri})
|
tokens = await discordApp.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: redirectUri })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(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]
|
tokens = tokens[2]
|
||||||
|
|
||||||
return {error: null, accessToken: tokens.access_token}
|
return { error: null, accessToken: tokens.access_token }
|
||||||
},
|
},
|
||||||
callback: async function (user, accessToken, ipAddress) {
|
callback: async function (user, accessToken, ipAddress) {
|
||||||
if (!discordApp) API.Discord.oauth2App()
|
if (!discordApp) API.Discord.oauth2App()
|
||||||
|
|
||||||
let ddata
|
let ddata
|
||||||
try {
|
try {
|
||||||
let resp = await discordApp.get('https://discordapp.com/api/users/@me', accessToken)
|
const resp = await discordApp.get('https://discordapp.com/api/users/@me', accessToken)
|
||||||
ddata = JSON.parse(resp)
|
ddata = JSON.parse(resp)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
return {error: 'Could not get user information'}
|
return { error: 'Could not get user information' }
|
||||||
}
|
}
|
||||||
|
|
||||||
let uid = ddata.id
|
const uid = ddata.id
|
||||||
|
|
||||||
// Create a new user
|
// Create a new user
|
||||||
let cleanedData = Object.assign(ddata, {
|
const cleanedData = Object.assign(ddata, {
|
||||||
display_name: ddata.username,
|
display_name: ddata.username,
|
||||||
email: ddata.email || ''
|
email: ddata.email || ''
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import gm from 'gm'
|
import gm from 'gm'
|
||||||
import url from 'url'
|
import { URL } from 'url'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import uuid from 'uuid/v4'
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
import http from '../../scripts/http'
|
import http from '../../scripts/http'
|
||||||
|
|
||||||
@ -20,8 +20,8 @@ const imageTypes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function decodeBase64Image (dataString) {
|
function decodeBase64Image (dataString) {
|
||||||
let matches = dataString.match(/^data:([A-Za-z-+/]+);base64,(.+)$/)
|
const matches = dataString.match(/^data:([A-Za-z-+/]+);base64,(.+)$/)
|
||||||
let response = {}
|
const response = {}
|
||||||
|
|
||||||
if (matches.length !== 3) {
|
if (matches.length !== 3) {
|
||||||
return null
|
return null
|
||||||
@ -34,10 +34,10 @@ function decodeBase64Image (dataString) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saneFields (fields) {
|
function saneFields (fields) {
|
||||||
let out = {}
|
const out = {}
|
||||||
|
|
||||||
for (let i in fields) {
|
for (const i in fields) {
|
||||||
let entry = fields[i]
|
const entry = fields[i]
|
||||||
if (typeof entry === 'object' && entry.length === 1 && !isNaN(parseInt(entry[0]))) {
|
if (typeof entry === 'object' && entry.length === 1 && !isNaN(parseInt(entry[0]))) {
|
||||||
out[i] = parseInt(entry[0])
|
out[i] = parseInt(entry[0])
|
||||||
}
|
}
|
||||||
@ -53,17 +53,17 @@ async function bailOut (file, error) {
|
|||||||
|
|
||||||
async function imageBase64 (baseObj) {
|
async function imageBase64 (baseObj) {
|
||||||
if (!baseObj) return null
|
if (!baseObj) return null
|
||||||
let imgData = decodeBase64Image(baseObj)
|
const imgData = decodeBase64Image(baseObj)
|
||||||
|
|
||||||
if (!imgData) return null
|
if (!imgData) return null
|
||||||
if (!imageTypes[imgData.type]) return null
|
if (!imageTypes[imgData.type]) return null
|
||||||
|
|
||||||
let imageName = 'base64-' + uuid()
|
let imageName = 'base64-' + uuid()
|
||||||
let ext = imageTypes[imgData.type] || '.png'
|
const ext = imageTypes[imgData.type] || '.png'
|
||||||
|
|
||||||
imageName += ext
|
imageName += ext
|
||||||
|
|
||||||
let fpath = path.join(images, imageName)
|
const fpath = path.join(images, imageName)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.writeFile(fpath, imgData.data)
|
await fs.writeFile(fpath, imgData.data)
|
||||||
@ -72,11 +72,11 @@ async function imageBase64 (baseObj) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return {file: fpath}
|
return { file: fpath }
|
||||||
}
|
}
|
||||||
|
|
||||||
function gravatarURL (email) {
|
function gravatarURL (email) {
|
||||||
let sum = crypto.createHash('md5').update(email).digest('hex')
|
const sum = crypto.createHash('md5').update(email).digest('hex')
|
||||||
return gravatar + sum + '.jpg'
|
return gravatar + sum + '.jpg'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +85,8 @@ async function downloadImage (imgUrl, designation) {
|
|||||||
if (!designation) designation = 'download'
|
if (!designation) designation = 'download'
|
||||||
|
|
||||||
let imageName = designation + '-' + uuid()
|
let imageName = designation + '-' + uuid()
|
||||||
let uridata = url.parse(imgUrl)
|
const uridata = new URL(imgUrl)
|
||||||
let pathdata = path.parse(uridata.path)
|
const pathdata = path.parse(uridata.path)
|
||||||
|
|
||||||
imageName += pathdata.ext || '.png'
|
imageName += pathdata.ext || '.png'
|
||||||
|
|
||||||
@ -108,23 +108,23 @@ async function uploadImage (identifier, fields, files) {
|
|||||||
fields = saneFields(fields)
|
fields = saneFields(fields)
|
||||||
|
|
||||||
// Get file info, generate a file name
|
// Get file info, generate a file name
|
||||||
let fileHash = uuid()
|
const fileHash = uuid()
|
||||||
let contentType = file.headers['content-type']
|
const contentType = file.headers['content-type']
|
||||||
if (!contentType) return bailOut(file.path, 'Invalid of missing content-type header')
|
if (!contentType) return bailOut(file.path, 'Invalid of missing content-type header')
|
||||||
|
|
||||||
file = file.path
|
file = file.path
|
||||||
|
|
||||||
// Make sure content type is allowed
|
// Make sure content type is allowed
|
||||||
let match = false
|
let match = false
|
||||||
for (let i in imageTypes) {
|
for (const i in imageTypes) {
|
||||||
if (i === contentType) {
|
if (i === contentType) {
|
||||||
match = true
|
match = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!match) return bailOut(file, 'Invalid image type. Only PNG, JPG and JPEG files are allowed.')
|
if (!match) return bailOut(file, 'Invalid image type. Only PNG, JPG and JPEG files are allowed.')
|
||||||
let extension = imageTypes[contentType]
|
const extension = imageTypes[contentType]
|
||||||
let fileName = identifier + '-' + fileHash + extension
|
const fileName = identifier + '-' + fileHash + extension
|
||||||
|
|
||||||
// Check for cropping
|
// Check for cropping
|
||||||
if (fields.x == null || fields.y == null || fields.width == null || fields.height == null) {
|
if (fields.x == null || fields.y == null || fields.width == null || fields.height == null) {
|
||||||
@ -172,7 +172,7 @@ async function uploadImage (identifier, fields, files) {
|
|||||||
return bailOut(file, 'An error occured while cropping.')
|
return bailOut(file, 'An error occured while cropping.')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {file: fileName}
|
return { file: fileName }
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -3,7 +3,7 @@ import cprog from 'child_process'
|
|||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import notp from 'notp'
|
import notp from 'notp'
|
||||||
import base32 from 'thirty-two'
|
import base32 from 'thirty-two'
|
||||||
import uuidV1 from 'uuid/v1'
|
import { v1 as uuidV1 } from 'uuid'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
|
|
||||||
import config from '../../scripts/load-config'
|
import config from '../../scripts/load-config'
|
||||||
@ -16,12 +16,12 @@ const emailRe = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\
|
|||||||
// Fork a bcrypt process to hash and compare passwords
|
// Fork a bcrypt process to hash and compare passwords
|
||||||
function bcryptTask (data) {
|
function bcryptTask (data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let proc = cprog.fork(path.join(__dirname, '../../scripts', 'bcrypt.js'))
|
const proc = cprog.fork(path.join(__dirname, '../../scripts', 'bcrypt.js'))
|
||||||
let done = false
|
let done = false
|
||||||
proc.send(data.task + ' ' + JSON.stringify(data))
|
proc.send(data.task + ' ' + JSON.stringify(data))
|
||||||
proc.on('message', (chunk) => {
|
proc.on('message', (chunk) => {
|
||||||
if (chunk == null) return reject(new Error('No response'))
|
if (chunk == null) return reject(new Error('No response'))
|
||||||
let line = chunk.toString().trim()
|
const line = chunk.toString().trim()
|
||||||
done = true
|
done = true
|
||||||
if (line === 'error') {
|
if (line === 'error') {
|
||||||
return reject(new Error(line))
|
return reject(new Error(line))
|
||||||
@ -43,8 +43,8 @@ function bcryptTask (data) {
|
|||||||
function keysAvailable (object, required) {
|
function keysAvailable (object, required) {
|
||||||
let found = true
|
let found = true
|
||||||
|
|
||||||
for (let i in required) {
|
for (const i in required) {
|
||||||
let key = required[i]
|
const key = required[i]
|
||||||
if (object[key] == null) {
|
if (object[key] == null) {
|
||||||
found = false
|
found = false
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ async function cleanUpDonation (obj, mcOnly, timeframe) {
|
|||||||
user = await API.User.get(obj.user_id)
|
user = await API.User.get(obj.user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = {
|
const result = {
|
||||||
trackId: obj.id,
|
trackId: obj.id,
|
||||||
amount: obj.amount,
|
amount: obj.amount,
|
||||||
donated: obj.created_at
|
donated: obj.created_at
|
||||||
@ -75,10 +75,10 @@ async function cleanUpDonation (obj, mcOnly, timeframe) {
|
|||||||
result.name = user.display_name
|
result.name = user.display_name
|
||||||
}
|
}
|
||||||
|
|
||||||
let sources = obj.source.split(',')
|
const sources = obj.source.split(',')
|
||||||
for (let i in sources) {
|
for (const i in sources) {
|
||||||
if (sources[i].indexOf('mcu:') === 0) {
|
if (sources[i].indexOf('mcu:') === 0) {
|
||||||
let mcu = sources[i].split(':')[1]
|
const mcu = sources[i].split(':')[1]
|
||||||
if (mcu.match(/^([\w_]{2,16})$/i)) {
|
if (mcu.match(/^([\w_]{2,16})$/i)) {
|
||||||
result.minecraft_username = mcu
|
result.minecraft_username = mcu
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ async function cleanUpDonation (obj, mcOnly, timeframe) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
let txnStore = []
|
const txnStore = []
|
||||||
|
|
||||||
const API = {
|
const API = {
|
||||||
Hash: (len) => {
|
Hash: (len) => {
|
||||||
@ -101,10 +101,10 @@ const API = {
|
|||||||
if (!ppp) ppp = 5
|
if (!ppp) ppp = 5
|
||||||
if (!dcount) return null
|
if (!dcount) return null
|
||||||
|
|
||||||
let pageCount = Math.ceil(dcount / ppp)
|
const pageCount = Math.ceil(dcount / ppp)
|
||||||
if (page > pageCount) page = pageCount
|
if (page > pageCount) page = pageCount
|
||||||
|
|
||||||
let offset = (page - 1) * ppp
|
const offset = (page - 1) * ppp
|
||||||
|
|
||||||
return {
|
return {
|
||||||
page: page,
|
page: page,
|
||||||
@ -135,7 +135,7 @@ const API = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await models.User.query().where(scope, identifier)
|
const user = await models.User.query().where(scope, identifier)
|
||||||
if (!user.length) return null
|
if (!user.length) return null
|
||||||
|
|
||||||
return user[0]
|
return user[0]
|
||||||
@ -154,16 +154,16 @@ const API = {
|
|||||||
socialStatus: async function (user) {
|
socialStatus: async function (user) {
|
||||||
user = await API.User.ensureObject(user, ['password'])
|
user = await API.User.ensureObject(user, ['password'])
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
let external = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
|
const external = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
|
||||||
let enabled = {}
|
const enabled = {}
|
||||||
|
|
||||||
for (let i in external) {
|
for (const i in external) {
|
||||||
let ext = external[i]
|
const ext = external[i]
|
||||||
enabled[ext.service] = true
|
enabled[ext.service] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let accountSourceIsExternal = user.password === null || user.password === ''
|
const accountSourceIsExternal = user.password === null || user.password === ''
|
||||||
let obj = {
|
const obj = {
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
password: !accountSourceIsExternal
|
password: !accountSourceIsExternal
|
||||||
}
|
}
|
||||||
@ -186,8 +186,8 @@ const API = {
|
|||||||
},
|
},
|
||||||
changeAvatar: async function (user, fileName) {
|
changeAvatar: async function (user, fileName) {
|
||||||
user = await API.User.ensureObject(user, ['avatar_file'])
|
user = await API.User.ensureObject(user, ['avatar_file'])
|
||||||
let uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images')
|
const uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images')
|
||||||
let pathOf = path.join(uploadsDir, fileName)
|
const pathOf = path.join(uploadsDir, fileName)
|
||||||
|
|
||||||
if (!await fs.exists(pathOf)) {
|
if (!await fs.exists(pathOf)) {
|
||||||
throw new Error('No such file')
|
throw new Error('No such file')
|
||||||
@ -195,26 +195,26 @@ const API = {
|
|||||||
|
|
||||||
// Delete previous upload
|
// Delete previous upload
|
||||||
if (user.avatar_file != null) {
|
if (user.avatar_file != null) {
|
||||||
let file = path.join(uploadsDir, user.avatar_file)
|
const file = path.join(uploadsDir, user.avatar_file)
|
||||||
if (await fs.exists(file)) {
|
if (await fs.exists(file)) {
|
||||||
await fs.unlink(file)
|
await fs.unlink(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await API.User.update(user, {avatar_file: fileName})
|
await API.User.update(user, { avatar_file: fileName })
|
||||||
return fileName
|
return fileName
|
||||||
},
|
},
|
||||||
removeAvatar: async function (user) {
|
removeAvatar: async function (user) {
|
||||||
user = await API.User.ensureObject(user, ['avatar_file'])
|
user = await API.User.ensureObject(user, ['avatar_file'])
|
||||||
let uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images')
|
const uploadsDir = path.join(__dirname, '../../', 'usercontent', 'images')
|
||||||
if (!user.avatar_file) return {}
|
if (!user.avatar_file) return {}
|
||||||
|
|
||||||
let file = path.join(uploadsDir, user.avatar_file)
|
const file = path.join(uploadsDir, user.avatar_file)
|
||||||
if (await fs.exists(file)) {
|
if (await fs.exists(file)) {
|
||||||
await fs.unlink(file)
|
await fs.unlink(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
return API.User.update(user, {avatar_file: null})
|
return API.User.update(user, { avatar_file: null })
|
||||||
},
|
},
|
||||||
getBanStatus: async function (field, ip = false) {
|
getBanStatus: async function (field, ip = false) {
|
||||||
let bans
|
let bans
|
||||||
@ -224,15 +224,15 @@ const API = {
|
|||||||
bans = await models.Ban.query().where('user_id', field)
|
bans = await models.Ban.query().where('user_id', field)
|
||||||
}
|
}
|
||||||
|
|
||||||
let bansActive = []
|
const bansActive = []
|
||||||
|
|
||||||
for (let i in bans) {
|
for (const i in bans) {
|
||||||
let ban = bans[i]
|
const ban = bans[i]
|
||||||
|
|
||||||
// Check expiry
|
// Check expiry
|
||||||
if (ban.expires_at && new Date(ban.expires_at).getTime() < Date.now()) continue
|
if (ban.expires_at && new Date(ban.expires_at).getTime() < Date.now()) continue
|
||||||
|
|
||||||
let banInfo = {
|
const banInfo = {
|
||||||
banned: ban.created_at,
|
banned: ban.created_at,
|
||||||
reason: ban.reason,
|
reason: ban.reason,
|
||||||
expiry: ban.expires_at
|
expiry: ban.expires_at
|
||||||
@ -247,7 +247,7 @@ const API = {
|
|||||||
password: async function (user, password) {
|
password: async function (user, password) {
|
||||||
user = await API.User.ensureObject(user, ['password'])
|
user = await API.User.ensureObject(user, ['password'])
|
||||||
if (!user.password) return false
|
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) {
|
activationToken: async function (token) {
|
||||||
let getToken = await models.Token.query().where('token', token).andWhere('type', 1)
|
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
|
if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return false
|
||||||
|
|
||||||
let user = await API.User.get(getToken.user_id)
|
const user = await API.User.get(getToken.user_id)
|
||||||
if (!user) return false
|
if (!user) return false
|
||||||
|
|
||||||
await models.User.query().patchAndFetchById(user.id, {activated: 1})
|
await models.User.query().patchAndFetchById(user.id, { activated: 1 })
|
||||||
await models.Token.query().delete().where('id', getToken.id)
|
await models.Token.query().delete().where('id', getToken.id)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
totpTokenRequired: async function (user) {
|
totpTokenRequired: async function (user) {
|
||||||
let getToken = await models.TotpToken.query().where('user_id', user.id)
|
const getToken = await models.TotpToken.query().where('user_id', user.id)
|
||||||
|
|
||||||
if (!getToken || !getToken.length) return false
|
if (!getToken || !getToken.length) return false
|
||||||
if (getToken[0].activated !== 1) return false
|
if (getToken[0].activated !== 1) return false
|
||||||
@ -286,7 +286,7 @@ const API = {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let login = notp.totp.verify(code, getToken.token, {})
|
const login = notp.totp.verify(code, getToken.token, {})
|
||||||
|
|
||||||
if (login) {
|
if (login) {
|
||||||
if (login.delta !== 0) {
|
if (login.delta !== 0) {
|
||||||
@ -295,7 +295,7 @@ const API = {
|
|||||||
|
|
||||||
if (getToken.activated !== 1) {
|
if (getToken.activated !== 1) {
|
||||||
// TODO: Send an email including the recovery code to the user
|
// 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
|
return true
|
||||||
@ -305,7 +305,7 @@ const API = {
|
|||||||
},
|
},
|
||||||
purgeTotp: async function (user, password) {
|
purgeTotp: async function (user, password) {
|
||||||
user = await API.User.ensureObject(user, ['password'])
|
user = await API.User.ensureObject(user, ['password'])
|
||||||
let pwmatch = await API.User.Login.password(user, password)
|
const pwmatch = await API.User.Login.password(user, password)
|
||||||
if (!pwmatch) return false
|
if (!pwmatch) return false
|
||||||
|
|
||||||
// TODO: Inform user via email
|
// TODO: Inform user via email
|
||||||
@ -320,12 +320,12 @@ const API = {
|
|||||||
if (!user.password || user.password === '') return null
|
if (!user.password || user.password === '') return null
|
||||||
|
|
||||||
// Get existing tokens for the user and delete them if found
|
// Get existing tokens for the user and delete them if found
|
||||||
let getToken = await models.TotpToken.query().where('user_id', user.id)
|
const getToken = await models.TotpToken.query().where('user_id', user.id)
|
||||||
if (getToken && getToken.length) {
|
if (getToken && getToken.length) {
|
||||||
await models.TotpToken.query().delete().where('user_id', user.id)
|
await models.TotpToken.query().delete().where('user_id', user.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
let newToken = {
|
const newToken = {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
token: API.Hash(16),
|
token: API.Hash(16),
|
||||||
recovery_code: API.Hash(8),
|
recovery_code: API.Hash(8),
|
||||||
@ -333,44 +333,44 @@ const API = {
|
|||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
let hashed = base32.encode(newToken.token)
|
const hashed = base32.encode(newToken.token)
|
||||||
let domain = 'icynet.eu'
|
const domain = 'icynet.eu'
|
||||||
|
|
||||||
await models.TotpToken.query().insert(newToken)
|
await models.TotpToken.query().insert(newToken)
|
||||||
|
|
||||||
let uri = encodeURIComponent('otpauth://totp/' + user.username + '@' + domain + '?secret=' + hashed)
|
const uri = encodeURIComponent('otpauth://totp/' + user.username + '@' + domain + '?secret=' + hashed)
|
||||||
|
|
||||||
return uri
|
return uri
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Register: {
|
Register: {
|
||||||
hashPassword: async function (password) {
|
hashPassword: async function (password) {
|
||||||
return bcryptTask({task: 'hash', password: password})
|
return bcryptTask({ task: 'hash', password: password })
|
||||||
},
|
},
|
||||||
validateEmail: (email) => {
|
validateEmail: (email) => {
|
||||||
return emailRe.test(email)
|
return emailRe.test(email)
|
||||||
},
|
},
|
||||||
newAccount: async function (regdata) {
|
newAccount: async function (regdata) {
|
||||||
let email = config.email && config.email.enabled
|
const email = config.email && config.email.enabled
|
||||||
let data = Object.assign(regdata, {
|
const data = Object.assign(regdata, {
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
updated_at: new Date(),
|
updated_at: new Date(),
|
||||||
uuid: uuidV1(),
|
uuid: uuidV1(),
|
||||||
activated: email ? 0 : 1
|
activated: email ? 0 : 1
|
||||||
})
|
})
|
||||||
|
|
||||||
let userTest = await API.User.get(regdata.username)
|
const userTest = await API.User.get(regdata.username)
|
||||||
if (userTest) {
|
if (userTest) {
|
||||||
throw new Error('This username is already taken!')
|
throw new Error('This username is already taken!')
|
||||||
}
|
}
|
||||||
|
|
||||||
let emailTest = await API.User.get(regdata.email)
|
const emailTest = await API.User.get(regdata.email)
|
||||||
if (emailTest) {
|
if (emailTest) {
|
||||||
throw new Error('This email address is already registered!')
|
throw new Error('This email address is already registered!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
let user = await models.User.query().insert(data)
|
const user = await models.User.query().insert(data)
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
await API.User.Register.activationEmail(user, true)
|
await API.User.Register.activationEmail(user, true)
|
||||||
@ -380,7 +380,7 @@ const API = {
|
|||||||
},
|
},
|
||||||
activationEmail: async function (user, deleteOnFail = false) {
|
activationEmail: async function (user, deleteOnFail = false) {
|
||||||
// Activation token
|
// Activation token
|
||||||
let activationToken = API.Hash(16)
|
const activationToken = API.Hash(16)
|
||||||
|
|
||||||
await models.Token.query().insert({
|
await models.Token.query().insert({
|
||||||
expires_at: new Date(Date.now() + 86400000), // 1 day
|
expires_at: new Date(Date.now() + 86400000), // 1 day
|
||||||
@ -393,7 +393,7 @@ const API = {
|
|||||||
|
|
||||||
// Send Activation Email
|
// Send Activation Email
|
||||||
try {
|
try {
|
||||||
let em = await emailer.pushMail('activate', user.email, {
|
const em = await emailer.pushMail('activate', user.email, {
|
||||||
domain: config.server.domain,
|
domain: config.server.domain,
|
||||||
display_name: user.display_name,
|
display_name: user.display_name,
|
||||||
activation_token: activationToken
|
activation_token: activationToken
|
||||||
@ -415,20 +415,20 @@ const API = {
|
|||||||
},
|
},
|
||||||
Reset: {
|
Reset: {
|
||||||
reset: async function (email, passRequired = true) {
|
reset: async function (email, passRequired = true) {
|
||||||
let emailEnabled = config.email && config.email.enabled
|
const emailEnabled = config.email && config.email.enabled
|
||||||
|
|
||||||
if (!emailEnabled) throw new Error('Cannot reset password.')
|
if (!emailEnabled) throw new Error('Cannot reset password.')
|
||||||
|
|
||||||
let user = await API.User.get(email)
|
const user = await API.User.get(email)
|
||||||
if (!user) throw new Error('This email address does not match any user in our database.')
|
if (!user) throw new Error('This email address does not match any user in our database.')
|
||||||
if (!user.password && passRequired) throw new Error('The user associated with this email address has used an external website to log in, thus the password cannot be reset.')
|
if (!user.password && passRequired) throw new Error('The user associated with this email address has used an external website to log in, thus the password cannot be reset.')
|
||||||
|
|
||||||
let recentTokens = await models.Token.query().where('user_id', user.id).andWhere('expires_at', '>', new Date()).andWhere('type', 2)
|
const recentTokens = await models.Token.query().where('user_id', user.id).andWhere('expires_at', '>', new Date()).andWhere('type', 2)
|
||||||
if (recentTokens.length >= 2) {
|
if (recentTokens.length >= 2) {
|
||||||
throw new Error('You\'ve made too many reset requests recently. Please slow down.')
|
throw new Error('You\'ve made too many reset requests recently. Please slow down.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let resetToken = API.Hash(16)
|
const resetToken = API.Hash(16)
|
||||||
await models.Token.query().insert({
|
await models.Token.query().insert({
|
||||||
expires_at: new Date(Date.now() + 86400000), // 1 day
|
expires_at: new Date(Date.now() + 86400000), // 1 day
|
||||||
token: resetToken,
|
token: resetToken,
|
||||||
@ -440,7 +440,7 @@ const API = {
|
|||||||
console.debug('Reset token:', resetToken)
|
console.debug('Reset token:', resetToken)
|
||||||
if (email) {
|
if (email) {
|
||||||
try {
|
try {
|
||||||
let em = await emailer.pushMail('reset_password', user.email, {
|
const em = await emailer.pushMail('reset_password', user.email, {
|
||||||
domain: config.server.domain,
|
domain: config.server.domain,
|
||||||
display_name: user.display_name,
|
display_name: user.display_name,
|
||||||
reset_token: resetToken
|
reset_token: resetToken
|
||||||
@ -463,15 +463,15 @@ const API = {
|
|||||||
|
|
||||||
if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return null
|
if (getToken.expires_at && new Date(getToken.expires_at).getTime() < Date.now()) return null
|
||||||
|
|
||||||
let user = await API.User.get(getToken.user_id)
|
const user = await API.User.get(getToken.user_id)
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
|
|
||||||
return user
|
return user
|
||||||
},
|
},
|
||||||
changePassword: async function (user, password, token) {
|
changePassword: async function (user, password, token) {
|
||||||
let hashed = await API.User.Register.hashPassword(password)
|
const hashed = await API.User.Register.hashPassword(password)
|
||||||
|
|
||||||
await models.User.query().patchAndFetchById(user.id, {password: hashed, updated_at: new Date()})
|
await models.User.query().patchAndFetchById(user.id, { password: hashed, updated_at: new Date() })
|
||||||
await models.Token.query().delete().where('token', token)
|
await models.Token.query().delete().where('token', token)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -480,18 +480,18 @@ const API = {
|
|||||||
OAuth2: {
|
OAuth2: {
|
||||||
getUserAuthorizations: async function (user) {
|
getUserAuthorizations: async function (user) {
|
||||||
user = await API.User.ensureObject(user)
|
user = await API.User.ensureObject(user)
|
||||||
let auths = await models.OAuth2AuthorizedClient.query().where('user_id', user.id)
|
const auths = await models.OAuth2AuthorizedClient.query().where('user_id', user.id)
|
||||||
|
|
||||||
let nicelist = []
|
const nicelist = []
|
||||||
|
|
||||||
for (let i in auths) {
|
for (const i in auths) {
|
||||||
let auth = auths[i]
|
const auth = auths[i]
|
||||||
let client = await models.OAuth2Client.query().where('id', auth.client_id)
|
let client = await models.OAuth2Client.query().where('id', auth.client_id)
|
||||||
|
|
||||||
if (!client.length) continue
|
if (!client.length) continue
|
||||||
client = client[0]
|
client = client[0]
|
||||||
|
|
||||||
let obj = {
|
const obj = {
|
||||||
id: client.id,
|
id: client.id,
|
||||||
title: client.title,
|
title: client.title,
|
||||||
description: client.description,
|
description: client.description,
|
||||||
@ -508,13 +508,13 @@ const API = {
|
|||||||
},
|
},
|
||||||
removeUserAuthorization: async function (user, clientId) {
|
removeUserAuthorization: async function (user, clientId) {
|
||||||
user = await API.User.ensureObject(user)
|
user = await API.User.ensureObject(user)
|
||||||
let auth = await models.OAuth2AuthorizedClient.query().where('user_id', user.id).andWhere('client_id', clientId)
|
const auth = await models.OAuth2AuthorizedClient.query().where('user_id', user.id).andWhere('client_id', clientId)
|
||||||
if (!auth.length) return false
|
if (!auth.length) return false
|
||||||
|
|
||||||
await models.OAuth2AccessToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id)
|
await models.OAuth2AccessToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id)
|
||||||
await models.OAuth2RefreshToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id)
|
await models.OAuth2RefreshToken.query().delete().where('client_id', clientId).andWhere('user_id', user.id)
|
||||||
|
|
||||||
for (let i in auth) {
|
for (const i in auth) {
|
||||||
await models.OAuth2AuthorizedClient.query().delete().where('id', auth[i].id)
|
await models.OAuth2AuthorizedClient.query().delete().where('id', auth[i].id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,11 +524,11 @@ const API = {
|
|||||||
},
|
},
|
||||||
Payment: {
|
Payment: {
|
||||||
handleIPN: async function (body) {
|
handleIPN: async function (body) {
|
||||||
let sandboxed = body.test_ipn === '1'
|
const sandboxed = body.test_ipn === '1'
|
||||||
let url = 'https://ipnpb.' + (sandboxed ? 'sandbox.' : '') + 'paypal.com/cgi-bin/webscr'
|
const url = 'https://ipnpb.' + (sandboxed ? 'sandbox.' : '') + 'paypal.com/cgi-bin/webscr'
|
||||||
|
|
||||||
console.debug('Incoming payment')
|
console.debug('Incoming payment')
|
||||||
let verification = await http.POST(url, {}, Object.assign({
|
const verification = await http.POST(url, {}, Object.assign({
|
||||||
cmd: '_notify-validate'
|
cmd: '_notify-validate'
|
||||||
}, body))
|
}, body))
|
||||||
|
|
||||||
@ -551,15 +551,15 @@ const API = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let user
|
let user
|
||||||
let source = []
|
const source = []
|
||||||
if (sandboxed) {
|
if (sandboxed) {
|
||||||
source.push('virtual')
|
source.push('virtual')
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add hooks
|
// TODO: add hooks
|
||||||
let custom = body.custom.split(',')
|
const custom = body.custom.split(',')
|
||||||
for (let i in custom) {
|
for (const i in custom) {
|
||||||
let str = custom[i]
|
const str = custom[i]
|
||||||
if (str.indexOf('userid:') === 0) {
|
if (str.indexOf('userid:') === 0) {
|
||||||
body.user_id = parseInt(str.split(':')[1])
|
body.user_id = parseInt(str.split(':')[1])
|
||||||
} else if (str.indexOf('mcu:') === 0) {
|
} else if (str.indexOf('mcu:') === 0) {
|
||||||
@ -573,7 +573,7 @@ const API = {
|
|||||||
user = await API.User.get(body.payer_email)
|
user = await API.User.get(body.payer_email)
|
||||||
}
|
}
|
||||||
|
|
||||||
let donation = {
|
const donation = {
|
||||||
user_id: user ? user.id : null,
|
user_id: user ? user.id : null,
|
||||||
amount: (body.mc_gross || body.payment_gross || 'Unknown') + ' ' + (body.mc_currency || 'EUR'),
|
amount: (body.mc_gross || body.payment_gross || 'Unknown') + ' ' + (body.mc_currency || 'EUR'),
|
||||||
source: source.join(','),
|
source: source.join(','),
|
||||||
@ -589,21 +589,21 @@ const API = {
|
|||||||
userContributions: async function (user) {
|
userContributions: async function (user) {
|
||||||
user = await API.User.ensureObject(user)
|
user = await API.User.ensureObject(user)
|
||||||
|
|
||||||
let dbq = await models.Donation.query().orderBy('created_at', 'desc').where('user_id', user.id)
|
const dbq = await models.Donation.query().orderBy('created_at', 'desc').where('user_id', user.id)
|
||||||
let contribs = []
|
const contribs = []
|
||||||
|
|
||||||
for (let i in dbq) {
|
for (const i in dbq) {
|
||||||
contribs.push(await cleanUpDonation(dbq[i]))
|
contribs.push(await cleanUpDonation(dbq[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return contribs
|
return contribs
|
||||||
},
|
},
|
||||||
allContributions: async function (count, mcOnly, timeframe = 0) {
|
allContributions: async function (count, mcOnly, timeframe = 0) {
|
||||||
let dbq = await models.Donation.query().orderBy('created_at', 'desc').limit(count)
|
const dbq = await models.Donation.query().orderBy('created_at', 'desc').limit(count)
|
||||||
let contribs = []
|
const contribs = []
|
||||||
|
|
||||||
for (let i in dbq) {
|
for (const i in dbq) {
|
||||||
let obj = await cleanUpDonation(dbq[i], mcOnly, timeframe)
|
const obj = await cleanUpDonation(dbq[i], mcOnly, timeframe)
|
||||||
if (!obj) continue
|
if (!obj) continue
|
||||||
contribs.push(obj)
|
contribs.push(obj)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Model} from '../../scripts/load-database'
|
import { Model } from '../../scripts/load-database'
|
||||||
|
|
||||||
class User extends Model {
|
class User extends Model {
|
||||||
static get tableName () {
|
static get tableName () {
|
||||||
|
@ -13,8 +13,8 @@ function slugify (title) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function cleanArticle (entry, shortenContent = false) {
|
async function cleanArticle (entry, shortenContent = false) {
|
||||||
let poster = await API.User.get(entry.user_id)
|
const poster = await API.User.get(entry.user_id)
|
||||||
let article = {
|
const article = {
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
slug: slugify(entry.title),
|
slug: slugify(entry.title),
|
||||||
title: entry.title,
|
title: entry.title,
|
||||||
@ -40,13 +40,13 @@ async function cleanArticle (entry, shortenContent = false) {
|
|||||||
const News = {
|
const News = {
|
||||||
preview: async () => {
|
preview: async () => {
|
||||||
// Fetch 3 latest stories
|
// Fetch 3 latest stories
|
||||||
let news = await Models.News.query().orderBy('created_at', 'desc').limit(3)
|
const news = await Models.News.query().orderBy('created_at', 'desc').limit(3)
|
||||||
|
|
||||||
if (!news.length) return []
|
if (!news.length) return []
|
||||||
|
|
||||||
let articles = []
|
const articles = []
|
||||||
for (let i in news) {
|
for (const i in news) {
|
||||||
let entry = news[i]
|
const entry = news[i]
|
||||||
articles.push(await cleanArticle(entry))
|
articles.push(await cleanArticle(entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,17 +56,17 @@ const News = {
|
|||||||
let count = await Models.News.query().count('id as ids')
|
let count = await Models.News.query().count('id as ids')
|
||||||
if (page < 1) page = 1
|
if (page < 1) page = 1
|
||||||
|
|
||||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
if (!count.length || !count[0].ids || isNaN(page)) {
|
||||||
return { error: 'No news' }
|
return { error: 'No news' }
|
||||||
}
|
}
|
||||||
|
|
||||||
count = count[0].ids
|
count = count[0].ids
|
||||||
let paginated = API.Pagination(perPage, parseInt(count), page)
|
const paginated = API.Pagination(perPage, parseInt(count), page)
|
||||||
let news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
|
const news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
|
||||||
|
|
||||||
let articles = []
|
const articles = []
|
||||||
for (let i in news) {
|
for (const i in news) {
|
||||||
let entry = news[i]
|
const entry = news[i]
|
||||||
|
|
||||||
articles.push(await cleanArticle(entry))
|
articles.push(await cleanArticle(entry))
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ const News = {
|
|||||||
return cleanArticle(article)
|
return cleanArticle(article)
|
||||||
},
|
},
|
||||||
compose: async (user, body) => {
|
compose: async (user, body) => {
|
||||||
let article = {
|
const article = {
|
||||||
title: body.title,
|
title: body.title,
|
||||||
content: body.content,
|
content: body.content,
|
||||||
tags: body.tags || '',
|
tags: body.tags || '',
|
||||||
@ -93,28 +93,28 @@ const News = {
|
|||||||
updated_at: new Date()
|
updated_at: new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await Models.News.query().insert(article)
|
const result = await Models.News.query().insert(article)
|
||||||
result.slug = slugify(result.title)
|
result.slug = slugify(result.title)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
edit: async (id, body) => {
|
edit: async (id, body) => {
|
||||||
let patch = {
|
const patch = {
|
||||||
content: body.content,
|
content: body.content,
|
||||||
updated_at: new Date()
|
updated_at: new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await Models.News.query().patchAndFetchById(id, patch)
|
const result = await Models.News.query().patchAndFetchById(id, patch)
|
||||||
if (!result) throw new Error('Something went wrong.')
|
if (!result) throw new Error('Something went wrong.')
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
generateFeed: async () => {
|
generateFeed: async () => {
|
||||||
if (feed && new Date(feed.options.updated).getTime() > Date.now() - 3600000) return feed // Update feed hourly
|
if (feed && new Date(feed.options.updated).getTime() > Date.now() - 3600000) return feed // Update feed hourly
|
||||||
|
|
||||||
let posts = await Models.News.query().orderBy('created_at', 'desc').limit(perPage)
|
const posts = await Models.News.query().orderBy('created_at', 'desc').limit(perPage)
|
||||||
let cleanNews = []
|
const cleanNews = []
|
||||||
|
|
||||||
for (let i in posts) {
|
for (const i in posts) {
|
||||||
cleanNews.push(await cleanArticle(posts[i]))
|
cleanNews.push(await cleanArticle(posts[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,8 +138,8 @@ const News = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let i in cleanNews) {
|
for (const i in cleanNews) {
|
||||||
let post = cleanNews[i]
|
const post = cleanNews[i]
|
||||||
|
|
||||||
feed.addItem({
|
feed.addItem({
|
||||||
title: post.title,
|
title: post.title,
|
||||||
|
@ -52,7 +52,7 @@ module.exports = wrap(async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
console.debug('Parameter response_type is', responseType)
|
console.debug('Parameter response_type is', responseType)
|
||||||
|
|
||||||
let client = await req.oauth2.model.client.fetchById(clientId)
|
const client = await req.oauth2.model.client.fetchById(clientId)
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return response.error(req, res, new error.InvalidClient('Client not found'), redirectUri)
|
return response.error(req, res, new error.InvalidClient('Client not found'), redirectUri)
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (createAllowFuture) {
|
if (createAllowFuture) {
|
||||||
if (!req.body || (typeof req.body['decision']) === 'undefined') {
|
if (!req.body || (typeof req.body.decision) === 'undefined') {
|
||||||
throw new error.InvalidRequest('No decision parameter passed')
|
throw new error.InvalidRequest('No decision parameter passed')
|
||||||
} else if (req.body['decision'] === '0') {
|
} else if (req.body.decision === '0') {
|
||||||
throw new error.AccessDenied('User denied access to the resource')
|
throw new error.AccessDenied('User denied access to the resource')
|
||||||
} else {
|
} else {
|
||||||
console.debug('Decision check passed')
|
console.debug('Decision check passed')
|
||||||
|
@ -9,9 +9,9 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (createAllowFuture) {
|
if (createAllowFuture) {
|
||||||
if (!req.body || (typeof req.body['decision']) === 'undefined') {
|
if (!req.body || (typeof req.body.decision) === 'undefined') {
|
||||||
throw new error.InvalidRequest('No decision parameter passed')
|
throw new error.InvalidRequest('No decision parameter passed')
|
||||||
} else if (req.body['decision'] === '0') {
|
} else if (req.body.decision === '0') {
|
||||||
throw new error.AccessDenied('User denied access to the resource')
|
throw new error.AccessDenied('User denied access to the resource')
|
||||||
} else {
|
} else {
|
||||||
console.debug('Decision check passed')
|
console.debug('Decision check passed')
|
||||||
|
@ -38,13 +38,13 @@ module.exports = wrap(async function (req, res) {
|
|||||||
return response.error(req, res, new error.InvalidRequest('Token not provided in request body'))
|
return response.error(req, res, new error.InvalidRequest('Token not provided in request body'))
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = await req.oauth2.model.accessToken.fetchByToken(req.body.token)
|
const token = await req.oauth2.model.accessToken.fetchByToken(req.body.token)
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return response.error(req, res, new error.InvalidRequest('Token does not exist'))
|
return response.error(req, res, new error.InvalidRequest('Token does not exist'))
|
||||||
}
|
}
|
||||||
|
|
||||||
let ttl = req.oauth2.model.accessToken.getTTL(token)
|
const ttl = req.oauth2.model.accessToken.getTTL(token)
|
||||||
let resObj = {
|
const resObj = {
|
||||||
token_type: 'bearer',
|
token_type: 'bearer',
|
||||||
token: token.token,
|
token: token.token,
|
||||||
expires_in: Math.floor(ttl / 1000)
|
expires_in: Math.floor(ttl / 1000)
|
||||||
|
@ -43,13 +43,13 @@ module.exports = wrap(async (req, res) => {
|
|||||||
grantType = req.body.grant_type
|
grantType = req.body.grant_type
|
||||||
console.debug('Parameter grant_type is', grantType)
|
console.debug('Parameter grant_type is', grantType)
|
||||||
|
|
||||||
let client = await req.oauth2.model.client.fetchById(clientId)
|
const client = await req.oauth2.model.client.fetchById(clientId)
|
||||||
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return response.error(req, res, new error.InvalidClient('Client not found'))
|
return response.error(req, res, new error.InvalidClient('Client not found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
let valid = req.oauth2.model.client.checkSecret(client, clientSecret)
|
const valid = req.oauth2.model.client.checkSecret(client, clientSecret)
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return response.error(req, res, new error.UnauthorizedClient('Invalid client secret'))
|
return response.error(req, res, new error.UnauthorizedClient('Invalid client secret'))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import error from '../../error'
|
import error from '../../error'
|
||||||
|
|
||||||
module.exports = async (oauth2, client, providedCode, redirectUri) => {
|
module.exports = async (oauth2, client, providedCode, redirectUri) => {
|
||||||
let respObj = {
|
const respObj = {
|
||||||
token_type: 'bearer'
|
token_type: 'bearer'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import error from '../../error'
|
|||||||
module.exports = async (oauth2, client, wantScope) => {
|
module.exports = async (oauth2, client, wantScope) => {
|
||||||
let scope = null
|
let scope = null
|
||||||
|
|
||||||
let resObj = {
|
const resObj = {
|
||||||
token_type: 'bearer'
|
token_type: 'bearer'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import error from '../../error'
|
|||||||
|
|
||||||
module.exports = async (oauth2, client, username, password, scope) => {
|
module.exports = async (oauth2, client, username, password, scope) => {
|
||||||
let user = null
|
let user = null
|
||||||
let resObj = {
|
const resObj = {
|
||||||
token_type: 'bearer'
|
token_type: 'bearer'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ module.exports = async (oauth2, client, username, password, scope) => {
|
|||||||
throw new error.InvalidClient('User not found')
|
throw new error.InvalidClient('User not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
let valid = await oauth2.model.user.checkPassword(user, password)
|
const valid = await oauth2.model.user.checkPassword(user, password)
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
throw new error.InvalidClient('Wrong password')
|
throw new error.InvalidClient('Wrong password')
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ module.exports = async (oauth2, client, pRefreshToken, scope) => {
|
|||||||
let refreshToken = null
|
let refreshToken = null
|
||||||
let accessToken = null
|
let accessToken = null
|
||||||
|
|
||||||
let resObj = {
|
const resObj = {
|
||||||
token_type: 'bearer'
|
token_type: 'bearer'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,18 +22,18 @@ const middleware = wrap(async function (req, res, next) {
|
|||||||
|
|
||||||
token = pieces[1]
|
token = pieces[1]
|
||||||
console.debug('Bearer token parsed from authorization header:', token)
|
console.debug('Bearer token parsed from authorization header:', token)
|
||||||
} else if (req.query && req.query['access_token']) {
|
} else if (req.query && req.query.access_token) {
|
||||||
token = req.query['access_token']
|
token = req.query.access_token
|
||||||
console.debug('Bearer token parsed from query params:', token)
|
console.debug('Bearer token parsed from query params:', token)
|
||||||
} else if (req.body && req.body['access_token']) {
|
} else if (req.body && req.body.access_token) {
|
||||||
token = req.body['access_token']
|
token = req.body.access_token
|
||||||
console.debug('Bearer token parsed from body params:', token)
|
console.debug('Bearer token parsed from body params:', token)
|
||||||
} else {
|
} else {
|
||||||
return response.error(req, res, new error.AccessDenied('Bearer token not found'))
|
return response.error(req, res, new error.AccessDenied('Bearer token not found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to fetch access token
|
// Try to fetch access token
|
||||||
let object = await req.oauth2.model.accessToken.fetchByToken(token)
|
const object = await req.oauth2.model.accessToken.fetchByToken(token)
|
||||||
if (!object) {
|
if (!object) {
|
||||||
response.error(req, res, new error.Forbidden('Token not found or has expired'))
|
response.error(req, res, new error.Forbidden('Token not found or has expired'))
|
||||||
} else if (!req.oauth2.model.accessToken.checkTTL(object)) {
|
} else if (!req.oauth2.model.accessToken.checkTTL(object)) {
|
||||||
|
@ -32,7 +32,7 @@ const OAuthDB = {
|
|||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await Models.OAuth2AccessToken.query().insert(obj)
|
const res = await Models.OAuth2AccessToken.query().insert(obj)
|
||||||
if (!res) return null
|
if (!res) return null
|
||||||
|
|
||||||
return res.token
|
return res.token
|
||||||
@ -54,7 +54,7 @@ const OAuthDB = {
|
|||||||
return (object.expires_at - Date.now())
|
return (object.expires_at - Date.now())
|
||||||
},
|
},
|
||||||
fetchByUserIdClientId: async (userId, clientId) => {
|
fetchByUserIdClientId: async (userId, clientId) => {
|
||||||
let tkn = await Models.OAuth2AccessToken.query().where('user_id', userId).andWhere('client_id', clientId)
|
const tkn = await Models.OAuth2AccessToken.query().where('user_id', userId).andWhere('client_id', clientId)
|
||||||
|
|
||||||
if (!tkn.length) return null
|
if (!tkn.length) return null
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ const OAuthDB = {
|
|||||||
return client.id
|
return client.id
|
||||||
},
|
},
|
||||||
fetchById: async (id) => {
|
fetchById: async (id) => {
|
||||||
let client = await Models.OAuth2Client.query().where('id', id)
|
const client = await Models.OAuth2Client.query().where('id', id)
|
||||||
|
|
||||||
if (!client.length) return null
|
if (!client.length) return null
|
||||||
|
|
||||||
@ -110,9 +110,9 @@ const OAuthDB = {
|
|||||||
scope = OAuthDB.client.transformScope(scope)
|
scope = OAuthDB.client.transformScope(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientScopes = client.scope.split(' ')
|
const clientScopes = client.scope.split(' ')
|
||||||
|
|
||||||
for (let i in scope) {
|
for (const i in scope) {
|
||||||
if (clientScopes.indexOf(scope[i]) === -1) {
|
if (clientScopes.indexOf(scope[i]) === -1) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ const OAuthDB = {
|
|||||||
checkPassword: Users.User.Login.password,
|
checkPassword: Users.User.Login.password,
|
||||||
fetchFromRequest: async (req) => {
|
fetchFromRequest: async (req) => {
|
||||||
if (!req.session.user) return null
|
if (!req.session.user) return null
|
||||||
let banStatus = await Users.User.getBanStatus(req.session.user.id)
|
const banStatus = await Users.User.getBanStatus(req.session.user.id)
|
||||||
|
|
||||||
if (banStatus.length) {
|
if (banStatus.length) {
|
||||||
delete req.session.user
|
delete req.session.user
|
||||||
@ -242,11 +242,11 @@ const OAuthDB = {
|
|||||||
scope = scope.join(' ')
|
scope = scope.join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
let authorized = await Models.OAuth2AuthorizedClient.query().where('user_id', userId)
|
const authorized = await Models.OAuth2AuthorizedClient.query().where('user_id', userId)
|
||||||
if (!authorized.length) return false
|
if (!authorized.length) return false
|
||||||
|
|
||||||
let correct = false
|
let correct = false
|
||||||
for (let i in authorized) {
|
for (const i in authorized) {
|
||||||
if (authorized[i].client_id === clientId) {
|
if (authorized[i].client_id === clientId) {
|
||||||
correct = authorized[i]
|
correct = authorized[i]
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ const OAuthDB = {
|
|||||||
scope = scope.join(' ')
|
scope = scope.join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
let obj = {
|
const obj = {
|
||||||
scope,
|
scope,
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
|
@ -24,7 +24,7 @@ module.exports.error = function (req, res, err, redirectUri) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (redirectUri) {
|
if (redirectUri) {
|
||||||
let obj = {
|
const obj = {
|
||||||
error: err.code,
|
error: err.code,
|
||||||
error_description: err.message
|
error_description: err.message
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ module.exports.error = function (req, res, err, redirectUri) {
|
|||||||
redirectUri += '?' + query.stringify(obj)
|
redirectUri += '?' + query.stringify(obj)
|
||||||
redirect(req, res, redirectUri)
|
redirect(req, res, redirectUri)
|
||||||
} else {
|
} 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 () {
|
function spawnWorkers () {
|
||||||
let workerCount = config.server.workers === 0 ? cpuCount : config.server.workers
|
const workerCount = config.server.workers === 0 ? cpuCount : config.server.workers
|
||||||
console.log('Spinning up %d worker process%s', workerCount, (workerCount !== 1 ? 'es' : ''))
|
console.log('Spinning up %d worker process%s', workerCount, (workerCount !== 1 ? 'es' : ''))
|
||||||
|
|
||||||
for (let i = 0; i < workerCount; i++) {
|
for (let i = 0; i < workerCount; i++) {
|
||||||
@ -52,7 +52,7 @@ function watchFileTree () {
|
|||||||
|
|
||||||
console.log('[WatchTask] %s changed, restarting workers', f)
|
console.log('[WatchTask] %s changed, restarting workers', f)
|
||||||
if (workers.length) {
|
if (workers.length) {
|
||||||
for (let i in workers) {
|
for (const i in workers) {
|
||||||
workers[i].send('stop')
|
workers[i].send('stop')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,11 +97,11 @@ cluster.setupMaster({
|
|||||||
})
|
})
|
||||||
|
|
||||||
cluster.on('exit', (worker, code, signal) => {
|
cluster.on('exit', (worker, code, signal) => {
|
||||||
let extra = ((code || '') + ' ' + (signal || '')).trim()
|
const extra = ((code || '') + ' ' + (signal || '')).trim()
|
||||||
|
|
||||||
console.error('Worker process %d exited %s', worker.process.pid, (extra ? '(' + extra + ')' : ''))
|
console.error('Worker process %d exited %s', worker.process.pid, (extra ? '(' + extra + ')' : ''))
|
||||||
|
|
||||||
let index = workers.indexOf(worker)
|
const index = workers.indexOf(worker)
|
||||||
|
|
||||||
if (index !== -1) workers.splice(index, 1)
|
if (index !== -1) workers.splice(index, 1)
|
||||||
if (code === 0) return
|
if (code === 0) return
|
||||||
@ -119,7 +119,7 @@ process.on('SIGUSR2', () => {
|
|||||||
console.log('Received SIGUSR2. Restarting workers.')
|
console.log('Received SIGUSR2. Restarting workers.')
|
||||||
|
|
||||||
if (workers.length) {
|
if (workers.length) {
|
||||||
for (let i in workers) {
|
for (const i in workers) {
|
||||||
workers[i].send('stop')
|
workers[i].send('stop')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import ensureLogin from '../../scripts/ensureLogin'
|
|||||||
import wrap from '../../scripts/asyncRoute'
|
import wrap from '../../scripts/asyncRoute'
|
||||||
import API from '../api/admin'
|
import API from '../api/admin'
|
||||||
import Emailer from '../api/emailer'
|
import Emailer from '../api/emailer'
|
||||||
import {User} from '../api'
|
import { User } from '../api'
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const apiRouter = express.Router()
|
const apiRouter = express.Router()
|
||||||
@ -12,7 +12,7 @@ const apiRouter = express.Router()
|
|||||||
// Check for privilege required to access the admin panel
|
// Check for privilege required to access the admin panel
|
||||||
router.use(ensureLogin, wrap(async (req, res, next) => {
|
router.use(ensureLogin, wrap(async (req, res, next) => {
|
||||||
if (!req.session.privilege) {
|
if (!req.session.privilege) {
|
||||||
let u = await User.get(req.session.user)
|
const u = await User.get(req.session.user)
|
||||||
req.session.privilege = u.nw_privilege
|
req.session.privilege = u.nw_privilege
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,10 +31,10 @@ router.use(ensureLogin, wrap(async (req, res, next) => {
|
|||||||
|
|
||||||
router.get('/access', (req, res) => {
|
router.get('/access', (req, res) => {
|
||||||
if (!req.session.accesstime || req.session.accesstime < Date.now()) {
|
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
|
// Post password to continue
|
||||||
@ -42,16 +42,16 @@ router.post('/', wrap(async (req, res, next) => {
|
|||||||
if (!req.body.password) return next()
|
if (!req.body.password) return next()
|
||||||
|
|
||||||
if (req.body.csrf !== req.session.csrf) {
|
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()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
let passReady = await User.Login.password(req.session.user, req.body.password)
|
const passReady = await User.Login.password(req.session.user, req.body.password)
|
||||||
if (passReady) {
|
if (passReady) {
|
||||||
req.session.accesstime = Date.now() + 600000 // 10 minutes
|
req.session.accesstime = Date.now() + 600000 // 10 minutes
|
||||||
return res.redirect('/admin')
|
return res.redirect('/admin')
|
||||||
} else {
|
} else {
|
||||||
req.flash('message', {error: true, text: 'Invalid password'})
|
req.flash('message', { error: true, text: 'Invalid password' })
|
||||||
}
|
}
|
||||||
|
|
||||||
next()
|
next()
|
||||||
@ -68,7 +68,7 @@ router.use(wrap(async (req, res, next) => {
|
|||||||
delete req.session.accesstime
|
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
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let users = await API.getAllUsers(page, req.session.user.id)
|
const users = await API.getAllUsers(page, req.session.user.id)
|
||||||
res.jsonp(users)
|
res.jsonp(users)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.get('/user/:id', wrap(async (req, res) => {
|
apiRouter.get('/user/:id', wrap(async (req, res) => {
|
||||||
let id = parseInt(req.params.id)
|
const id = parseInt(req.params.id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid number')
|
throw new Error('Invalid number')
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ apiRouter.get('/user/:id', wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/user', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/user', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.user_id)
|
const id = parseInt(req.body.user_id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid or missing user ID')
|
throw new Error('Invalid or missing user ID')
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ apiRouter.post('/user', csrfVerify, wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.user_id)
|
const id = parseInt(req.body.user_id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid or missing user ID')
|
throw new Error('Invalid or missing user ID')
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ apiRouter.post('/user/resend_activation', csrfVerify, wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.user_id)
|
const id = parseInt(req.body.user_id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid or missing user ID')
|
throw new Error('Invalid or missing user ID')
|
||||||
}
|
}
|
||||||
@ -149,7 +149,7 @@ apiRouter.post('/user/revoke_totp', csrfVerify, wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.user_id)
|
const id = parseInt(req.body.user_id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid or missing user ID')
|
throw new Error('Invalid or missing user ID')
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ apiRouter.post('/user/reset_password', csrfVerify, wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/user/lock', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/user/lock', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.user_id)
|
const id = parseInt(req.body.user_id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid or missing user ID')
|
throw new Error('Invalid or missing user ID')
|
||||||
}
|
}
|
||||||
@ -170,11 +170,11 @@ const availableScopes = ['uuid', 'email', 'username', 'display_name']
|
|||||||
apiRouter.get('/search/users', wrap(async (req, res) => {
|
apiRouter.get('/search/users', wrap(async (req, res) => {
|
||||||
if (!req.query.terms) throw new Error('Please specify search terms!')
|
if (!req.query.terms) throw new Error('Please specify search terms!')
|
||||||
|
|
||||||
let scopes = []
|
const scopes = []
|
||||||
if (req.query.scopes) {
|
if (req.query.scopes) {
|
||||||
let scq = req.query.scopes.split(',')
|
const scq = req.query.scopes.split(',')
|
||||||
|
|
||||||
for (let i in scq) {
|
for (const i in scq) {
|
||||||
scq[i] = scq[i].trim()
|
scq[i] = scq[i].trim()
|
||||||
|
|
||||||
if (availableScopes.indexOf(scq[i]) !== -1) {
|
if (availableScopes.indexOf(scq[i]) !== -1) {
|
||||||
@ -187,7 +187,7 @@ apiRouter.get('/search/users', wrap(async (req, res) => {
|
|||||||
scopes.push('email')
|
scopes.push('email')
|
||||||
}
|
}
|
||||||
|
|
||||||
let results = await API.searchUsers(req.query.terms, scopes)
|
const results = await API.searchUsers(req.query.terms, scopes)
|
||||||
res.jsonp(results)
|
res.jsonp(results)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -202,17 +202,17 @@ apiRouter.get('/clients', wrap(async (req, res) => {
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let clients = await API.getAllClients(page)
|
const clients = await API.getAllClients(page)
|
||||||
res.jsonp(clients)
|
res.jsonp(clients)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.get('/client/:id', wrap(async (req, res) => {
|
apiRouter.get('/client/:id', wrap(async (req, res) => {
|
||||||
let id = parseInt(req.params.id)
|
const id = parseInt(req.params.id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid number')
|
throw new Error('Invalid number')
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = await API.getClient(id)
|
const client = await API.getClient(id)
|
||||||
|
|
||||||
res.jsonp(client)
|
res.jsonp(client)
|
||||||
}))
|
}))
|
||||||
@ -224,7 +224,7 @@ apiRouter.post('/client/new', csrfVerify, wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
|
|
||||||
if (!id || isNaN(id)) throw new Error('ID missing')
|
if (!id || isNaN(id)) throw new Error('ID missing')
|
||||||
|
|
||||||
@ -234,23 +234,23 @@ apiRouter.post('/client/update', csrfVerify, wrap(async (req, res) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/client/new_secret', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/client/new_secret', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid client ID')
|
throw new Error('Invalid client ID')
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = await API.newSecret(id)
|
const client = await API.newSecret(id)
|
||||||
|
|
||||||
res.jsonp(client)
|
res.jsonp(client)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/client/delete', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/client/delete', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid client ID')
|
throw new Error('Invalid client ID')
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = await API.removeClient(id)
|
const client = await API.removeClient(id)
|
||||||
|
|
||||||
res.jsonp(client)
|
res.jsonp(client)
|
||||||
}))
|
}))
|
||||||
@ -266,17 +266,17 @@ apiRouter.get('/bans', wrap(async (req, res) => {
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let bans = await API.getAllBans(page)
|
const bans = await API.getAllBans(page)
|
||||||
res.jsonp(bans)
|
res.jsonp(bans)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
|
||||||
let id = parseInt(req.body.id)
|
const id = parseInt(req.body.id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
throw new Error('Invalid number')
|
throw new Error('Invalid number')
|
||||||
}
|
}
|
||||||
|
|
||||||
let ban = await API.removeBan(id)
|
const ban = await API.removeBan(id)
|
||||||
|
|
||||||
res.jsonp(ban)
|
res.jsonp(ban)
|
||||||
}))
|
}))
|
||||||
@ -284,7 +284,7 @@ apiRouter.post('/ban/pardon', csrfVerify, wrap(async (req, res) => {
|
|||||||
apiRouter.post('/ban', csrfVerify, wrap(async (req, res) => {
|
apiRouter.post('/ban', csrfVerify, wrap(async (req, res) => {
|
||||||
if (!req.body.user_id) throw new Error('ID missing')
|
if (!req.body.user_id) throw new Error('ID missing')
|
||||||
|
|
||||||
let result = await API.addBan(req.body, req.session.user.id)
|
const result = await API.addBan(req.body, req.session.user.id)
|
||||||
|
|
||||||
res.jsonp(result)
|
res.jsonp(result)
|
||||||
}))
|
}))
|
||||||
@ -299,14 +299,14 @@ apiRouter.post('/email', csrfVerify, wrap(async (req, res) => {
|
|||||||
html: req.body.content
|
html: req.body.content
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await Emailer.sendMail(req.body.email, message)
|
const result = await Emailer.sendMail(req.body.email, message)
|
||||||
|
|
||||||
res.jsonp(result)
|
res.jsonp(result)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
apiRouter.use((err, req, res, next) => {
|
apiRouter.use((err, req, res, next) => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
return res.status(400).jsonp({error: err.message})
|
return res.status(400).jsonp({ error: err.message })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.use('/api', apiRouter)
|
router.use('/api', apiRouter)
|
||||||
|
@ -9,18 +9,18 @@ import Image from '../api/image'
|
|||||||
import News from '../api/news'
|
import News from '../api/news'
|
||||||
import API from '../api'
|
import API from '../api'
|
||||||
|
|
||||||
let router = express.Router()
|
const router = express.Router()
|
||||||
let dev = process.env.NODE_ENV !== 'production'
|
const dev = process.env.NODE_ENV !== 'production'
|
||||||
|
|
||||||
// Restrict API usage
|
// Restrict API usage
|
||||||
let apiLimiter = new RateLimit({
|
const apiLimiter = new RateLimit({
|
||||||
windowMs: 5 * 60 * 1000, // 5 minutes
|
windowMs: 5 * 60 * 1000, // 5 minutes
|
||||||
max: 100,
|
max: 100,
|
||||||
delayMs: 0
|
delayMs: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// Restrict image uploads
|
// Restrict image uploads
|
||||||
let uploadLimiter = new RateLimit({
|
const uploadLimiter = new RateLimit({
|
||||||
windowMs: 60 * 60 * 1000, // 1 hour
|
windowMs: 60 * 60 * 1000, // 1 hour
|
||||||
max: 10,
|
max: 10,
|
||||||
delayMs: 0
|
delayMs: 0
|
||||||
@ -30,11 +30,11 @@ router.use(apiLimiter)
|
|||||||
|
|
||||||
// Turn things like 'key1[key2]': 'value' into key1: {key2: 'value'} because facebook
|
// Turn things like 'key1[key2]': 'value' into key1: {key2: 'value'} because facebook
|
||||||
function objectAssembler (insane) {
|
function objectAssembler (insane) {
|
||||||
let object = {}
|
const object = {}
|
||||||
for (let key in insane) {
|
for (const key in insane) {
|
||||||
let value = insane[key]
|
const value = insane[key]
|
||||||
if (key.indexOf('[') !== -1) {
|
if (key.indexOf('[') !== -1) {
|
||||||
let subKey = key.match(/^([\w]+)\[(\w+)\]$/)
|
const subKey = key.match(/^([\w]+)\[(\w+)\]$/)
|
||||||
if (subKey[1] && subKey[2]) {
|
if (subKey[1] && subKey[2]) {
|
||||||
if (!object[subKey[1]]) {
|
if (!object[subKey[1]]) {
|
||||||
object[subKey[1]] = {}
|
object[subKey[1]] = {}
|
||||||
@ -63,7 +63,7 @@ function createSession (req, user) {
|
|||||||
|
|
||||||
// Get either `uuid` or `id` from `:id` parameter
|
// Get either `uuid` or `id` from `:id` parameter
|
||||||
function idParam (req) {
|
function idParam (req) {
|
||||||
let id = req.params.id
|
const id = req.params.id
|
||||||
if (id.length === 36 && id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)) {
|
if (id.length === 36 && id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)) {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
@ -77,17 +77,17 @@ function idParam (req) {
|
|||||||
|
|
||||||
// Either give JSON or make a redirect
|
// Either give JSON or make a redirect
|
||||||
function JsonData (req, res, error, 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
|
// Common middleware for all external account unlinks
|
||||||
function removeAuthMiddleware (identifier) {
|
function removeAuthMiddleware (identifier) {
|
||||||
return wrap(async (req, res) => {
|
return wrap(async (req, res) => {
|
||||||
if (!req.session.user) return res.redirect('/login')
|
if (!req.session.user) return res.redirect('/login')
|
||||||
let done = await APIExtern.Common.remove(req.session.user, identifier)
|
const done = await APIExtern.Common.remove(req.session.user, identifier)
|
||||||
|
|
||||||
if (!done) {
|
if (!done) {
|
||||||
req.flash('message', {error: true, text: 'Unable to unlink social media account'})
|
req.flash('message', { error: true, text: 'Unable to unlink social media account' })
|
||||||
}
|
}
|
||||||
|
|
||||||
res.redirect('/user/manage')
|
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()
|
if (!config.external || !config.external.facebook || !config.external.facebook.client) return next()
|
||||||
|
|
||||||
// Fix up the retarded object Facebook sends us
|
// Fix up the retarded object Facebook sends us
|
||||||
let sane = objectAssembler(req.body)
|
const sane = objectAssembler(req.body)
|
||||||
if (!sane || !sane.authResponse) {
|
if (!sane || !sane.authResponse) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await APIExtern.Facebook.callback(req.session.user, sane.authResponse, req.realIP)
|
const response = await APIExtern.Facebook.callback(req.session.user, sane.authResponse, req.realIP)
|
||||||
|
|
||||||
if (response.banned) {
|
if (response.banned) {
|
||||||
return JsonData(req, res, 'You are banned.')
|
return JsonData(req, res, 'You are banned.')
|
||||||
@ -119,7 +119,7 @@ router.post('/external/facebook/callback', wrap(async (req, res, next) => {
|
|||||||
|
|
||||||
// Create session
|
// Create session
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
let user = response.user
|
const user = response.user
|
||||||
createSession(req, user)
|
createSession(req, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,10 +134,10 @@ router.get('/external/facebook/remove', removeAuthMiddleware('facebook'))
|
|||||||
*/
|
*/
|
||||||
router.get('/external/twitter/login', wrap(async (req, res) => {
|
router.get('/external/twitter/login', wrap(async (req, res) => {
|
||||||
if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/')
|
if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/')
|
||||||
let tokens = await APIExtern.Twitter.getRequestToken()
|
const tokens = await APIExtern.Twitter.getRequestToken()
|
||||||
|
|
||||||
if (tokens.error) {
|
if (tokens.error) {
|
||||||
return res.jsonp({error: tokens.error})
|
return res.jsonp({ error: tokens.error })
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.twitter_auth = tokens
|
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) => {
|
router.get('/external/twitter/callback', wrap(async (req, res) => {
|
||||||
if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/login')
|
if (!config.external || !config.external.twitter || !config.external.twitter.api) return res.redirect('/login')
|
||||||
if (!req.session.twitter_auth) return res.redirect('/login')
|
if (!req.session.twitter_auth) return res.redirect('/login')
|
||||||
let ta = req.session.twitter_auth
|
const ta = req.session.twitter_auth
|
||||||
let uri = '/login'
|
const uri = '/login'
|
||||||
|
|
||||||
if (!req.query.oauth_verifier) {
|
if (!req.query.oauth_verifier) {
|
||||||
req.flash('message', {error: true, text: 'Couldn\'t get a verifier'})
|
req.flash('message', { error: true, text: 'Couldn\'t get a verifier' })
|
||||||
return res.redirect(uri)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
let accessTokens = await APIExtern.Twitter.getAccessTokens(ta.token, ta.token_secret, req.query.oauth_verifier)
|
const accessTokens = await APIExtern.Twitter.getAccessTokens(ta.token, ta.token_secret, req.query.oauth_verifier)
|
||||||
delete req.session.twitter_auth
|
delete req.session.twitter_auth
|
||||||
|
|
||||||
if (accessTokens.error) {
|
if (accessTokens.error) {
|
||||||
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)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await APIExtern.Twitter.callback(req.session.user, accessTokens, req.realIP)
|
const response = await APIExtern.Twitter.callback(req.session.user, accessTokens, req.realIP)
|
||||||
if (response.banned) {
|
if (response.banned) {
|
||||||
return res.render('user/banned', {bans: response.banned, ipban: response.ip})
|
return res.render('user/banned', { bans: response.banned, ipban: response.ip })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
req.flash('message', {error: true, text: response.error})
|
req.flash('message', { error: true, text: response.error })
|
||||||
return res.redirect(uri)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
let user = response.user
|
const user = response.user
|
||||||
createSession(req, user)
|
createSession(req, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('redirect', {url: uri})
|
res.render('redirect', { url: uri })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/external/twitter/remove', removeAuthMiddleware('twitter'))
|
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) => {
|
router.get('/external/discord/login', wrap(async (req, res) => {
|
||||||
if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/')
|
if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/')
|
||||||
|
|
||||||
let infos = APIExtern.Discord.getAuthorizeURL(req)
|
const infos = APIExtern.Discord.getAuthorizeURL(req)
|
||||||
|
|
||||||
res.redirect(infos.url)
|
res.redirect(infos.url)
|
||||||
}))
|
}))
|
||||||
@ -199,44 +199,44 @@ router.get('/external/discord/login', wrap(async (req, res) => {
|
|||||||
router.get('/external/discord/callback', wrap(async (req, res) => {
|
router.get('/external/discord/callback', wrap(async (req, res) => {
|
||||||
if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/login')
|
if (!config.external || !config.external.discord || !config.external.discord.api) return res.redirect('/login')
|
||||||
|
|
||||||
let code = req.query.code
|
const code = req.query.code
|
||||||
let state = req.query.state
|
const state = req.query.state
|
||||||
let uri = '/login'
|
const uri = '/login'
|
||||||
|
|
||||||
if (!code) {
|
if (!code) {
|
||||||
req.flash('message', {error: true, text: 'No authorization.'})
|
req.flash('message', { error: true, text: 'No authorization.' })
|
||||||
return res.redirect(uri)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state || state !== APIExtern.Common.stateGenerator(req)) {
|
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)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete req.session.discord_auth
|
delete req.session.discord_auth
|
||||||
|
|
||||||
let accessToken = await APIExtern.Discord.getAccessToken(code)
|
const accessToken = await APIExtern.Discord.getAccessToken(code)
|
||||||
if (accessToken.error) {
|
if (accessToken.error) {
|
||||||
req.flash('message', {error: true, text: accessToken.error})
|
req.flash('message', { error: true, text: accessToken.error })
|
||||||
return res.redirect(uri)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await APIExtern.Discord.callback(req.session.user, accessToken.accessToken, req.realIP)
|
const response = await APIExtern.Discord.callback(req.session.user, accessToken.accessToken, req.realIP)
|
||||||
if (response.banned) {
|
if (response.banned) {
|
||||||
return res.render('user/banned', {bans: response.banned, ipban: response.ip})
|
return res.render('user/banned', { bans: response.banned, ipban: response.ip })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
req.flash('message', {error: true, text: response.error})
|
req.flash('message', { error: true, text: response.error })
|
||||||
return res.redirect(uri)
|
return res.redirect(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
let user = response.user
|
const user = response.user
|
||||||
createSession(req, user)
|
createSession(req, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('redirect', {url: uri})
|
res.render('redirect', { url: uri })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/external/discord/remove', removeAuthMiddleware('discord'))
|
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')
|
return JsonData(req, res, 'Invalid or missing ID token!', '/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await APIExtern.Google.callback(req.session.user, req.body, req.realIP)
|
const response = await APIExtern.Google.callback(req.session.user, req.body, req.realIP)
|
||||||
if (response.banned) {
|
if (response.banned) {
|
||||||
return JsonData(req, res, 'You are banned.', '/login')
|
return JsonData(req, res, 'You are banned.', '/login')
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ router.post('/external/google/callback', wrap(async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
let user = response.user
|
const user = response.user
|
||||||
createSession(req, user)
|
createSession(req, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,11 +291,11 @@ router.get('/news', (req, res, next) => {
|
|||||||
// Get a page of articles
|
// Get a page of articles
|
||||||
router.get('/news/all/:page', wrap(async (req, res) => {
|
router.get('/news/all/:page', wrap(async (req, res) => {
|
||||||
if (!req.params.page || isNaN(parseInt(req.params.page))) {
|
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)
|
const page = parseInt(req.params.page)
|
||||||
let articles = await News.listNews(page)
|
const articles = await News.listNews(page)
|
||||||
|
|
||||||
res.jsonp(articles)
|
res.jsonp(articles)
|
||||||
}))
|
}))
|
||||||
@ -306,22 +306,22 @@ router.get('/news/all/', (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.post('/news/edit/:id', wrap(async (req, res, next) => {
|
router.post('/news/edit/:id', wrap(async (req, res, next) => {
|
||||||
let id = parseInt(req.params.id)
|
const id = parseInt(req.params.id)
|
||||||
|
|
||||||
if (!req.session.user || req.session.user.privilege < 1) return next()
|
if (!req.session.user || req.session.user.privilege < 1) return next()
|
||||||
|
|
||||||
if (!id || isNaN(id)) {
|
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) {
|
if (!req.body.content) {
|
||||||
return res.status(400).jsonp({error: 'Content is required.'})
|
return res.status(400).jsonp({ error: 'Content is required.' })
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await News.edit(id, req.body)
|
await News.edit(id, req.body)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res.status(400).jsonp({error: e.message})
|
return res.status(400).jsonp({ error: e.message })
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(204).end()
|
res.status(204).end()
|
||||||
@ -330,18 +330,18 @@ router.post('/news/edit/:id', wrap(async (req, res, next) => {
|
|||||||
// Fetch article
|
// Fetch article
|
||||||
router.get('/news/:id', wrap(async (req, res) => {
|
router.get('/news/:id', wrap(async (req, res) => {
|
||||||
if (!req.params.id || isNaN(parseInt(req.params.id))) {
|
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)
|
const id = parseInt(req.params.id)
|
||||||
let article = await News.article(id)
|
const article = await News.article(id)
|
||||||
|
|
||||||
res.jsonp(article)
|
res.jsonp(article)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Preview endpoint
|
// Preview endpoint
|
||||||
router.get('/news', wrap(async (req, res) => {
|
router.get('/news', wrap(async (req, res) => {
|
||||||
let articles = await News.preview()
|
const articles = await News.preview()
|
||||||
|
|
||||||
res.jsonp(articles)
|
res.jsonp(articles)
|
||||||
}))
|
}))
|
||||||
@ -352,11 +352,11 @@ router.get('/news', wrap(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
// Promisify multiparty form parser
|
// Promisify multiparty form parser
|
||||||
async function promiseForm (req) {
|
async function promiseForm (req) {
|
||||||
let form = new multiparty.Form()
|
const form = new multiparty.Form()
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
form.parse(req, async (err, fields, files) => {
|
form.parse(req, async (err, fields, files) => {
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
resolve({fields: fields, files: files})
|
resolve({ fields: fields, files: files })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -364,23 +364,23 @@ async function promiseForm (req) {
|
|||||||
// Upload avatar image
|
// Upload avatar image
|
||||||
router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => {
|
router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
let data = await promiseForm(req)
|
const data = await promiseForm(req)
|
||||||
|
|
||||||
let avatarFile
|
let avatarFile
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let result = await Image.uploadImage(req.session.user.username, data.fields, data.files)
|
const result = await Image.uploadImage(req.session.user.username, data.fields, data.files)
|
||||||
|
|
||||||
avatarFile = await API.User.changeAvatar(req.session.user, result.file)
|
avatarFile = await API.User.changeAvatar(req.session.user, result.file)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res.status(400).jsonp({error: e.message})
|
return res.status(400).jsonp({ error: e.message })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatarFile) {
|
if (avatarFile) {
|
||||||
req.session.user.avatar_file = 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({})
|
res.status(200).jsonp({})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -391,13 +391,13 @@ router.post('/avatar/remove', wrap(async (req, res, next) => {
|
|||||||
await API.User.removeAvatar(req.session.user)
|
await API.User.removeAvatar(req.session.user)
|
||||||
req.session.user.avatar_file = null
|
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
|
// Get latest avatar of logged in user
|
||||||
router.get('/avatar', wrap(async (req, res, next) => {
|
router.get('/avatar', wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
let user = req.session.user
|
const user = req.session.user
|
||||||
|
|
||||||
if (!user.avatar_file) return next()
|
if (!user.avatar_file) return next()
|
||||||
|
|
||||||
@ -407,10 +407,10 @@ router.get('/avatar', wrap(async (req, res, next) => {
|
|||||||
|
|
||||||
// Get latest avatar of user by id
|
// Get latest avatar of user by id
|
||||||
router.get('/avatar/:id', wrap(async (req, res, next) => {
|
router.get('/avatar/:id', wrap(async (req, res, next) => {
|
||||||
let id = idParam(req)
|
const id = idParam(req)
|
||||||
if (!id) return next()
|
if (!id) return next()
|
||||||
|
|
||||||
let user = await API.User.get(id)
|
const user = await API.User.get(id)
|
||||||
|
|
||||||
if (!user || !user.avatar_file) return next()
|
if (!user || !user.avatar_file) return next()
|
||||||
|
|
||||||
@ -420,7 +420,7 @@ router.get('/avatar/:id', wrap(async (req, res, next) => {
|
|||||||
|
|
||||||
router.get('/avatar/gravatar', (req, res, next) => {
|
router.get('/avatar/gravatar', (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
let email = req.session.user.email
|
const email = req.session.user.email
|
||||||
|
|
||||||
res.set('Content-Type', 'text/plain')
|
res.set('Content-Type', 'text/plain')
|
||||||
res.end(Image.gravatarURL(email))
|
res.end(Image.gravatarURL(email))
|
||||||
@ -428,17 +428,17 @@ router.get('/avatar/gravatar', (req, res, next) => {
|
|||||||
|
|
||||||
router.post('/avatar/gravatar', wrap(async (req, res, next) => {
|
router.post('/avatar/gravatar', wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
let user = req.session.user
|
const user = req.session.user
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let gravURL = await Image.downloadImage(Image.gravatarURL(user.email), 'GRAV-' + user.username)
|
const gravURL = await Image.downloadImage(Image.gravatarURL(user.email), 'GRAV-' + user.username)
|
||||||
let file = await API.User.changeAvatar(user, gravURL)
|
const file = await API.User.changeAvatar(user, gravURL)
|
||||||
|
|
||||||
req.session.user.avatar_file = file
|
req.session.user.avatar_file = file
|
||||||
|
|
||||||
req.flash('message', {error: false, text: 'Success!'})
|
req.flash('message', { error: false, text: 'Success!' })
|
||||||
} catch (e) {
|
} 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({})
|
res.jsonp({})
|
||||||
@ -458,7 +458,7 @@ router.use('/avatar', (req, res) => {
|
|||||||
router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
|
router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
|
|
||||||
let list = await API.User.OAuth2.getUserAuthorizations(req.session.user)
|
const list = await API.User.OAuth2.getUserAuthorizations(req.session.user)
|
||||||
if (!list) return next()
|
if (!list) return next()
|
||||||
|
|
||||||
res.jsonp(list)
|
res.jsonp(list)
|
||||||
@ -468,11 +468,11 @@ router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
|
|||||||
router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) => {
|
router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
|
|
||||||
let clientId = parseInt(req.body.client_id)
|
const clientId = parseInt(req.body.client_id)
|
||||||
if (isNaN(clientId)) return res.status(400).jsonp({error: 'Missing Client ID parameter'})
|
if (isNaN(clientId)) return res.status(400).jsonp({ error: 'Missing Client ID parameter' })
|
||||||
|
|
||||||
let done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId)
|
const done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId)
|
||||||
if (!done) return res.status(400).jsonp({error: 'Failed to remove client authorization'})
|
if (!done) return res.status(400).jsonp({ error: 'Failed to remove client authorization' })
|
||||||
|
|
||||||
res.status(204).end()
|
res.status(204).end()
|
||||||
}))
|
}))
|
||||||
@ -483,7 +483,7 @@ router.post('/oauth2/authorized-clients/revoke', wrap(async (req, res, next) =>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
router.post('/paypal/ipn', wrap(async (req, res) => {
|
router.post('/paypal/ipn', wrap(async (req, res) => {
|
||||||
let content = req.body
|
const content = req.body
|
||||||
|
|
||||||
if (content && content.payment_status && content.payment_status === 'Completed') {
|
if (content && content.payment_status && content.payment_status === 'Completed') {
|
||||||
await API.Payment.handleIPN(content)
|
await API.Payment.handleIPN(content)
|
||||||
@ -495,7 +495,7 @@ router.post('/paypal/ipn', wrap(async (req, res) => {
|
|||||||
router.get('/donations/user', wrap(async (req, res, next) => {
|
router.get('/donations/user', wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
|
|
||||||
let contribs = await API.Payment.userContributions(req.session.user)
|
const contribs = await API.Payment.userContributions(req.session.user)
|
||||||
res.jsonp(contribs)
|
res.jsonp(contribs)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -509,22 +509,22 @@ router.get('/donations', wrap(async (req, res, next) => {
|
|||||||
count = 10
|
count = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
let mcu = req.query.mcu === '1' || req.query.mcu === 'true'
|
const mcu = req.query.mcu === '1' || req.query.mcu === 'true'
|
||||||
let timeFrame = parseInt(req.query.timeFrame)
|
let timeFrame = parseInt(req.query.timeFrame)
|
||||||
if (isNaN(timeFrame)) timeFrame = 0
|
if (isNaN(timeFrame)) timeFrame = 0
|
||||||
|
|
||||||
let contribs = await API.Payment.allContributions(count, mcu, timeFrame)
|
const contribs = await API.Payment.allContributions(count, mcu, timeFrame)
|
||||||
res.jsonp(contribs)
|
res.jsonp(contribs)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 404
|
// 404
|
||||||
router.use((req, res) => {
|
router.use((req, res) => {
|
||||||
res.status(404).jsonp({error: 'Not found'})
|
res.status(404).jsonp({ error: 'Not found' })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.use((err, req, res, next) => {
|
router.use((err, req, res, next) => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
res.jsonp({error: 'Internal server error.'})
|
res.jsonp({ error: 'Internal server error.' })
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
@ -15,10 +15,10 @@ import apiRouter from './api'
|
|||||||
import oauthRouter from './oauth2'
|
import oauthRouter from './oauth2'
|
||||||
import adminRouter from './admin'
|
import adminRouter from './admin'
|
||||||
|
|
||||||
let router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
// Restrict account creation
|
// Restrict account creation
|
||||||
let accountLimiter = new RateLimit({
|
const accountLimiter = new RateLimit({
|
||||||
windowMs: 60 * 60 * 1000, // 1 hour
|
windowMs: 60 * 60 * 1000, // 1 hour
|
||||||
max: 10,
|
max: 10,
|
||||||
delayMs: 0,
|
delayMs: 0,
|
||||||
@ -70,7 +70,7 @@ router.use(wrap(async (req, res, next) => {
|
|||||||
console.debug('User session update')
|
console.debug('User session update')
|
||||||
|
|
||||||
// Check for bans
|
// Check for bans
|
||||||
let banStatus = await API.User.getBanStatus(req.session.user.id)
|
const banStatus = await API.User.getBanStatus(req.session.user.id)
|
||||||
|
|
||||||
if (banStatus.length) {
|
if (banStatus.length) {
|
||||||
delete req.session.user
|
delete req.session.user
|
||||||
@ -78,11 +78,11 @@ router.use(wrap(async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update user session
|
// Update user session
|
||||||
let udata = await API.User.get(req.session.user)
|
const udata = await API.User.get(req.session.user)
|
||||||
setSession(req, udata)
|
setSession(req, udata)
|
||||||
|
|
||||||
// Update IP address
|
// Update IP address
|
||||||
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
|
// Add social media login buttons
|
||||||
function extraButtons (recheck) {
|
function extraButtons (recheck) {
|
||||||
let et = config.external
|
const et = config.external
|
||||||
return function (req, res, next) {
|
return function (req, res, next) {
|
||||||
if (!et) return next()
|
if (!et) return next()
|
||||||
res.locals.auth = {
|
res.locals.auth = {
|
||||||
@ -149,22 +149,22 @@ function formKeep (req, res, next) {
|
|||||||
router.get('/login/reset', extraButtons(false), (req, res) => {
|
router.get('/login/reset', extraButtons(false), (req, res) => {
|
||||||
if (req.session.user) return redirectLogin(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)
|
// Password reset endpoint (emailed link)
|
||||||
router.get('/reset/:token', wrap(async (req, res) => {
|
router.get('/reset/:token', wrap(async (req, res) => {
|
||||||
if (req.session.user) return res.redirect('/login')
|
if (req.session.user) return res.redirect('/login')
|
||||||
let token = req.params.token
|
const token = req.params.token
|
||||||
let success = await API.User.Reset.resetToken(token)
|
const success = await API.User.Reset.resetToken(token)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
req.flash('message', {error: true, text: 'Invalid or expired reset token.'})
|
req.flash('message', { error: true, text: 'Invalid or expired reset token.' })
|
||||||
res.redirect('/login')
|
res.redirect('/login')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('user/password_new', {token: true})
|
res.render('user/password_new', { token: true })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/login', extraButtons(false), (req, res) => {
|
router.get('/login', extraButtons(false), (req, res) => {
|
||||||
@ -190,13 +190,13 @@ router.get('/register', extraButtons(true), formKeep, (req, res) => {
|
|||||||
// User activation endpoint (emailed link)
|
// User activation endpoint (emailed link)
|
||||||
router.get('/activate/:token', wrap(async (req, res) => {
|
router.get('/activate/:token', wrap(async (req, res) => {
|
||||||
if (req.session.user) return res.redirect('/login')
|
if (req.session.user) return res.redirect('/login')
|
||||||
let token = req.params.token
|
const token = req.params.token
|
||||||
let success = await API.User.Login.activationToken(token)
|
const success = await API.User.Login.activationToken(token)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
req.flash('message', {error: true, text: 'Invalid or expired activation token.'})
|
req.flash('message', { error: true, text: 'Invalid or expired activation token.' })
|
||||||
} else {
|
} 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')
|
res.redirect('/login')
|
||||||
@ -204,10 +204,10 @@ router.get('/activate/:token', wrap(async (req, res) => {
|
|||||||
|
|
||||||
// View for enabling Two-Factor Authentication
|
// View for enabling Two-Factor Authentication
|
||||||
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
||||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
const twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||||
if (twoFaEnabled) return res.redirect('/')
|
if (twoFaEnabled) return res.redirect('/')
|
||||||
|
|
||||||
let newToken = await API.User.Login.totpAquire(req.session.user)
|
const newToken = await API.User.Login.totpAquire(req.session.user)
|
||||||
if (!newToken) return res.redirect('/')
|
if (!newToken) return res.redirect('/')
|
||||||
|
|
||||||
res.render('user/totp', { uri: newToken })
|
res.render('user/totp', { uri: newToken })
|
||||||
@ -215,7 +215,7 @@ router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
|||||||
|
|
||||||
// View for disabling Two-Factor Authentication
|
// View for disabling Two-Factor Authentication
|
||||||
router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => {
|
router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => {
|
||||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
const twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||||
|
|
||||||
if (!twoFaEnabled) return res.redirect('/')
|
if (!twoFaEnabled) return res.redirect('/')
|
||||||
res.render('user/password')
|
res.render('user/password')
|
||||||
@ -229,13 +229,13 @@ router.get('/login/verify', (req, res) => {
|
|||||||
// User settings page
|
// User settings page
|
||||||
router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||||
let totpEnabled = false
|
let totpEnabled = false
|
||||||
let socialStatus = await API.User.socialStatus(req.session.user)
|
const socialStatus = await API.User.socialStatus(req.session.user)
|
||||||
|
|
||||||
if (socialStatus.password) {
|
if (socialStatus.password) {
|
||||||
totpEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
totpEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
let et = config.external
|
const et = config.external
|
||||||
if (et) {
|
if (et) {
|
||||||
res.locals.auth = {}
|
res.locals.auth = {}
|
||||||
// Decide whether we need a disconnect or a log in with button for social account logins
|
// Decide whether we need a disconnect or a log in with button for social account logins
|
||||||
@ -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
|
// Change password
|
||||||
router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
|
router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
|
||||||
let socialStatus = await API.User.socialStatus(req.session.user)
|
const socialStatus = await API.User.socialStatus(req.session.user)
|
||||||
|
|
||||||
res.render('user/password_new', {token: !socialStatus.password})
|
res.render('user/password_new', { token: !socialStatus.password })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Change email
|
// Change email
|
||||||
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
|
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
|
||||||
let obfuscated = req.session.user.email
|
let obfuscated = req.session.user.email
|
||||||
if (obfuscated) {
|
if (obfuscated) {
|
||||||
let split = obfuscated.split('@')
|
const split = obfuscated.split('@')
|
||||||
let rep = split[0].charAt(0) + '***' + split[0].charAt(split[0].length - 1)
|
const rep = split[0].charAt(0) + '***' + split[0].charAt(split[0].length - 1)
|
||||||
obfuscated = rep + '@' + split[1]
|
obfuscated = rep + '@' + split[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
let socialStatus = await API.User.socialStatus(req.session.user)
|
const socialStatus = await API.User.socialStatus(req.session.user)
|
||||||
|
|
||||||
res.render('user/email_change', {email: obfuscated, password: socialStatus.password})
|
res.render('user/email_change', { email: obfuscated, password: socialStatus.password })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/donate', wrap(async (req, res, next) => {
|
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
|
// Used to display errors on forms and save data
|
||||||
function formError (req, res, error, redirect) {
|
function formError (req, res, error, redirect) {
|
||||||
// Security measures: never store any passwords in any session
|
// Security measures: never store any passwords in any session
|
||||||
for (let key in req.body) {
|
for (const key in req.body) {
|
||||||
if (key.indexOf('password') !== -1) {
|
if (key.indexOf('password') !== -1) {
|
||||||
delete req.body[key]
|
delete req.body[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.flash('formkeep', req.body || {})
|
req.flash('formkeep', req.body || {})
|
||||||
req.flash('message', {error: true, text: error})
|
req.flash('message', { error: true, text: error })
|
||||||
res.redirect(redirect || req.originalUrl)
|
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.')
|
return formError(req, res, 'You need to enter the code.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let verified = await API.User.Login.totpCheck(req.session.user, req.body.code)
|
const verified = await API.User.Login.totpCheck(req.session.user, req.body.code)
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
return formError(req, res, 'Something went wrong! Try scanning the code again.')
|
return formError(req, res, 'Something went wrong! Try scanning the code again.')
|
||||||
}
|
}
|
||||||
@ -366,7 +366,7 @@ router.post('/user/two-factor/disable', csrfValidation, wrap(async (req, res, ne
|
|||||||
return formError(req, res, 'Please enter your password.')
|
return formError(req, res, 'Please enter your password.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let purge = await API.User.Login.purgeTotp(req.session.user, req.body.password)
|
const purge = await API.User.Login.purgeTotp(req.session.user, req.body.password)
|
||||||
if (!purge) {
|
if (!purge) {
|
||||||
return formError(req, res, 'Invalid password.')
|
return formError(req, res, 'Invalid password.')
|
||||||
}
|
}
|
||||||
@ -384,12 +384,12 @@ router.post('/login/verify', csrfValidation, wrap(async (req, res, next) => {
|
|||||||
return formError(req, res, 'You need to enter the code.')
|
return formError(req, res, 'You need to enter the code.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let totpCheck = await API.User.Login.totpCheck(req.session.totp_check, req.body.code, req.body.recovery || false)
|
const totpCheck = await API.User.Login.totpCheck(req.session.totp_check, req.body.code, req.body.recovery || false)
|
||||||
if (!totpCheck) {
|
if (!totpCheck) {
|
||||||
return formError(req, res, 'Invalid code!')
|
return formError(req, res, 'Invalid code!')
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await API.User.get(req.session.totp_check)
|
const user = await API.User.get(req.session.totp_check)
|
||||||
delete req.session.totp_check
|
delete req.session.totp_check
|
||||||
|
|
||||||
setSession(req, user)
|
setSession(req, user)
|
||||||
@ -404,12 +404,12 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
|
|||||||
return res.redirect('/login')
|
return res.redirect('/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await API.User.get(req.body.username)
|
const user = await API.User.get(req.body.username)
|
||||||
if (!user) return formError(req, res, 'Invalid username or password.')
|
if (!user) return formError(req, res, 'Invalid username or password.')
|
||||||
|
|
||||||
if (!user.password || user.password === '') return formError(req, res, 'Please log in using the buttons on the right.')
|
if (!user.password || user.password === '') return formError(req, res, 'Please log in using the buttons on the right.')
|
||||||
|
|
||||||
let pwMatch = await API.User.Login.password(user, req.body.password)
|
const pwMatch = await API.User.Login.password(user, req.body.password)
|
||||||
if (!pwMatch) return formError(req, res, 'Invalid username or password.')
|
if (!pwMatch) return formError(req, res, 'Invalid username or password.')
|
||||||
|
|
||||||
if (user.activated === 0) return formError(req, res, 'Please activate your account first. If you did not receive an email, please contact an administrator.')
|
if (user.activated === 0) return formError(req, res, 'Please activate your account first. If you did not receive an email, please contact an administrator.')
|
||||||
@ -417,13 +417,13 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
|
|||||||
if (user.locked === 1) return formError(req, res, 'This account has been locked. Please contact an administrator for more information.')
|
if (user.locked === 1) return formError(req, res, 'This account has been locked. Please contact an administrator for more information.')
|
||||||
|
|
||||||
// Check if the user is banned
|
// Check if the user is banned
|
||||||
let banStatus = await API.User.getBanStatus(user.id)
|
const banStatus = await API.User.getBanStatus(user.id)
|
||||||
if (banStatus.length) {
|
if (banStatus.length) {
|
||||||
return res.render('user/banned', {bans: banStatus, ipban: false})
|
return res.render('user/banned', { bans: banStatus, ipban: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to the verification dialog if 2FA is enabled
|
// Redirect to the verification dialog if 2FA is enabled
|
||||||
let totpRequired = await API.User.Login.totpTokenRequired(user)
|
const totpRequired = await API.User.Login.totpTokenRequired(user)
|
||||||
if (totpRequired) {
|
if (totpRequired) {
|
||||||
req.session.totp_check = user.id
|
req.session.totp_check = user.id
|
||||||
return res.redirect('/login/verify')
|
return res.redirect('/login/verify')
|
||||||
@ -453,8 +453,8 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
|
|||||||
return formError(req, res, 'You need to enter your email address.')
|
return formError(req, res, 'You need to enter your email address.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let email = req.body.email
|
const email = req.body.email
|
||||||
let validEmail = await API.User.Register.validateEmail(email)
|
const validEmail = await API.User.Register.validateEmail(email)
|
||||||
if (!validEmail) {
|
if (!validEmail) {
|
||||||
return formError(req, res, 'You need to enter a valid email address.')
|
return formError(req, res, 'You need to enter a valid email address.')
|
||||||
}
|
}
|
||||||
@ -462,7 +462,7 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
|
|||||||
try {
|
try {
|
||||||
await API.User.Reset.reset(email, false)
|
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')
|
res.redirect('/login/reset?success=true')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return formError(req, res, e.message)
|
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)
|
// Password reset endpoint (emailed link)
|
||||||
router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
|
router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
|
||||||
if (req.session.user) return res.redirect('/login')
|
if (req.session.user) return res.redirect('/login')
|
||||||
let token = req.params.token
|
const token = req.params.token
|
||||||
let user = await API.User.Reset.resetToken(token)
|
const user = await API.User.Reset.resetToken(token)
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
req.flash('message', {error: true, text: 'Invalid or expired reset token.'})
|
req.flash('message', { error: true, text: 'Invalid or expired reset token.' })
|
||||||
res.redirect('/login')
|
res.redirect('/login')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4th Check: Password length
|
// 4th Check: Password length
|
||||||
let password = req.body.password
|
const password = req.body.password
|
||||||
if (!password || password.length < 8) {
|
if (!password || password.length < 8) {
|
||||||
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
|
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5th Check: Password match
|
// 5th Check: Password match
|
||||||
let passwordAgain = req.body.password_repeat
|
const passwordAgain = req.body.password_repeat
|
||||||
if (!passwordAgain || password !== passwordAgain) {
|
if (!passwordAgain || password !== passwordAgain) {
|
||||||
return formError(req, res, 'Passwords do not match!')
|
return formError(req, res, 'Passwords do not match!')
|
||||||
}
|
}
|
||||||
@ -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)
|
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')
|
res.redirect('/login')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return formError(req, res, e.message)
|
return formError(req, res, e.message)
|
||||||
@ -514,37 +514,37 @@ router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ban check
|
// Ban check
|
||||||
let banStatus = await API.User.getBanStatus(req.realIP, true)
|
const banStatus = await API.User.getBanStatus(req.realIP, true)
|
||||||
if (banStatus.length) {
|
if (banStatus.length) {
|
||||||
return res.render('user/banned', {bans: banStatus, ipban: true})
|
return res.render('user/banned', { bans: banStatus, ipban: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1st Check: Username Characters and length
|
// 1st Check: Username Characters and length
|
||||||
let username = req.body.username
|
const username = req.body.username
|
||||||
if (!username || !username.match(/^([\w-_]{3,26})$/i)) {
|
if (!username || !username.match(/^([\w-_]{3,26})$/i)) {
|
||||||
return formError(req, res, 'Invalid username! Must be between 3-26 characters and composed of alphanumeric characters!')
|
return formError(req, res, 'Invalid username! Must be between 3-26 characters and composed of alphanumeric characters!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2nd Check: Display Name
|
// 2nd Check: Display Name
|
||||||
let displayName = req.body.display_name
|
const displayName = req.body.display_name
|
||||||
if (!displayName || !displayName.match(/^([^\\`]{3,32})$/i)) {
|
if (!displayName || !displayName.match(/^([^\\`]{3,32})$/i)) {
|
||||||
return formError(req, res, 'Invalid display name!')
|
return formError(req, res, 'Invalid display name!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3rd Check: Email Address
|
// 3rd Check: Email Address
|
||||||
let email = req.body.email
|
const email = req.body.email
|
||||||
if (!email || !API.User.Register.validateEmail(email)) {
|
if (!email || !API.User.Register.validateEmail(email)) {
|
||||||
return formError(req, res, 'Invalid email address!')
|
return formError(req, res, 'Invalid email address!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4th Check: Password length
|
// 4th Check: Password length
|
||||||
let password = req.body.password
|
const password = req.body.password
|
||||||
if (!password || password.length < 8) {
|
if (!password || password.length < 8) {
|
||||||
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
|
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5th Check: Password match
|
// 5th Check: Password match
|
||||||
let passwordAgain = req.body.password_repeat
|
const passwordAgain = req.body.password_repeat
|
||||||
if (!passwordAgain || password !== passwordAgain) {
|
if (!passwordAgain || password !== passwordAgain) {
|
||||||
return formError(req, res, 'Passwords do not match!')
|
return formError(req, res, 'Passwords do not match!')
|
||||||
}
|
}
|
||||||
@ -570,7 +570,7 @@ router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hash the password
|
// Hash the password
|
||||||
let hash = await API.User.Register.hashPassword(password)
|
const hash = await API.User.Register.hashPassword(password)
|
||||||
let newUser
|
let newUser
|
||||||
|
|
||||||
// Attempt to create the user
|
// Attempt to create the user
|
||||||
@ -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.'
|
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')
|
res.redirect('/login')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -626,22 +626,22 @@ router.post('/user/manage', csrfValidation, wrap(async (req, res, next) => {
|
|||||||
|
|
||||||
req.session.user.display_name = displayName
|
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')
|
res.redirect('/user/manage')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Change user password
|
// Change user password
|
||||||
router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
|
router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
let user = req.session.user
|
const user = req.session.user
|
||||||
let socialStatus = await API.User.socialStatus(user)
|
const socialStatus = await API.User.socialStatus(user)
|
||||||
|
|
||||||
if (!req.body.password_old && socialStatus.password) {
|
if (!req.body.password_old && socialStatus.password) {
|
||||||
return formError(req, res, 'Please enter your current password.')
|
return formError(req, res, 'Please enter your current password.')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (socialStatus.password) {
|
if (socialStatus.password) {
|
||||||
let passwordMatch = await API.User.Login.password(user, req.body.password_old)
|
const passwordMatch = await API.User.Login.password(user, req.body.password_old)
|
||||||
if (!passwordMatch) {
|
if (!passwordMatch) {
|
||||||
return formError(req, res, 'The password you provided is incorrect.')
|
return formError(req, res, 'The password you provided is incorrect.')
|
||||||
}
|
}
|
||||||
@ -652,7 +652,7 @@ router.post('/user/manage/password', accountLimiter, csrfValidation, wrap(async
|
|||||||
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
|
return formError(req, res, 'Invalid password! Please use at least 8 characters!')
|
||||||
}
|
}
|
||||||
|
|
||||||
let passwordAgain = req.body.password_repeat
|
const passwordAgain = req.body.password_repeat
|
||||||
if (!passwordAgain || password !== passwordAgain) {
|
if (!passwordAgain || password !== passwordAgain) {
|
||||||
return formError(req, res, 'The passwords do not match!')
|
return formError(req, res, 'The passwords do not match!')
|
||||||
}
|
}
|
||||||
@ -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')
|
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) => {
|
router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
|
||||||
if (!req.session.user) return next()
|
if (!req.session.user) return next()
|
||||||
|
|
||||||
let user = await API.User.get(req.session.user)
|
const user = await API.User.get(req.session.user)
|
||||||
let email = req.body.email
|
const email = req.body.email
|
||||||
let newEmail = req.body.email_new
|
const newEmail = req.body.email_new
|
||||||
let password = req.body.password
|
const password = req.body.password
|
||||||
|
|
||||||
if (!newEmail || (!email && user.email !== '')) {
|
if (!newEmail || (!email && user.email !== '')) {
|
||||||
return formError(req, res, 'Please fill in all of the fields.')
|
return formError(req, res, 'Please fill in all of the fields.')
|
||||||
@ -702,18 +702,18 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
|
|||||||
return formError(req, res, 'Enter a password.')
|
return formError(req, res, 'Enter a password.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let passwordMatch = await API.User.Login.password(user, password)
|
const passwordMatch = await API.User.Login.password(user, password)
|
||||||
if (!passwordMatch) {
|
if (!passwordMatch) {
|
||||||
return formError(req, res, 'The password you provided is incorrect.')
|
return formError(req, res, 'The password you provided is incorrect.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let emailValid = API.User.Register.validateEmail(newEmail)
|
const emailValid = API.User.Register.validateEmail(newEmail)
|
||||||
if (!emailValid) {
|
if (!emailValid) {
|
||||||
return formError(req, res, 'Invalid email address.')
|
return formError(req, res, 'Invalid email address.')
|
||||||
}
|
}
|
||||||
|
|
||||||
let emailTaken = await API.User.get(newEmail)
|
const emailTaken = await API.User.get(newEmail)
|
||||||
if (emailTaken) {
|
if (emailTaken) {
|
||||||
return formError(req, res, 'This email is already taken.')
|
return formError(req, res, 'This email is already taken.')
|
||||||
}
|
}
|
||||||
@ -730,7 +730,7 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
|
|||||||
|
|
||||||
req.session.user.email = newEmail
|
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')
|
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.
|
// Serve a document form the documents directory, cache it.
|
||||||
const docsDir = path.join(__dirname, '../../documents')
|
const docsDir = path.join(__dirname, '../../documents')
|
||||||
router.get('/docs/:name', wrap(async (req, res, next) => {
|
router.get('/docs/:name', wrap(async (req, res, next) => {
|
||||||
let doc = path.join(docsDir, req.params.name + '.html')
|
const doc = path.join(docsDir, req.params.name + '.html')
|
||||||
if (!await fs.exists(docsDir) || !await fs.exists(doc)) {
|
if (!await fs.exists(docsDir) || !await fs.exists(doc)) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.readFile(doc, {encoding: 'utf8'}, (err, contents) => {
|
fs.readFile(doc, { encoding: 'utf8' }, (err, contents) => {
|
||||||
if (err) return next(err)
|
if (err) return next(err)
|
||||||
res.header('Cache-Control', 'max-age=' + 7 * 24 * 60 * 60 * 1000) // 1 week
|
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
|
// Serve news
|
||||||
router.get('/news/:id?-*', wrap(async (req, res) => {
|
router.get('/news/:id?-*', wrap(async (req, res) => {
|
||||||
let id = parseInt(req.params.id)
|
const id = parseInt(req.params.id)
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
return res.status(404).render('article', {article: null})
|
return res.status(404).render('article', { article: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
let article = await News.article(id)
|
const article = await News.article(id)
|
||||||
if (!article.id) {
|
if (!article.id) {
|
||||||
return res.status(404).render('article', {article: null})
|
return res.status(404).render('article', { article: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
let editing = false
|
let editing = false
|
||||||
@ -803,7 +803,7 @@ router.get('/news/:id?-*', wrap(async (req, res) => {
|
|||||||
editing = true
|
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) => {
|
router.get('/news/', wrap(async (req, res) => {
|
||||||
@ -812,20 +812,20 @@ router.get('/news/', wrap(async (req, res) => {
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let news = await News.listNews(page)
|
const news = await News.listNews(page)
|
||||||
|
|
||||||
res.render('news/news', {news: news})
|
res.render('news/news', { news: news })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/news/atom.xml', wrap(async (req, res) => {
|
router.get('/news/atom.xml', wrap(async (req, res) => {
|
||||||
let feed = await News.generateFeed()
|
const feed = await News.generateFeed()
|
||||||
|
|
||||||
res.set('Content-Type', 'application/atom+xml')
|
res.set('Content-Type', 'application/atom+xml')
|
||||||
res.send(feed.atom1())
|
res.send(feed.atom1())
|
||||||
}))
|
}))
|
||||||
|
|
||||||
router.get('/news/feed.json', wrap(async (req, res) => {
|
router.get('/news/feed.json', wrap(async (req, res) => {
|
||||||
let feed = await News.generateFeed()
|
const feed = await News.generateFeed()
|
||||||
|
|
||||||
res.set('Content-Type', 'application/json')
|
res.set('Content-Type', 'application/json')
|
||||||
res.send(feed.json1())
|
res.send(feed.json1())
|
||||||
|
@ -6,12 +6,12 @@ import RateLimit from 'express-rate-limit'
|
|||||||
import wrap from '../../scripts/asyncRoute'
|
import wrap from '../../scripts/asyncRoute'
|
||||||
import config from '../../scripts/load-config.js'
|
import config from '../../scripts/load-config.js'
|
||||||
|
|
||||||
let router = express.Router()
|
const router = express.Router()
|
||||||
let oauth = new OAuth2()
|
const oauth = new OAuth2()
|
||||||
|
|
||||||
router.use(oauth.express())
|
router.use(oauth.express())
|
||||||
|
|
||||||
let oauthLimiter = new RateLimit({
|
const oauthLimiter = new RateLimit({
|
||||||
windowMs: 5 * 60 * 1000, // 5 minutes
|
windowMs: 5 * 60 * 1000, // 5 minutes
|
||||||
max: 10,
|
max: 10,
|
||||||
delayMs: 0
|
delayMs: 0
|
||||||
@ -32,8 +32,8 @@ router.post('/introspect', oauth.controller.introspection)
|
|||||||
|
|
||||||
// Protected user information resource
|
// Protected user information resource
|
||||||
router.get('/user', oauth.bearer, wrap(async (req, res) => {
|
router.get('/user', oauth.bearer, wrap(async (req, res) => {
|
||||||
let accessToken = req.oauth2.accessToken
|
const accessToken = req.oauth2.accessToken
|
||||||
let user = await UAPI.User.get(accessToken.user_id)
|
const user = await UAPI.User.get(accessToken.user_id)
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(404).jsonp({
|
return res.status(404).jsonp({
|
||||||
@ -41,7 +41,7 @@ router.get('/user', oauth.bearer, wrap(async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let udata = {
|
const udata = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
uuid: user.uuid,
|
uuid: user.uuid,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
|
@ -12,8 +12,8 @@ import flash from '../scripts/flash'
|
|||||||
import config from '../scripts/load-config'
|
import config from '../scripts/load-config'
|
||||||
import email from './api/emailer'
|
import email from './api/emailer'
|
||||||
|
|
||||||
let app = express()
|
const app = express()
|
||||||
let SessionStore = connectSession(session)
|
const SessionStore = connectSession(session)
|
||||||
|
|
||||||
app.enable('trust proxy', 1)
|
app.enable('trust proxy', 1)
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ app.use((req, res, next) => {
|
|||||||
app.use(favicon(path.join(__dirname, '..', 'static', 'image', 'icynet.ico')))
|
app.use(favicon(path.join(__dirname, '..', 'static', 'image', 'icynet.ico')))
|
||||||
|
|
||||||
module.exports = (args) => {
|
module.exports = (args) => {
|
||||||
app.set('view options', {layout: false})
|
app.set('view options', { layout: false })
|
||||||
app.set('view engine', 'pug')
|
app.set('view engine', 'pug')
|
||||||
app.set('views', path.join(__dirname, '../views'))
|
app.set('views', path.join(__dirname, '../views'))
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ module.exports = (args) => {
|
|||||||
app.use(morgan('dev'))
|
app.use(morgan('dev'))
|
||||||
}
|
}
|
||||||
|
|
||||||
let staticAge = args.dev ? 1000 : 7 * 24 * 60 * 60 * 1000 // 1 week of cache in production
|
const staticAge = args.dev ? 1000 : 7 * 24 * 60 * 60 * 1000 // 1 week of cache in production
|
||||||
|
|
||||||
// Static content directories, cache these requests.
|
// Static content directories, cache these requests.
|
||||||
// It is also a good idea to use nginx to serve these directories in order to save on computing power
|
// It is also a good idea to use nginx to serve these directories in order to save on computing power
|
||||||
|
@ -4,7 +4,7 @@ const path = require('path')
|
|||||||
const util = require('util')
|
const util = require('util')
|
||||||
|
|
||||||
require('@babel/register')({
|
require('@babel/register')({
|
||||||
plugins: [ '@babel/plugin-transform-modules-commonjs' ]
|
plugins: ['@babel/plugin-transform-modules-commonjs']
|
||||||
})
|
})
|
||||||
|
|
||||||
process.once('message', (args) => {
|
process.once('message', (args) => {
|
||||||
|
@ -34,5 +34,5 @@ block body
|
|||||||
input#custominfo(type="hidden", name="custom", value="")
|
input#custominfo(type="hidden", name="custom", value="")
|
||||||
.buttoncont
|
.buttoncont
|
||||||
a.btn.btn-primary.text-light(name="submit", onclick="$(this).closest('form').submit()")
|
a.btn.btn-primary.text-light(name="submit", onclick="$(this).closest('form').submit()")
|
||||||
i.fa.fa-fw.fa-paypal
|
i.fab.fa-fw.fa-paypal
|
||||||
| Donate
|
| Donate
|
||||||
|
@ -18,7 +18,7 @@ block body
|
|||||||
input(type="hidden", name="csrf", value=csrf)
|
input(type="hidden", name="csrf", value=csrf)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="username") Username or Email Address
|
label(for="username") Username or Email Address
|
||||||
input.form-control(type="text", name="username", id="username")
|
input.form-control(type="text", name="username", id="username", autofocus)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="password") Password
|
label(for="password") Password
|
||||||
input.form-control(type="password", name="password", id="password")
|
input.form-control(type="password", name="password", id="password")
|
||||||
|
@ -17,5 +17,5 @@ block body
|
|||||||
input(type="hidden", name="csrf", value=csrf)
|
input(type="hidden", name="csrf", value=csrf)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="password") Password
|
label(for="password") Password
|
||||||
input.form-control#password(type="password", name="password")
|
input.form-control#password(type="password", name="password", autofocus)
|
||||||
button.btn.btn-primary(type="submit") Continue
|
button.btn.btn-primary(type="submit") Continue
|
||||||
|
@ -16,7 +16,7 @@ block body
|
|||||||
input(type="hidden", name="csrf", value=csrf)
|
input(type="hidden", name="csrf", value=csrf)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="username") Username
|
label(for="username") Username
|
||||||
input.form-control#username(type="text", name="username", value=formkeep.username, aria-labelledby="usernameText")
|
input.form-control#username(type="text", name="username", value=formkeep.username, aria-labelledby="usernameText", autofocus)
|
||||||
small#usernameText English characters, numbers and -_ only.
|
small#usernameText English characters, numbers and -_ only.
|
||||||
.form-group
|
.form-group
|
||||||
label(for="display_name") Display Name
|
label(for="display_name") Display Name
|
||||||
|
@ -18,5 +18,5 @@ block body
|
|||||||
input(type="hidden", name="csrf", value=csrf)
|
input(type="hidden", name="csrf", value=csrf)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="email") Email Address
|
label(for="email") Email Address
|
||||||
input.form-control#email(type="email", name="email")
|
input.form-control#email(type="email", name="email", autofocus)
|
||||||
button.btn.btn-primary(type="submit") Continue
|
button.btn.btn-primary(type="submit") Continue
|
||||||
|
@ -17,5 +17,5 @@ block body
|
|||||||
input(type="hidden", name="csrf", value=csrf)
|
input(type="hidden", name="csrf", value=csrf)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="code") Code
|
label(for="code") Code
|
||||||
input.form-control#code(type="text", name="code", autocomplete="off")
|
input.form-control#code(type="text", name="code", autocomplete="off", autofocus)
|
||||||
button.btn.btn-primary(type="submit") Log in
|
button.btn.btn-primary(type="submit") Log in
|
||||||
|
@ -25,7 +25,7 @@ block body
|
|||||||
form#totpForm(method="POST", action="")
|
form#totpForm(method="POST", action="")
|
||||||
input(type="hidden", name="csrf", value=csrf)
|
input(type="hidden", name="csrf", value=csrf)
|
||||||
.form-group
|
.form-group
|
||||||
label(for="code") Enter the Code
|
label(for="code",autofocus) Enter the Code
|
||||||
input.form-control#code(type="text", name="code", autocomplete="off")
|
input.form-control#code(type="text", name="code", autocomplete="off")
|
||||||
button.btn.btn-primary(type="submit") Enable Now
|
button.btn.btn-primary(type="submit") Enable Now
|
||||||
aside.col
|
aside.col
|
||||||
|
@ -12,7 +12,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
|
vue$: 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
@ -12,6 +12,6 @@ module.exports = merge(common, {
|
|||||||
],
|
],
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: true,
|
minimize: true,
|
||||||
minimizer: [ new TerserPlugin() ]
|
minimizer: [new TerserPlugin()]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user