forked from kaverti/website
384 lines
10 KiB
JavaScript
384 lines
10 KiB
JavaScript
/*
|
|
@swagger
|
|
components:
|
|
schemas:
|
|
Book:
|
|
type: object
|
|
required:
|
|
- title
|
|
- author
|
|
- finished
|
|
properties:
|
|
id:
|
|
type: integer
|
|
description: The auto-generated id of the book.
|
|
title:
|
|
type: string
|
|
description: The title of your book.
|
|
author:
|
|
type: string
|
|
description: Who wrote the book?
|
|
finished:
|
|
type: boolean
|
|
description: Have you finished reading it?
|
|
createdAt:
|
|
type: string
|
|
format: date
|
|
description: The date of the record creation.
|
|
example:
|
|
title: The Pragmatic Programmer
|
|
author: Andy Hunt / Dave Thomas
|
|
finished: true
|
|
*/
|
|
let bcrypt = require('bcryptjs')
|
|
let multer = require('multer')
|
|
let express = require('express')
|
|
let router = express.Router()
|
|
const auth = require('../lib/auth')
|
|
var Recaptcha = require('express-recaptcha').RecaptchaV3;
|
|
var recaptcha = new Recaptcha('6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy', '6LdlbrwZAAAAAMAWPVDrL8eNPxrws6AMDtLf1bgd');
|
|
var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
|
|
const Errors = require('../lib/errors.js')
|
|
var format = require('date-format');
|
|
let {
|
|
User, Post, ProfilePicture, Item, userWall, StaffApplications, Inventory, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
|
} = require('../models')
|
|
let pagination = require('../lib/pagination.js')
|
|
const sgMail = require('@sendgrid/mail');
|
|
const MailGen = require('mailgen')
|
|
const crypto = require("crypto")
|
|
const cryptoRandomString = require("crypto-random-string")
|
|
const rateLimit = require("express-rate-limit");
|
|
|
|
const emailLimiter = rateLimit({
|
|
windowMs: 60000,
|
|
max: 1, // limit each IP to 100 requests per windowMs
|
|
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 1 request to this endpoint per minute.\",\"status\":429}]}"
|
|
});
|
|
|
|
function setUserSession(req, res, username, UserId, admin) {
|
|
req.userData.loggedIn = true
|
|
req.userData.username = username
|
|
req.userData.UserId = UserId
|
|
res.cookie('username', username)
|
|
|
|
if(admin) { req.userData.admin = true }
|
|
}
|
|
|
|
router.post('/oidfhuisadhi8243', emailLimiter, async(req, res) => {
|
|
try {
|
|
await Ban.isIpBanned(req.ip)
|
|
|
|
let userParams = {
|
|
username: req.body.username,
|
|
email: req.body.email,
|
|
hash: req.body.password,
|
|
passkey: req.body.passkey,
|
|
admin: false,
|
|
bodyColor: '#fffff',
|
|
headColor: '#fffff',
|
|
leftLegColor: '#fffff',
|
|
rightLegColor: '#fffff',
|
|
leftArmColor: '#fffff',
|
|
rightArmColor: '#fffff',
|
|
koins: '250',
|
|
currency2: '0',
|
|
picture: 'default',
|
|
booster: false,
|
|
theme: 'light',
|
|
emailToken: crypto(16)
|
|
}
|
|
let user = await User.create(userParams)
|
|
await Ip.createIfNotExists(req.ip, user)
|
|
|
|
setUserSession(req, res, user.username, user.id, userParams.admin)
|
|
res.json(user.toJSON())
|
|
} catch (e) { next(e) }
|
|
})
|
|
router.post('/', emailLimiter, async(req, res, next) => {
|
|
try {
|
|
await Ban.isIpBanned(req.ip)
|
|
|
|
let userParams = {
|
|
username: req.body.username,
|
|
email: req.body.email,
|
|
hash: req.body.password,
|
|
passkey: req.body.passkey,
|
|
admin: false,
|
|
bodyColor: '#ffffff',
|
|
headColor: '#ffffff',
|
|
leftLegColor: '#ffffff',
|
|
rightLegColor: '#ffffff',
|
|
leftArmColor: '#ffffff',
|
|
rightArmColor: '#ffffff',
|
|
koins: '250',
|
|
currency2: '0',
|
|
picture: 'default',
|
|
developerMode: false,
|
|
emailVerified: false,
|
|
theme: 'light',
|
|
emailToken: cryptoRandomString({length: 16})
|
|
}
|
|
let user = await User.create(userParams)
|
|
await Ip.createIfNotExists(req.ip, user)
|
|
|
|
setUserSession(req, res, user.username, user.id, userParams.admin)
|
|
res.json(user.toJSON())
|
|
} catch (e) { next(e) }
|
|
})
|
|
router.post('/job-application', auth, async(req, res, next) => {
|
|
try {
|
|
let userParams = {
|
|
username: req.body.username,
|
|
dob: req.body.dob,
|
|
email: req.body.email,
|
|
whyWork: req.body.whyWork,
|
|
otherForm: req.body.otherForm,
|
|
experience: req.body.experience,
|
|
selectedOption: req.body.selectedOption,
|
|
}
|
|
await StaffApplications.submitApplication(userParams)
|
|
} catch (e) { next(e) }
|
|
})
|
|
router.get('/:username', async(req, res, next) => {
|
|
try {
|
|
let queryObj = {
|
|
attributes: {exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset']},
|
|
where: {username: req.params.username}
|
|
}
|
|
|
|
if (req.query.posts) {
|
|
|
|
let {from, limit} = pagination.getPaginationProps(req.query, true)
|
|
|
|
let postInclude = {
|
|
model: Post,
|
|
include: Post.includeOptions(),
|
|
limit,
|
|
order: [['id', 'DESC']]
|
|
}
|
|
if (from !== null) {
|
|
postInclude.where = {id: {$lte: from}}
|
|
}
|
|
queryObj.include = [postInclude]
|
|
|
|
let user = await User.findOne(queryObj)
|
|
if (!user) throw Errors.accountDoesNotExist
|
|
|
|
let meta = await user.getMeta(limit)
|
|
|
|
res.json(Object.assign(user.toJSON(limit), {meta}))
|
|
} else if (req.query.wall) {
|
|
let {from, limit} = pagination.getPaginationProps(req.query, true)
|
|
let postInclude = {
|
|
model: userWall,
|
|
include: userWall.includeOptions(),
|
|
limit,
|
|
order: [['id', 'DESC']],
|
|
}
|
|
if (from !== null) {
|
|
postInclude.where = {id: {$lte: from}}
|
|
}
|
|
queryObj.include = [postInclude]
|
|
|
|
let user = await User.findOne(queryObj)
|
|
if (!user) throw Errors.accountDoesNotExist
|
|
if (user.userWallOptOut) {
|
|
throw Errors.userWallOptOut
|
|
}
|
|
|
|
res.json(Object.assign(user.toJSON(limit)))
|
|
} else if (req.query.threads) {
|
|
let queryString = ''
|
|
|
|
Object.keys(req.query).forEach(query => {
|
|
queryString += `&${query}=${req.query[query]}`
|
|
})
|
|
|
|
res.redirect('/api/v1/forums/category/ALL?username=' + req.params.username + queryString)
|
|
} else if(req.query.marketplace) {
|
|
let {from, limit} = pagination.getPaginationProps(req.query, true)
|
|
let UserId = await User.findOne({
|
|
where: {
|
|
username: req.params.username
|
|
}
|
|
})
|
|
if(!UserId) throw Errors.accountDoesNotExist
|
|
let marketplace = await Item.findAll({
|
|
where: {
|
|
UserId: UserId.id
|
|
}
|
|
})
|
|
let postInclude = {
|
|
model: Item,
|
|
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] },
|
|
limit,
|
|
order: [['id', 'DESC']],
|
|
}
|
|
queryObj.include = [postInclude]
|
|
|
|
let user = await User.findOne(queryObj)
|
|
res.status(200)
|
|
let meta = await user.getMeta(limit)
|
|
|
|
res.json(Object.assign(user.toJSON(limit), {meta}))
|
|
} else if(req.query.inventory) {
|
|
let {from, limit} = pagination.getPaginationProps(req.query, true)
|
|
let userLookup = await User.findOne({
|
|
where: {
|
|
username: req.params.username
|
|
}
|
|
})
|
|
let marketplace = await Inventory.findAll({
|
|
where: {
|
|
UserId: userLookup.id
|
|
}
|
|
})
|
|
let postInclude = {
|
|
model: Inventory,
|
|
include: { model: Item, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } },
|
|
limit,
|
|
order: [['id', 'DESC']],
|
|
}
|
|
queryObj.include = [postInclude]
|
|
|
|
let user = await User.findOne(queryObj)
|
|
res.status(200)
|
|
let meta = await user.getMeta(limit)
|
|
|
|
res.json(Object.assign(user.toJSON(limit), {meta}))
|
|
} else {
|
|
let user = await User.findOne(queryObj)
|
|
if(!user) throw Errors.accountDoesNotExist
|
|
|
|
res.json(user.toJSON())
|
|
}
|
|
|
|
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
router.post('/login', async(req, res, next) => {
|
|
try {
|
|
await Ban.isIpBanned(req.ip, req.body.email)
|
|
|
|
let user = await User.findOne({ where: {
|
|
username: req.body.username
|
|
}})
|
|
if(user) {
|
|
if(await user.comparePassword(req.body.password)) {
|
|
await Ip.createIfNotExists(req.ip, user)
|
|
|
|
setUserSession(req, res, user.username, user.id, user.admin)
|
|
res.json({
|
|
username: user.username,
|
|
admin: user.admin,
|
|
success: true
|
|
})
|
|
} else {
|
|
res.status(401)
|
|
res.json({
|
|
errors: [Errors.invalidLoginCredentials]
|
|
})
|
|
}
|
|
} else {
|
|
res.status(401)
|
|
res.json({
|
|
errors: [Errors.invalidLoginCredentials]
|
|
})
|
|
}
|
|
} catch (err) { next(err) }
|
|
})
|
|
|
|
router.post('/:username/logout', auth, async(req, res) => {
|
|
req.userData.destroy(() => {
|
|
res.clearCookie('username')
|
|
res.clearCookie('admin')
|
|
res.json({
|
|
success: true
|
|
})
|
|
})
|
|
})
|
|
|
|
router.get('/:username/picture', async(req, res, next) => {
|
|
try {
|
|
let user = await User.findOne({
|
|
where: {
|
|
username: req.params.username
|
|
}
|
|
})
|
|
if(!user) throw Errors.accountDoesNotExist
|
|
|
|
let picture = await ProfilePicture.findOne({
|
|
where: {
|
|
UserId: user.id
|
|
}
|
|
})
|
|
|
|
if(!picture) {
|
|
res.status(404)
|
|
res.end('')
|
|
} else {
|
|
res.writeHead(200, {
|
|
'Content-Type': picture.mimetype,
|
|
'Content-disposition': 'attachment;filename=profile',
|
|
'Content-Length': picture.file.length
|
|
})
|
|
res.end(new Buffer(picture.file, 'binary'))
|
|
}
|
|
} catch (e) { next(e) }
|
|
})
|
|
|
|
router.get('/', async(req, res, next) => {
|
|
try {
|
|
let sortFields = {
|
|
createdAt: 'X.id',
|
|
username: 'X.username',
|
|
threadCount: 'threadCount',
|
|
postCount: 'postCount'
|
|
};
|
|
let offset = Number.isInteger(+req.query.offset) ? +req.query.offset : 0;
|
|
let havingClause = '';
|
|
if(req.query.role === 'admin') {
|
|
havingClause = 'HAVING Users.admin = true';
|
|
} else if(req.query.role === 'user') {
|
|
havingClause = 'HAVING Users.admin = false';
|
|
} else {
|
|
havingClause = 'Having Users.hidden = false';
|
|
}
|
|
if(req.query.search) {
|
|
//I.e. if there is not already a HAVING clause
|
|
if(!havingClause.length) {
|
|
havingClause = 'HAVING ';
|
|
} else {
|
|
havingClause += ' AND ';
|
|
}
|
|
havingClause += 'Users.username LIKE $search';
|
|
}
|
|
let sql = `
|
|
SELECT X.username, X.admin, X.picture, X.level, X.levelProgress, X.bot, X.booster, X.description, X.bodyColor, X.headColor, X.leftLegColor, X.rightLegColor, X.leftArmColor, X.rightArmColor, X.hidden, X.system, X.createdAt, X.contributor, X.postCount, COUNT(Threads.id) as threadCount
|
|
FROM (
|
|
SELECT Users.*, COUNT(Posts.id) as postCount
|
|
FROM Users
|
|
LEFT OUTER JOIN Posts
|
|
ON Users.id = Posts.UserId
|
|
GROUP BY Users.id
|
|
${havingClause}
|
|
) as X
|
|
LEFT OUTER JOIN threads
|
|
ON X.id = Threads.UserId
|
|
GROUP BY X.id
|
|
ORDER BY ${sortFields[req.query.sort] || 'X.id'} ${req.query.order === 'asc' ? 'DESC' : 'ASC'}
|
|
LIMIT 30
|
|
OFFSET ${offset}
|
|
`;
|
|
let users = await sequelize.query(sql, {
|
|
model: User,
|
|
bind: { search: req.query.search + '%' }
|
|
});
|
|
res.json(users)
|
|
} catch (e) { next(e) }
|
|
})
|
|
|
|
module.exports = router;
|