Merge branch 'canary'
This commit is contained in:
commit
2e43d68c93
|
@ -3,6 +3,6 @@ module.exports = {
|
|||
sessionSecret: process.env.SESSION_SECRET || 'iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;',
|
||||
imageUploadTeams: process.env.TEAMUPLOADS || "C:\\Users\\matth\\Documents\\GitHub\\Kaverti-Team-Images",
|
||||
maintenance: process.env.MAINTENANCE || false,
|
||||
rootFolder: process.env.ROOTFOLDER || "C:/Users/matth/Documents/GitHub/website/",
|
||||
cdnFolder: process.env.CDNFOLDER || "C:/xampp21/htdocs/"
|
||||
rootFolder: process.env.ROOTFOLDER || "/home/kaverti/website/",
|
||||
cdnFolder: process.env.CDNFOLDER || "/var/www/html/cdn/"
|
||||
}
|
||||
|
|
|
@ -384,6 +384,6 @@ export default {
|
|||
},
|
||||
mounted () {
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -250,7 +250,7 @@
|
|||
this.axios.put('/api/v1/chat/conversation/' + this.$route.params.id);
|
||||
|
||||
//Conversation panel might not have loaded request, so try again in 200 msec
|
||||
if(!this.$store.state.conversations.length) {
|
||||
if(!this.$store.state.user.conversations.length) {
|
||||
setTimeout(this.updateLastRead, 200);
|
||||
} else {
|
||||
this.$store.commit('updateConversationLastRead', +this.$route.params.id);
|
||||
|
|
|
@ -39,19 +39,19 @@
|
|||
</div>
|
||||
<c-scroll-load
|
||||
class='side_panel__conversations'
|
||||
:class='{ "side_panel__conversations--empty": !$store.state.conversations.length }'
|
||||
:class='{ "side_panel__conversations--empty": !$store.state.user.conversations.length }'
|
||||
|
||||
:loading='loading'
|
||||
position='bottom'
|
||||
@load='getConversations'
|
||||
>
|
||||
<side-panel-conversation
|
||||
:v-for='conversation in $store.state.conversations'
|
||||
:v-for='conversation in $store.state.user.conversations'
|
||||
:conversation='conversation'
|
||||
tabindex='0'
|
||||
></side-panel-conversation>
|
||||
|
||||
<div v-if='!$store.state.conversations.length && !loading'>
|
||||
<div v-if='!$store.state.user.conversations.length && !loading'>
|
||||
No conversations
|
||||
</div>
|
||||
</c-scroll-load>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
let {
|
||||
User, sequelize
|
||||
} = require('../models')
|
||||
const jwt = require("jsonwebtoken");
|
||||
const Errors = require('../lib/errors')
|
||||
module.exports = async(req, res, next) => {
|
||||
try {
|
||||
const token = req.headers.authorization.replace("Bearer ", "");
|
||||
const decoded = jwt.verify(token, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
req.userData = decoded;
|
||||
let user = await User.findOne({ where: {
|
||||
id: req.userData.id
|
||||
}})
|
||||
if(user && user.jwtOffset == req.userData.offset) {
|
||||
req.userData = decoded;
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
};
|
|
@ -16,7 +16,7 @@ module.exports = function (err, socket) {
|
|||
console.log(err);
|
||||
socket.emit('errors', {
|
||||
status: 500,
|
||||
errors: [{ message: 'There was an unknown error on our side - please try again later' }]
|
||||
errors: [{ message: 'There was an unknown error with Kaverti websockets - please try again later' }]
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = {
|
||||
up(queryInterface, Sequelize) {
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'blogposts',
|
||||
'plainText',
|
||||
{
|
||||
type: Sequelize.TEXT,
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
module.exports = {
|
||||
up(queryInterface, Sequelize) {
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'Teams',
|
||||
'hatId',
|
||||
{
|
||||
type: Sequelize.BIGINT,
|
||||
},
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'Teams',
|
||||
'pantsId',
|
||||
{
|
||||
type: Sequelize.BIGINT,
|
||||
},
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'Teams',
|
||||
'shirtId',
|
||||
{
|
||||
type: Sequelize.BIGINT,
|
||||
},
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'Teams',
|
||||
'faceId',
|
||||
{
|
||||
type: Sequelize.BIGINT,
|
||||
defaultValue: 0
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('feedback', {
|
||||
id: {
|
||||
type: Sequelize.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
route: {
|
||||
type: Sequelize.TEXT,
|
||||
defaultValue: '/',
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: Sequelize.TEXT
|
||||
},
|
||||
stars: {
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
text: {
|
||||
type: Sequelize.TEXT
|
||||
}
|
||||
}, {
|
||||
charset: 'utf8mb4'
|
||||
})
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('awards');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
up(queryInterface, Sequelize) {
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'feedback',
|
||||
'createdAt',
|
||||
{
|
||||
type: Sequelize.DATE,
|
||||
},
|
||||
),
|
||||
queryInterface.addColumn(
|
||||
'feedback',
|
||||
'updatedAt',
|
||||
{
|
||||
type: Sequelize.DATE,
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
};
|
141
models/blog.js
141
models/blog.js
|
@ -1,7 +1,36 @@
|
|||
let urlSlug = require('url-slug')
|
||||
var md = require('../kaverti_modules/markdown-it')({
|
||||
html: true, // Enable HTML tags in source
|
||||
xhtmlOut: false, // Use '/' to close single tags (<br />).
|
||||
// This is only for full CommonMark compatibility.
|
||||
breaks: true, // Convert '\n' in paragraphs into <br>
|
||||
langPrefix: 'language-', // CSS language prefix for fenced blocks. Can be
|
||||
// useful for external highlighters.
|
||||
linkify: true, // Autoconvert URL-like text to links
|
||||
image: true,
|
||||
|
||||
// Enable some language-neutral replacement + quotes beautification
|
||||
typographer: true,
|
||||
|
||||
// Double + single quotes replacement pairs, when typographer enabled,
|
||||
// and smartquotes on. Could be either a String or an Array.
|
||||
//
|
||||
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
|
||||
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
|
||||
quotes: '“”‘’',
|
||||
|
||||
// Highlighter function. Should return escaped HTML,
|
||||
// or '' if the source string is not changed and should be escaped externally.
|
||||
// If result starts with <pre... internal wrapper is skipped.
|
||||
highlight: function (/*str, lang*/) { return ''; }
|
||||
});
|
||||
var emoji = require('markdown-it-emoji');
|
||||
var twemoji = require('twemoji')
|
||||
md.use(emoji);
|
||||
md.renderer.rules.emoji = function(token, idx) {
|
||||
return twemoji.parse(token[idx].content);
|
||||
};
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
let Thread = sequelize.define('Thread', {
|
||||
let BlogPost = sequelize.define('BlogPost', {
|
||||
name: {
|
||||
type: DataTypes.TEXT,
|
||||
set (val) {
|
||||
|
@ -32,104 +61,18 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
}
|
||||
},
|
||||
slug: DataTypes.TEXT,
|
||||
postsCount: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0
|
||||
slug: {
|
||||
type: DataTypes.TEXT,
|
||||
defaultValue: "deprecated"
|
||||
},
|
||||
locked: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
}
|
||||
}, {
|
||||
instanceMethods: {
|
||||
getMeta (limit) {
|
||||
let meta = {}
|
||||
|
||||
let posts = this.Posts
|
||||
let firstPost = posts[0]
|
||||
let lastPost = posts.slice(-1)[0]
|
||||
|
||||
//next url
|
||||
if(!lastPost || lastPost.postNumber+1 === this.postsCount) {
|
||||
meta.nextURL = null
|
||||
} else {
|
||||
meta.nextURL =
|
||||
process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `blog/post/${this.id}?limit=${limit}&from=${lastPost.postNumber + 1}`
|
||||
}
|
||||
|
||||
//previous url
|
||||
if(!firstPost || firstPost.postNumber === 0) {
|
||||
meta.previousURL = null
|
||||
} else if(firstPost.postNumber - limit < 0) {
|
||||
meta.previousURL =
|
||||
process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `forums/thread/${this.id}?limit=${firstPost.postNumber}&from=0`
|
||||
} else {
|
||||
meta.previousURL =
|
||||
process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `forums/thread/${this.id}?limit=${limit}&from=${firstPost.postNumber - limit}`
|
||||
}
|
||||
|
||||
//remaining posts
|
||||
if(lastPost === undefined) {
|
||||
meta.nextPostsCount = 0
|
||||
meta.previousPostsCount = 0
|
||||
meta.postsRemaining = 0
|
||||
} else {
|
||||
let postsRemaining =
|
||||
this.postsCount - lastPost.postNumber - 1
|
||||
|
||||
meta.postsRemaining = postsRemaining
|
||||
|
||||
if(postsRemaining < limit) {
|
||||
meta.nextPostsCount = postsRemaining
|
||||
} else {
|
||||
meta.nextPostsCount = limit
|
||||
}
|
||||
|
||||
if(firstPost.postNumber === 0) {
|
||||
meta.previousPostsCount = 0
|
||||
} else if(firstPost.postNumber - limit < 0) {
|
||||
meta.previousPostsCount = firstPost.postNumber
|
||||
} else {
|
||||
meta.previousPostsCount = limit
|
||||
}
|
||||
}
|
||||
|
||||
return meta
|
||||
}
|
||||
plainText: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
classMethods: {
|
||||
associate (models) {
|
||||
Thread.belongsTo(models.User)
|
||||
Thread.belongsTo(models.Category)
|
||||
Thread.belongsTo(models.PollQuestion)
|
||||
Thread.hasMany(models.Post, { foreignKeyConstraint: true, onDelete: 'CASCADE' })
|
||||
},
|
||||
includeOptions (from, limit) {
|
||||
let models = sequelize.models
|
||||
|
||||
return [
|
||||
{ model: models.User, attributes: ['username', 'createdAt', 'color', 'picture', 'updatedAt', 'id'] },
|
||||
models.Category,
|
||||
{
|
||||
model: models.Post,
|
||||
where: { postNumber: { $gte: from } },
|
||||
order: [['id', 'ASC']],
|
||||
limit,
|
||||
include: [
|
||||
{ model: models.Thread, attributes: ['slug'] },
|
||||
{ model: models.User, as: 'Likes', attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
|
||||
{ model: models.User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'admin'] },
|
||||
{
|
||||
model: models.Post, as: 'Replies', include:
|
||||
[{ model: models.User, attributes: ['username', 'id', 'color', 'picture'] }]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
content: {
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
createdAt: DataTypes.DATE,
|
||||
updatedAt: DataTypes.DATE,
|
||||
})
|
||||
|
||||
return Thread
|
||||
return BlogPost
|
||||
}
|
||||
|
|
|
@ -16,41 +16,39 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
},
|
||||
groupUsers: DataTypes.STRING
|
||||
}, {
|
||||
classMethods: {
|
||||
associate (models) {
|
||||
Conversation.belongsToMany(models.User, {
|
||||
through: models.UserConversation
|
||||
});
|
||||
Conversation.hasMany(models.Message);
|
||||
},
|
||||
async setName (userId) {
|
||||
let json = this.toJSON();
|
||||
|
||||
if (!this.Users || this.Users.length < 2) {
|
||||
let users = await sequelize.models.User.findAll({
|
||||
attributes: {exclude: ['hash']},
|
||||
include: [{
|
||||
model: sequelize.models.Conversation,
|
||||
where: {id: this.id}
|
||||
}]
|
||||
});
|
||||
|
||||
json.Users = users.map(u => u.toJSON());
|
||||
}
|
||||
|
||||
if (this.name) {
|
||||
return json;
|
||||
} else if (json.Users.length === 2) {
|
||||
json.name = json.Users.find(u => u.id !== userId).username;
|
||||
} else {
|
||||
json.name = json.Users.map(u => u.username).join(', ');
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return Conversation;
|
||||
Conversation.associate = function (models) {
|
||||
Conversation.belongsToMany(models.User, {
|
||||
through: models.UserConversation
|
||||
});
|
||||
Conversation.hasMany(models.Message);
|
||||
}
|
||||
Conversation.prototype.setName = async function (userId) {
|
||||
let json = this.toJSON();
|
||||
|
||||
if (!this.Users || this.Users.length < 2) {
|
||||
let users = await sequelize.models.User.findAll({
|
||||
attributes: {exclude: ['hash']},
|
||||
include: [{
|
||||
model: sequelize.models.Conversation,
|
||||
where: {id: this.id}
|
||||
}]
|
||||
});
|
||||
|
||||
json.Users = users.map(u => u.toJSON());
|
||||
}
|
||||
|
||||
if (this.name) {
|
||||
return json;
|
||||
} else if (json.Users.length === 2) {
|
||||
json.name = json.Users.find(u => u.id !== userId).username;
|
||||
} else {
|
||||
json.name = json.Users.map(u => u.username).join(', ');
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
return Conversation
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
const Errors = require('../lib/errors')
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
let Feedback = sequelize.define('Feedback', {
|
||||
id: {
|
||||
type: DataTypes.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
route: {
|
||||
type: DataTypes.TEXT,
|
||||
defaultValue: '/',
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.TEXT,
|
||||
validate: {
|
||||
isEmail: true
|
||||
}
|
||||
},
|
||||
stars: {
|
||||
type: DataTypes.INTEGER,
|
||||
validate: {
|
||||
min: 1,
|
||||
max: 5
|
||||
}
|
||||
},
|
||||
text: {
|
||||
type: DataTypes.TEXT,
|
||||
validate: {
|
||||
len: [5, 512]
|
||||
}
|
||||
},
|
||||
createdAt: DataTypes.DATE,
|
||||
updatedAt: DataTypes.DATE
|
||||
}, {
|
||||
freezeTableName: true
|
||||
})
|
||||
return Feedback
|
||||
}
|
|
@ -118,6 +118,19 @@ module.exports = (sequelize, DataTypes) => {
|
|||
default: false,
|
||||
defaultValue: false
|
||||
},
|
||||
hatId: {
|
||||
type: DataTypes.BIGINT
|
||||
},
|
||||
shirtId: {
|
||||
type: DataTypes.BIGINT
|
||||
},
|
||||
pantsId: {
|
||||
type: DataTypes.BIGINT
|
||||
},
|
||||
faceId: {
|
||||
type: DataTypes.BIGINT,
|
||||
defaultValue: 0
|
||||
},
|
||||
picture: {
|
||||
type: DataTypes.TEXT('long'),
|
||||
validate: {
|
||||
|
|
|
@ -80,9 +80,9 @@ module.exports = (sequelize, DataTypes) => {
|
|||
theme: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'light',
|
||||
values: ['light', 'dark'],
|
||||
values: ['light', 'dark', 'amoled', 'highContrast'],
|
||||
isIn: {
|
||||
args: [['light', 'dark']],
|
||||
args: [['light', 'dark', 'amoled', 'highContrast']],
|
||||
msg: "Theme can only be one of the pre-defined options"
|
||||
},
|
||||
},
|
||||
|
@ -334,6 +334,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
User.hasMany(models.Inventory)
|
||||
User.hasMany(models.Transaction)
|
||||
User.hasMany(models.AuditLog)
|
||||
User.hasMany(models.BlogPost)
|
||||
User.hasMany(models.Item)
|
||||
User.hasMany(models.Item, {as: 'pants'})
|
||||
User.hasMany(models.Item, {as: 'shirt'})
|
||||
|
|
|
@ -44,6 +44,12 @@ module.exports = (sequelize, DataTypes) => {
|
|||
let userWall = sequelize.define('userWall', {
|
||||
content: {
|
||||
type: DataTypes.TEXT,
|
||||
validate: {
|
||||
len: {
|
||||
args: [4,256],
|
||||
message: "Your message needs to be at least 4 characters and less than 256."
|
||||
}
|
||||
},
|
||||
set (val) {
|
||||
if(!val) {
|
||||
throw Errors.sequelizeValidation(sequelize, {
|
||||
|
@ -71,6 +77,12 @@ module.exports = (sequelize, DataTypes) => {
|
|||
},
|
||||
plainText: {
|
||||
type: DataTypes.TEXT,
|
||||
validate: {
|
||||
len: {
|
||||
args: [4,256],
|
||||
message: "Your message needs to be at least 4 characters and less than 256."
|
||||
}
|
||||
},
|
||||
},
|
||||
postNumber: DataTypes.INTEGER,
|
||||
replyingToUsername: DataTypes.STRING,
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import bpy
|
||||
def hex_to_rgb(value):
|
||||
gamma = 2.05
|
||||
value = value.lstrip('#')
|
||||
lv = len(value)
|
||||
fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
||||
r = pow(fin[0] / 255, gamma)
|
||||
g = pow(fin[1] / 255, gamma)
|
||||
b = pow(fin[2] / 255, gamma)
|
||||
fin.clear()
|
||||
fin.append(r)
|
||||
fin.append(g)
|
||||
fin.append(b)
|
||||
return tuple(fin)
|
||||
bpy.ops.wm.open_mainfile(filepath='C:/Users/matth/Documents/GitHub/website//rendering/avatar.blend')
|
||||
bpy.data.objects['Head'].select = True
|
||||
bpy.data.materials['Head'].diffuse_color = hex_to_rgb('#ffffff')
|
||||
bpy.data.materials['Face'].diffuse_color = hex_to_rgb('#ffffff')
|
||||
bpy.data.objects['Left Arm'].select = True
|
||||
bpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('#ffffff')
|
||||
bpy.data.objects['Torso'].select = True
|
||||
bpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('#ffffff')
|
||||
bpy.data.objects['Right Arm'].select = True
|
||||
bpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('#ffffff')
|
||||
bpy.data.objects['Left Leg'].select = True
|
||||
bpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('#ffffff')
|
||||
bpy.data.objects['Right Leg'].select = True
|
||||
bpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('#ffffff')
|
||||
hat_import = bpy.ops.import_scene.obj(filepath='C:/Users/matth/Documents/GitHub/website//rendering/global/219738e01e9ec4f4272dd7e5aa4ac1dc.obj')
|
||||
hat = bpy.context.selected_objects[0]
|
||||
bpy.context.selected_objects[0].data.name = 'hat'
|
||||
bpy.context.selected_objects[0].name = 'hat'
|
||||
hat_material = bpy.data.materials.new('hat')
|
||||
hat_material.diffuse_shader = 'LAMBERT'
|
||||
hat.active_material = hat_material
|
||||
hat_image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website//rendering/global/2b15eb5292e8b6097575cb5ec44f7f55.png')
|
||||
hat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')
|
||||
hat_texture.image = hat_image
|
||||
hat_add = bpy.data.objects['hat'].active_material.texture_slots.add()
|
||||
hat_add.texture = hat_texture
|
||||
face_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website//rendering/global/defaultFace.png')
|
||||
bpy.data.textures['Face'].image = face_Image
|
||||
shirt_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website//rendering/global/0.png')
|
||||
bpy.data.textures['Shirt'].image = shirt_Image
|
||||
bpy.data.textures['ShirtR'].image = shirt_Image
|
||||
bpy.data.textures['ShirtL'].image = shirt_Image
|
||||
pants_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website//rendering/global/0.png')
|
||||
bpy.data.textures['PantsR'].image = pants_Image
|
||||
bpy.data.textures['PantsL'].image = pants_Image
|
||||
for obj in bpy.data.objects:
|
||||
obj.select = False
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.view3d.camera_to_view_selected()
|
||||
scene = bpy.context.scene
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
scene.render.filepath = 'C:/xampp21/htdocs//marketplace/preview/9650253457896447672190043079564275765258433403668372776443201272.png'
|
||||
bpy.ops.render.render(write_still = 1)
|
|
@ -12,7 +12,7 @@ def hex_to_rgb(value):
|
|||
fin.append(g)
|
||||
fin.append(b)
|
||||
return tuple(fin)
|
||||
bpy.ops.wm.open_mainfile(filepath='C:/Users/matth/Documents/GitHub/website/rendering/avatar.blend')
|
||||
bpy.ops.wm.open_mainfile(filepath='/home/kaverti/website/rendering/avatar.blend')
|
||||
bpy.data.objects['Head'].select = True
|
||||
bpy.data.materials['Head'].diffuse_color = hex_to_rgb('#F2C511')
|
||||
bpy.data.materials['Face'].diffuse_color = hex_to_rgb('#F2C511')
|
||||
|
@ -26,25 +26,25 @@ bpy.data.objects['Left Leg'].select = True
|
|||
bpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('#3D556E')
|
||||
bpy.data.objects['Right Leg'].select = True
|
||||
bpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('#C0382B')
|
||||
hat_import = bpy.ops.import_scene.obj(filepath='C:/Users/matth/Documents/GitHub/website/rendering/global/5873ada4b44681127842826981ff3d97.obj')
|
||||
hat_import = bpy.ops.import_scene.obj(filepath='/home/kaverti/website/rendering/global/5873ada4b44681127842826981ff3d97')
|
||||
hat = bpy.context.selected_objects[0]
|
||||
bpy.context.selected_objects[0].data.name = 'hat'
|
||||
bpy.context.selected_objects[0].name = 'hat'
|
||||
hat_material = bpy.data.materials.new('hat')
|
||||
hat_material.diffuse_shader = 'LAMBERT'
|
||||
hat.active_material = hat_material
|
||||
hat_image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/5873ada4b44681127842826981ff3d97.png')
|
||||
hat_image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/5873ada4b44681127842826981ff3d97.png')
|
||||
hat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')
|
||||
hat_texture.image = hat_image
|
||||
hat_add = bpy.data.objects['hat'].active_material.texture_slots.add()
|
||||
hat_add.texture = hat_texture
|
||||
face_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/fc56a2a4f55254f475ccea568402d2bc.png')
|
||||
face_Image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/fc56a2a4f55254f475ccea568402d2bc.png')
|
||||
bpy.data.textures['Face'].image = face_Image
|
||||
shirt_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/0.png')
|
||||
shirt_Image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/0.png')
|
||||
bpy.data.textures['Shirt'].image = shirt_Image
|
||||
bpy.data.textures['ShirtR'].image = shirt_Image
|
||||
bpy.data.textures['ShirtL'].image = shirt_Image
|
||||
pants_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/0.png')
|
||||
pants_Image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/0.png')
|
||||
bpy.data.textures['PantsR'].image = pants_Image
|
||||
bpy.data.textures['PantsL'].image = pants_Image
|
||||
for obj in bpy.data.objects:
|
||||
|
@ -53,5 +53,5 @@ for obj in bpy.data.objects:
|
|||
bpy.ops.view3d.camera_to_view_selected()
|
||||
scene = bpy.context.scene
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
scene.render.filepath = 'C:/xampp21/htdocs/user/avatars/full/e97a54fe29e2e3a2be14751f24113bd1.png'
|
||||
scene.render.filepath = '/var/www/html/cdn/user/avatars/full/4daef004be39e0c4c2f5b6498e6fcbaf.png'
|
||||
bpy.ops.render.render(write_still = 1)
|
|
@ -12,7 +12,7 @@ def hex_to_rgb(value):
|
|||
fin.append(g)
|
||||
fin.append(b)
|
||||
return tuple(fin)
|
||||
bpy.ops.wm.open_mainfile(filepath='C:/Users/matth/Documents/GitHub/website/rendering/avatarhs.blend')
|
||||
bpy.ops.wm.open_mainfile(filepath='/home/kaverti/website/rendering/avatarhs.blend')
|
||||
bpy.data.objects['Head'].select = True
|
||||
bpy.data.materials['Head'].diffuse_color = hex_to_rgb('#F2C511')
|
||||
bpy.data.materials['Face'].diffuse_color = hex_to_rgb('#F2C511')
|
||||
|
@ -26,28 +26,28 @@ bpy.data.objects['Left Leg'].select = True
|
|||
bpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('#3D556E')
|
||||
bpy.data.objects['Right Leg'].select = True
|
||||
bpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('#C0382B')
|
||||
hat_import = bpy.ops.import_scene.obj(filepath='C:/Users/matth/Documents/GitHub/website/rendering/global/5873ada4b44681127842826981ff3d97.obj')
|
||||
hat_import = bpy.ops.import_scene.obj(filepath='/home/kaverti/website/rendering/global/5873ada4b44681127842826981ff3d97')
|
||||
hat = bpy.context.selected_objects[0]
|
||||
bpy.context.selected_objects[0].data.name = 'hat'
|
||||
bpy.context.selected_objects[0].name = 'hat'
|
||||
hat_material = bpy.data.materials.new('hat')
|
||||
hat_material.diffuse_shader = 'LAMBERT'
|
||||
hat.active_material = hat_material
|
||||
hat_image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/5873ada4b44681127842826981ff3d97.png')
|
||||
hat_image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/5873ada4b44681127842826981ff3d97.png')
|
||||
hat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')
|
||||
hat_texture.image = hat_image
|
||||
hat_add = bpy.data.objects['hat'].active_material.texture_slots.add()
|
||||
hat_add.texture = hat_texture
|
||||
face_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/fc56a2a4f55254f475ccea568402d2bc.png')
|
||||
face_Image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/fc56a2a4f55254f475ccea568402d2bc.png')
|
||||
bpy.data.textures['Face'].image = face_Image
|
||||
shirt_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/0.png')
|
||||
shirt_Image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/0.png')
|
||||
bpy.data.textures['Shirt'].image = shirt_Image
|
||||
bpy.data.textures['ShirtR'].image = shirt_Image
|
||||
bpy.data.textures['ShirtL'].image = shirt_Image
|
||||
pants_Image = bpy.data.images.load(filepath = 'C:/Users/matth/Documents/GitHub/website/rendering/global/0.png')
|
||||
pants_Image = bpy.data.images.load(filepath = '/home/kaverti/website/rendering/global/0.png')
|
||||
bpy.data.textures['PantsR'].image = pants_Image
|
||||
bpy.data.textures['PantsL'].image = pants_Image
|
||||
scene = bpy.context.scene
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
scene.render.filepath = 'C:/xampp21/htdocs/user/avatars/headshot/e97a54fe29e2e3a2be14751f24113bd1.png'
|
||||
scene.render.filepath = '/var/www/html/cdn/user/avatars/headshot/4daef004be39e0c4c2f5b6498e6fcbaf.png'
|
||||
bpy.ops.render.render(write_still = 1)
|
129
server.js
129
server.js
|
@ -27,7 +27,6 @@ let config = require('./config/server.js')
|
|||
const expressSwagger = require('express-swagger-generator')(app);
|
||||
let swaggerJsdoc = require('swagger-jsdoc')
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: "3.0.0",
|
||||
|
@ -81,53 +80,95 @@ if(process.env.NODE_ENV !== 'test' && process.env.NODE_ENV !== 'production') {
|
|||
app.use(require('morgan')('dev'))
|
||||
}
|
||||
if(!config.maintenance) {
|
||||
app.use('/api/v1/user/', require('./routes/user'))
|
||||
app.use('/api/v1/passkey', require('./routes/user_passkey'))
|
||||
app.use('/api/v1/admin/admin_token', require('./routes/admin_token'))
|
||||
app.use('/api/v1/admin/passkey', require('./routes/user_passkey'))
|
||||
app.use('/api/v1/forums/category', require('./routes/category'))
|
||||
app.use('/api/v1/forums/thread', require('./routes/thread'))
|
||||
app.use('/api/v1/users/notification', require('./routes/notification'))
|
||||
app.use('/api/v1/forums/post', require('./routes/post'))
|
||||
app.use('/api/v1/kaverti/state', require('./routes/state'))
|
||||
app.use('/api/v1/users/report', require('./routes/report'))
|
||||
app.use('/api/v1/users/unban-request', require('./routes/UnbanRequest'))
|
||||
app.use('/api/v1/admin/ban', require('./routes/ban'))
|
||||
app.use('/api/v1/kaverti/search', require('./routes/search'))
|
||||
app.use('/api/v1/log', require('./routes/log'))
|
||||
app.use('/api/v1/forums/poll', require('./routes/poll'))
|
||||
app.use('/api/v1/forums/link_preview', require('./routes/link_preview'))
|
||||
app.use('/api/v1/kaverti/stats', require('./routes/stats'))
|
||||
app.use('/api/v1/users/login_status', require('./routes/login_status'))
|
||||
app.use('/api/v1/users/', require('./routes/userutils'))
|
||||
app.use('/api/v1/admin/killsession', require('./routes/admin_kill_session'))
|
||||
app.use('/api/v1/admin/ban', require('./routes/ban'))
|
||||
app.use('/api/v1/kaverti/job-apply', require('./routes/StaffApplications'))
|
||||
app.use('/api/v1/admin/', require('./routes/admin'))
|
||||
app.use('/api/v1/users/render', require('./routes/avatar'))
|
||||
app.use('/api/v1/avatar', require('./routes/avatar'))
|
||||
app.use('/api/v1/userinfo', require('./routes/userinfo'))
|
||||
app.use('/api/v1/wall', require('./routes/user_wall'))
|
||||
app.use('/api/v1/chat/conversation', require('./routes/conversation'));
|
||||
app.use('/api/v1/chat/message', require('./routes/message'));
|
||||
/* V1 Routes */
|
||||
app.use('/api/v1/user/', require('./v1_routes/user'))
|
||||
app.use('/api/v1/passkey', require('./v1_routes/user_passkey'))
|
||||
app.use('/api/v1/admin/admin_token', require('./v1_routes/admin_token'))
|
||||
app.use('/api/v1/admin/passkey', require('./v1_routes/user_passkey'))
|
||||
app.use('/api/v1/forums/category', require('./v1_routes/category'))
|
||||
app.use('/api/v1/forums/thread', require('./v1_routes/thread'))
|
||||
app.use('/api/v1/users/notification', require('./v1_routes/notification'))
|
||||
app.use('/api/v1/forums/post', require('./v1_routes/post'))
|
||||
app.use('/api/v1/kaverti/state', require('./v1_routes/state'))
|
||||
app.use('/api/v1/users/report', require('./v1_routes/report'))
|
||||
app.use('/api/v1/users/unban-request', require('./v1_routes/UnbanRequest'))
|
||||
app.use('/api/v1/admin/ban', require('./v1_routes/ban'))
|
||||
app.use('/api/v1/kaverti/search', require('./v1_routes/search'))
|
||||
app.use('/api/v1/log', require('./v1_routes/log'))
|
||||
app.use('/api/v1/forums/poll', require('./v1_routes/poll'))
|
||||
app.use('/api/v1/forums/link_preview', require('./v1_routes/link_preview'))
|
||||
app.use('/api/v1/kaverti/stats', require('./v1_routes/stats'))
|
||||
app.use('/api/v1/users/login_status', require('./v1_routes/login_status'))
|
||||
app.use('/api/v1/users/', require('./v1_routes/userutils'))
|
||||
app.use('/api/v1/admin/killsession', require('./v1_routes/admin_kill_session'))
|
||||
app.use('/api/v1/admin/ban', require('./v1_routes/ban'))
|
||||
app.use('/api/v1/kaverti/job-apply', require('./v1_routes/StaffApplications'))
|
||||
app.use('/api/v1/admin/', require('./v1_routes/admin'))
|
||||
app.use('/api/v1/users/render', require('./v1_routes/avatar'))
|
||||
app.use('/api/v1/avatar', require('./v1_routes/avatar'))
|
||||
app.use('/api/v1/userinfo', require('./v1_routes/userinfo'))
|
||||
app.use('/api/v1/wall', require('./v1_routes/user_wall'))
|
||||
app.use('/api/v1/chat/conversation', require('./v1_routes/conversation'));
|
||||
app.use('/api/v1/chat/message', require('./v1_routes/message'));
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs, {explorer: true}));
|
||||
app.use('/api/v1/teams/', require('./routes/team'))
|
||||
app.use('/api/v1/teams/admin/', require('./routes/team_admin'))
|
||||
app.use('/api/v1/teams/wall/', require('./routes/team_wall'))
|
||||
app.use('/api/v1/marketplace', require('./routes/marketplace'))
|
||||
app.use('/api/v1/inventory', require('./routes/inventory'))
|
||||
app.use('/api/v1/transactions', require('./routes/transactions'))
|
||||
app.use('/api/v1/relationships', require('./routes/relationship'))
|
||||
app.use('/api/v1/nodeinfo', require('./routes/node'))
|
||||
app.use('/api/v1/node/', require('./routes/node'))
|
||||
app.use('/api/v1/nodes/', require('./routes/node'))
|
||||
app.use('/api/v1/awards/', require('./routes/award'))
|
||||
app.use('/api/v1/teams/', require('./v1_routes/team'))
|
||||
app.use('/api/v1/teams/admin/', require('./v1_routes/team_admin'))
|
||||
app.use('/api/v1/teams/wall/', require('./v1_routes/team_wall'))
|
||||
app.use('/api/v1/marketplace', require('./v1_routes/marketplace'))
|
||||
app.use('/api/v1/inventory', require('./v1_routes/inventory'))
|
||||
app.use('/api/v1/transactions', require('./v1_routes/transactions'))
|
||||
app.use('/api/v1/relationships', require('./v1_routes/relationship'))
|
||||
app.use('/api/v1/about', require('./v1_routes/about'))
|
||||
/* V2 Routes */
|
||||
app.use('/api/v2/about', require('./v2_routes/about'))
|
||||
app.use('/api/v2/user/', require('./v2_routes/user'))
|
||||
app.use('/api/v2/passkey', require('./v2_routes/user_passkey'))
|
||||
app.use('/api/v2/admin/admin_token', require('./v2_routes/admin_token'))
|
||||
app.use('/api/v2/admin/passkey', require('./v2_routes/user_passkey'))
|
||||
app.use('/api/v2/forums/category', require('./v2_routes/category'))
|
||||
app.use('/api/v2/forums/thread', require('./v2_routes/thread'))
|
||||
app.use('/api/v2/users/notification', require('./v2_routes/notification'))
|
||||
app.use('/api/v2/forums/post', require('./v2_routes/post'))
|
||||
app.use('/api/v2/kaverti/state', require('./v2_routes/state'))
|
||||
app.use('/api/v2/users/report', require('./v2_routes/report'))
|
||||
app.use('/api/v2/users/unban-request', require('./v2_routes/UnbanRequest'))
|
||||
app.use('/api/v2/admin/ban', require('./v2_routes/ban'))
|
||||
app.use('/api/v2/kaverti/search', require('./v2_routes/search'))
|
||||
app.use('/api/v2/log', require('./v2_routes/log'))
|
||||
app.use('/api/v2/forums/poll', require('./v2_routes/poll'))
|
||||
app.use('/api/v2/forums/link_preview', require('./v2_routes/link_preview'))
|
||||
app.use('/api/v2/kaverti/stats', require('./v2_routes/stats'))
|
||||
app.use('/api/v2/users/login_status', require('./v2_routes/login_status'))
|
||||
app.use('/api/v2/users/', require('./v2_routes/userutils'))
|
||||
app.use('/api/v2/admin/killsession', require('./v2_routes/admin_kill_session'))
|
||||
app.use('/api/v2/admin/ban', require('./v2_routes/ban'))
|
||||
app.use('/api/v2/kaverti/job-apply', require('./v2_routes/StaffApplications'))
|
||||
app.use('/api/v2/admin/', require('./v2_routes/admin'))
|
||||
app.use('/api/v2/users/render', require('./v2_routes/avatar'))
|
||||
app.use('/api/v2/avatar', require('./v2_routes/avatar'))
|
||||
app.use('/api/v2/userinfo', require('./v2_routes/userinfo'))
|
||||
app.use('/api/v2/wall', require('./v2_routes/user_wall'))
|
||||
app.use('/api/v2/chat/conversation', require('./v2_routes/conversation'));
|
||||
app.use('/api/v2/chat/message', require('./v2_routes/message'));
|
||||
app.use('/api/v2/teams/', require('./v2_routes/team'))
|
||||
app.use('/api/v2/teams/admin/', require('./v2_routes/team_admin'))
|
||||
app.use('/api/v2/teams/wall/', require('./v2_routes/team_wall'))
|
||||
app.use('/api/v2/marketplace', require('./v2_routes/marketplace'))
|
||||
app.use('/api/v2/inventory', require('./v2_routes/inventory'))
|
||||
app.use('/api/v2/transactions', require('./v2_routes/transactions'))
|
||||
app.use('/api/v2/relationships', require('./v2_routes/relationship'))
|
||||
app.use('/api/v2/nodeinfo', require('./v2_routes/node'))
|
||||
app.use('/api/v2/node/', require('./v2_routes/node'))
|
||||
app.use('/api/v2/nodes/', require('./v2_routes/node'))
|
||||
app.use('/api/v2/awards/', require('./v2_routes/award'))
|
||||
app.use('/api/v2/blog', require('./v2_routes/blog'))
|
||||
app.use('/api/v2/feedback', require('./v2_routes/feedback'))
|
||||
app.use(require('./lib/errorHandler'))
|
||||
app.set('trust proxy', true)
|
||||
} else {
|
||||
app.use('/api/v1/userinfo', require('./routes/userinfo'))
|
||||
app.use('/api/v1/kaverti/state', require('./routes/state'))
|
||||
app.use('/api/v1/', require('./routes/maintenance'))
|
||||
app.use('/api/v1/userinfo', require('./v1_routes/userinfo'))
|
||||
app.use('/api/v1/kaverti/state', require('./v1_routes/state'))
|
||||
app.use('/api/v1/', require('./v1_routes/maintenance'))
|
||||
app.use(require('./lib/errorHandler'))
|
||||
app.set('trust proxy', true)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
router.get('/', async(req, res, next) => {
|
||||
res.status(200)
|
||||
res.json({"version": 1, "message": "Version 1 of the Kaverti API is considered stable, and will not have any drastic changes made", "warning": false, "deprecated": false, "deprecationDate": "Unknown", "client": "Stable"})
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,367 @@
|
|||
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, AuditLog, Team, Item, Post, ProfilePicture, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
} = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
var fs = require("fs");
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const cryptoRandomString = require("crypto-random-string")
|
||||
const limiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 3,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 3 requests to this endpoint per minute, if you performed an action such as avatar color changing, those changes were saved, however your avatar was not re-rendered, please re-render your avatar.\",\"status\":429}]}"
|
||||
});
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.put('/user/scrub', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
if(req.autosan.body.description === "descscram") {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.autosan.body.user
|
||||
}})
|
||||
if(user.admin) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to modify ' + req.autosan.body.user + ' but an error was thrown (Is admin, scrub description).'})
|
||||
throw Errors.modifyAdminUser
|
||||
}
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' modified user ' + req.autosan.body.user + ' and succeeded (scrub description).'})
|
||||
let userUpdate = await User.update({ description: "Description was removed by an administrator"}, { where: {
|
||||
username: req.autosan.body.user
|
||||
}})
|
||||
res.status(200)
|
||||
res.json({success: "true"})
|
||||
|
||||
} else if(req.body.username === "usernamescram") {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.autosan.body.user
|
||||
}})
|
||||
if(user.admin) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to modify ' + req.autosan.body.user + ' but an error was thrown (Is admin, scrub username).'})
|
||||
throw Errors.modifyAdminUser
|
||||
}
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' modified user ' + req.autosan.body.user + ' and succeeded (scrub username).'})
|
||||
let userUpdate = await User.update({username: Math.random().toString(36).substring(2)}, {
|
||||
where: {
|
||||
username: req.autosan.body.user
|
||||
}
|
||||
})
|
||||
res.json({success: true})
|
||||
} else {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to modify ' + req.autosan.body.user + ' but an error was thrown (unknown, scrub username).'})
|
||||
res.json({ success: false })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/user/modify', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
if(req.body.username) {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.body.username
|
||||
}})
|
||||
let user1 = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.accountDoesNotExist
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' modified ' + req.body.username + ' and succeeded (changed roles).'})
|
||||
if(user.admin && !user1.executive) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to modify ' + req.body.username + ' but an error was thrown (Is admin, changed roles).'})
|
||||
throw Errors.modifyAdminUser
|
||||
}
|
||||
if(user.executive) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to modify ' + req.body.username + ' but an error was thrown (Is executive, changed roles).'})
|
||||
throw Errors.modifyAdminUser
|
||||
}
|
||||
if(user1.executive) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' modified ' + req.body.username + ' and succeeded (changed roles, executive action).'})
|
||||
let userUpdate = await User.update({
|
||||
booster: req.body.booster,
|
||||
bot: req.body.bot,
|
||||
system: req.body.system,
|
||||
admin: req.body.admin,
|
||||
hidden: req.body.hidden
|
||||
}, {
|
||||
where: {
|
||||
username: req.body.username
|
||||
}
|
||||
})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' modified ' + req.body.username + ' and succeeded (changed roles, admin action).'})
|
||||
let userUpdate = await User.update({
|
||||
booster: req.body.booster,
|
||||
bot: req.body.bot,
|
||||
system: req.body.system,
|
||||
hidden: req.body.hidden
|
||||
}, {
|
||||
where: {
|
||||
username: req.body.username
|
||||
}
|
||||
})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
}
|
||||
} else {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to modify ' + req.body.username + ' but an error was thrown (account does not exist).'})
|
||||
res.status(400)
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/privileges', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
attributes: {include: ['username', 'admin', 'executive'], exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset', 'description', 'theme', 'contributor', 'passwordResetOptOut', 'picture', 'createdAt', 'updatedAt', 'id']},
|
||||
where: {username: req.userData.username},
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
res.json(user)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/teams/pending', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
let team = await Team.findAll({where: {approved: false, banned: false}})
|
||||
if(!team) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(team)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/teams/approve', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
let team = await Team.findOne({where: {username: req.body.username}})
|
||||
if(!team) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
if(req.body.approve) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' approved the ' + req.body.username + ' team and succeeded (approved team).'})
|
||||
await team.update({approved: true});
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else if(!req.body.approve && req.body.reason) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' banned the ' + req.body.username + ' team and succeeded (banned team).'})
|
||||
await team.update({banned: true, banReason: req.body.reason})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/marketplace/pending', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
let item = await Item.findAll({where: {approved: false, deleted: false}, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }})
|
||||
if(!item) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(item)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/marketplace/approve', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
let item = await Item.findOne({where: {id: req.body.id}})
|
||||
if(!item) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
if(req.body.approve && !req.body.delete) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' approved the Marketplace Item MID: ' + req.body.id + ' and succeeded (approved marketplace item).'})
|
||||
await item.update({approved: true});
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else if(req.body.delete && !req.body.approve) {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' removed the Marketplace Item MID: ' + req.body.id + ' and succeeded (removed marketplace item).'})
|
||||
await item.update({deleted: true, approved: false});
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/logs', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
let logs = await AuditLog.findAll()
|
||||
if(!logs) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(logs)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put("/user/avatar", limiter, auth, async(req, res, next) => {
|
||||
let userCheck = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
if(!userCheck.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
if(!req.userData.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.body.user
|
||||
}
|
||||
})
|
||||
if(!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
const { exec } = require('child_process');
|
||||
if(user.pantsId) {
|
||||
var pantsModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.pantsId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.hatId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.faceId) {
|
||||
var faceModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.faceId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.shirtId
|
||||
}
|
||||
})
|
||||
}
|
||||
let rootPathRender = "C:/Users/matth/Documents/GitHub/website/";
|
||||
let img2 = cryptoRandomString({length: 32})
|
||||
let img = img2
|
||||
var blendFilePath = rootPathRender + "rendering/avatar.blend";
|
||||
var blendFilePathHs = rootPathRender + "rendering/avatarhs.blend";
|
||||
var imageSavePath = "C:/winnmp/www/user/avatars/full/" + img + ".png";
|
||||
var imageSavePathHS = "C:/winnmp/www/user/avatars/headshot/" + img + ".png";
|
||||
var pythonFilePath = "rendering/usercontent/"+req.userData.UserId+".py";
|
||||
if(user.faceId) {
|
||||
var faceFilePath = rootPathRender + "rendering/global/" + faceModel.sourceFile + ".png";
|
||||
} else {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/" + shirtModel.sourceFile + ".png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/" + pantsModel.sourceFile + ".png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatFilePath = rootPathRender + "rendering/global/" + hatModel.sourceFile + ".obj"
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
|
||||
var imports = "import bpy";
|
||||
var functions = "def hex_to_rgb(value):\n gamma = 2.05\n value = value.lstrip('#')\n lv = len(value)\n fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))\n r = pow(fin[0] / 255, gamma)\n g = pow(fin[1] / 255, gamma)\n b = pow(fin[2] / 255, gamma)\n fin.clear()\n fin.append(r)\n fin.append(g)\n fin.append(b)\n return tuple(fin)";
|
||||
var blenderImport = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePath+"')";
|
||||
var blenderImportHs = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePathHs+"')";
|
||||
var headColor = "bpy.data.objects['Head'].select = True\nbpy.data.materials['Head'].diffuse_color = hex_to_rgb('"+user.headColor+"')\nbpy.data.materials['Face'].diffuse_color = hex_to_rgb('"+user.headColor+"')";
|
||||
var leftArmColor = "bpy.data.objects['Left Arm'].select = True\nbpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('"+user.leftArmColor+"')";
|
||||
var rightArmColor = "bpy.data.objects['Right Arm'].select = True\nbpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('"+user.rightArmColor+"')";
|
||||
var bodyColor = "bpy.data.objects['Torso'].select = True\nbpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('"+user.color+"')";
|
||||
var leftLegColor = "bpy.data.objects['Left Leg'].select = True\nbpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('"+user.leftLegColor+"')";
|
||||
var rightLegColor = "bpy.data.objects['Right Leg'].select = True\nbpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('"+user.rightLegColor+"')";
|
||||
var colors = headColor+"\n"+leftArmColor+"\n"+bodyColor+"\n"+rightArmColor+"\n"+leftLegColor+"\n"+rightLegColor;
|
||||
|
||||
if(user.hatId) {
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
var face = "face_Image = bpy.data.images.load(filepath = '"+faceFilePath+"')\nbpy.data.textures['Face'].image = face_Image";
|
||||
var shirt = "shirt_Image = bpy.data.images.load(filepath = '"+shirtFilePath+"')\nbpy.data.textures['Shirt'].image = shirt_Image\nbpy.data.textures['ShirtR'].image = shirt_Image\nbpy.data.textures['ShirtL'].image = shirt_Image";
|
||||
var pants = "pants_Image = bpy.data.images.load(filepath = '"+pantsFilePath+"')\nbpy.data.textures['PantsR'].image = pants_Image\nbpy.data.textures['PantsL'].image = pants_Image";
|
||||
var render = "for obj in bpy.data.objects:\n obj.select = False\n bpy.ops.object.select_all(action='SELECT')\nbpy.ops.view3d.camera_to_view_selected()\nscene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePath+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var renderHS = "scene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePathHS+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var python = imports+"\n"+functions+"\n"+blenderImport+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+render;
|
||||
var pythonHS = imports+"\n"+functions+"\n"+blenderImportHs+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+renderHS;
|
||||
fs.writeFile("rendering/usercontent/"+req.userData.UserId+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
fs.writeFile("rendering/usercontent/hs/"+req.userData.UserId+".py", pythonHS, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
|
||||
exec("blender -b -P rendering/usercontent/"+req.userData.UserId+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
});
|
||||
exec("blender -b -P rendering/usercontent/hs/"+req.userData.UserId+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
});
|
||||
user.update({picture: img})
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,20 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors.js')
|
||||
let AdminToken = require('../models').AdminToken
|
||||
let AuditLog = require('../models').AuditLog
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' attempted to use a deprecated administration route (admin token)'})
|
||||
if(!req.userData.loggedIn && !req.userData.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
} else {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,96 @@
|
|||
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')
|
||||
let {
|
||||
User, Post, ProfilePicture, AdminToken, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
} = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
|
||||
function setUserSession(req, res, username, UserId, admin) {
|
||||
req.userData.loggedIn = true
|
||||
req.userData.username = username
|
||||
req.userData.UserId = UserId
|
||||
|
||||
res.cookie('username', username)
|
||||
//Not for security purposes, just so client side can determine
|
||||
//to show certain parts of ui or not (i.e. could trivially be spoofed
|
||||
//but the server would not accept any api requests)
|
||||
res.cookie('admin', !!admin)
|
||||
|
||||
if(admin) { req.userData.admin = true }
|
||||
}
|
||||
router.get('/', async function(req, res) {
|
||||
if(req.userData.admin) {
|
||||
try {
|
||||
let sortFields = {
|
||||
createdAt: 'X.id',
|
||||
username: 'X.username',
|
||||
threadCount: 'threadCount',
|
||||
postCount: 'postCount',
|
||||
email: 'X.email',
|
||||
bot: 'X.bot'
|
||||
};
|
||||
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 = '';
|
||||
}
|
||||
|
||||
|
||||
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.bot, X.email, X.createdAt, 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' ? 'ASC' : 'DESC'}
|
||||
LIMIT 15
|
||||
OFFSET ${offset}
|
||||
`;
|
||||
|
||||
let users = await sequelize.query(sql, {
|
||||
model: User,
|
||||
bind: {search: req.query.search + '%'}
|
||||
});
|
||||
|
||||
res.json(users)
|
||||
res.json(users)
|
||||
} catch (e) { next(e) }
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,150 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
let config = require('../config/server.js')
|
||||
|
||||
var fs = require("fs");
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const { exec } = require('child_process');
|
||||
const cryptoRandomString = require("crypto-random-string")
|
||||
const limiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 3,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 3 requests to this endpoint per minute, if you performed an action such as avatar color changing, those changes were saved, however your avatar was not re-rendered, please re-render your avatar.\",\"status\":429}]}"
|
||||
});
|
||||
let { User, Sequelize, Item } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
var randomString = (Math.random().toString(36).substring(2))
|
||||
router.post("/refresh", limiter, auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
id: req.userData.UserId
|
||||
}
|
||||
})
|
||||
if(!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.pantsId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.hatId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.faceId) {
|
||||
var faceModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.faceId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.shirtId
|
||||
}
|
||||
})
|
||||
}
|
||||
let rootPathRender = config.rootFolder;
|
||||
let img2 = cryptoRandomString({length: 32})
|
||||
let img = img2
|
||||
var blendFilePath = rootPathRender + "rendering/avatar.blend";
|
||||
var blendFilePathHs = rootPathRender + "rendering/avatarhs.blend";
|
||||
var imageSavePath = config.cdnFolder + "user/avatars/full/" + img + ".png";
|
||||
var imageSavePathHS = config.cdnFolder + "user/avatars/headshot/" + img + ".png";
|
||||
var pythonFilePath = "rendering/usercontent/"+req.userData.UserId+".py";
|
||||
if(user.faceId) {
|
||||
var faceFilePath = rootPathRender + "rendering/global/" + faceModel.sourceFile;
|
||||
} else {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/" + shirtModel.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/" + pantsModel.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatFilePath = rootPathRender + "rendering/global/" + hatModel.sourceFile + ".obj"
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
|
||||
var imports = "import bpy";
|
||||
var functions = "def hex_to_rgb(value):\n gamma = 2.05\n value = value.lstrip('#')\n lv = len(value)\n fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))\n r = pow(fin[0] / 255, gamma)\n g = pow(fin[1] / 255, gamma)\n b = pow(fin[2] / 255, gamma)\n fin.clear()\n fin.append(r)\n fin.append(g)\n fin.append(b)\n return tuple(fin)";
|
||||
var blenderImport = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePath+"')";
|
||||
var blenderImportHs = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePathHs+"')";
|
||||
var headColor = "bpy.data.objects['Head'].select = True\nbpy.data.materials['Head'].diffuse_color = hex_to_rgb('"+user.headColor+"')\nbpy.data.materials['Face'].diffuse_color = hex_to_rgb('"+user.headColor+"')";
|
||||
var leftArmColor = "bpy.data.objects['Left Arm'].select = True\nbpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('"+user.leftArmColor+"')";
|
||||
var rightArmColor = "bpy.data.objects['Right Arm'].select = True\nbpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('"+user.rightArmColor+"')";
|
||||
var bodyColor = "bpy.data.objects['Torso'].select = True\nbpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('"+user.color+"')";
|
||||
var leftLegColor = "bpy.data.objects['Left Leg'].select = True\nbpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('"+user.leftLegColor+"')";
|
||||
var rightLegColor = "bpy.data.objects['Right Leg'].select = True\nbpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('"+user.rightLegColor+"')";
|
||||
var colors = headColor+"\n"+leftArmColor+"\n"+bodyColor+"\n"+rightArmColor+"\n"+leftLegColor+"\n"+rightLegColor;
|
||||
|
||||
if(user.hatId) {
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
var face = "face_Image = bpy.data.images.load(filepath = '"+faceFilePath+"')\nbpy.data.textures['Face'].image = face_Image";
|
||||
var shirt = "shirt_Image = bpy.data.images.load(filepath = '"+shirtFilePath+"')\nbpy.data.textures['Shirt'].image = shirt_Image\nbpy.data.textures['ShirtR'].image = shirt_Image\nbpy.data.textures['ShirtL'].image = shirt_Image";
|
||||
var pants = "pants_Image = bpy.data.images.load(filepath = '"+pantsFilePath+"')\nbpy.data.textures['PantsR'].image = pants_Image\nbpy.data.textures['PantsL'].image = pants_Image";
|
||||
var render = "for obj in bpy.data.objects:\n obj.select = False\n bpy.ops.object.select_all(action='SELECT')\nbpy.ops.view3d.camera_to_view_selected()\nscene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePath+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var renderHS = "scene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePathHS+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var python = imports+"\n"+functions+"\n"+blenderImport+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+render;
|
||||
var pythonHS = imports+"\n"+functions+"\n"+blenderImportHs+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+renderHS;
|
||||
fs.writeFile("rendering/usercontent/"+req.userData.UserId+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
fs.writeFile("rendering/usercontent/hs/"+req.userData.UserId+".py", pythonHS, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
|
||||
exec("blender -b -P rendering/usercontent/"+req.userData.UserId+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
});
|
||||
exec("blender -b -P rendering/usercontent/hs/"+req.userData.UserId+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
});
|
||||
user.update({picture: img})
|
||||
});
|
||||
|
||||
router.put('/colors', auth, async (req, res, next) => {
|
||||
try {
|
||||
let user = await User.findOne({
|
||||
where: {id: req.userData.UserId}
|
||||
})
|
||||
if(user) {
|
||||
user.update({
|
||||
color: req.body.color,
|
||||
headColor: req.body.headColor,
|
||||
leftArmColor: req.body.leftArmColor,
|
||||
rightArmColor: req.body.rightArmColor,
|
||||
leftLegColor: req.body.leftLegColor,
|
||||
rightLegColor: req.body.rightLegColor,
|
||||
})
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.unknown
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
module.exports = router;
|
|
@ -0,0 +1,77 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { User, Ban, AuditLog, Sequelize } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let user = await User.findOne({ where: { username: req.body.username } })
|
||||
if(!user) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'user does not exist',
|
||||
value: req.body.userId
|
||||
})
|
||||
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' banned ' + req.body.username + ' and succeeded (banned).'})
|
||||
|
||||
let ban = await Ban.create({
|
||||
message: req.body.message,
|
||||
ipBanned: req.body.ipBanned,
|
||||
ReadOnlyMode: req.body.ReadOnlyMode,
|
||||
DisableLogin: req.body.DisableLogin,
|
||||
})
|
||||
await ban.setUser(user)
|
||||
|
||||
let ret = await ban.reload({
|
||||
include: [{
|
||||
model: User,
|
||||
attributes: ['username', 'description', 'color', 'createdAt']
|
||||
}]
|
||||
})
|
||||
|
||||
res.json(ret.toJSON())
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let bans = await Ban.findAll({
|
||||
include: [User]
|
||||
})
|
||||
|
||||
res.json(bans.map(b => b.toJSON()))
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.delete('/:ban_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let ban = await Ban.findByPk(req.params.ban_id)
|
||||
if(!ban) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'ban does not exist',
|
||||
value: req.body.userId
|
||||
})
|
||||
AuditLog.create({UserId: req.userData.UserId, action: req.userData.username + ' unbanned UID: ' + ban.UserId + ' and succeeded (unbanned).'})
|
||||
|
||||
await ban.destroy()
|
||||
res.json({ success: true })
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -52,7 +52,7 @@ let getPostSchema = {
|
|||
router.get('/:conversationId', validation(getPostSchema), auth, async(req, res, next) => {
|
||||
try {
|
||||
let conversation = await conversationController.get(
|
||||
req.userData.id, +req.params.conversationId, +req.query.page
|
||||
req.userData.UserId, +req.params.conversationId, +req.query.page
|
||||
);
|
||||
|
||||
res.json(conversation);
|
||||
|
@ -71,7 +71,7 @@ router.put('/:conversationId', validation(putPostSchema), auth, async(req, res,
|
|||
try {
|
||||
let conversationId = +req.params.conversationId;
|
||||
res.json(
|
||||
await conversationController.updateLastRead(conversationId, req.userData.id)
|
||||
await conversationController.updateLastRead(conversationId, req.userData.UserId)
|
||||
);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ router.put('/:conversationId/name', validation(putNameSchema), auth, async(req,
|
|||
let name = req.body.name;
|
||||
|
||||
res.json(
|
||||
await conversationController.updateName(conversationId, req.userData.id, name)
|
||||
await conversationController.updateName(conversationId, req.userData.UserId, name)
|
||||
);
|
||||
} catch (e) { next(e); }
|
||||
});
|
|
@ -9,7 +9,7 @@ let { Ban, Item, Inventory, ItemCategory, User, sequelize, Sequelize } = require
|
|||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {UserId: req.userData.id},
|
||||
where: {UserId: req.userData.UserId},
|
||||
include: { model: Item, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } }
|
||||
}
|
||||
let transaction = await Inventory.findAll(queryObj)
|
|
@ -0,0 +1,556 @@
|
|||
|
||||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
let multer = require('multer')
|
||||
let cryptoRandomString = require("crypto-random-string");
|
||||
let config = require('../config/server.js')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let pagination = require('../lib/pagination')
|
||||
let { Ban, Item, Transaction, Inventory, ItemCategory, User, Ip, sequelize, Sequelize } = require('../models')
|
||||
let img2 = cryptoRandomString({length: 32})
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const limiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 100,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 1 request to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, config.rootFolder + '/rendering/global/');
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
cb(null, cryptoRandomString({length: 32}) + ".png")
|
||||
}
|
||||
});
|
||||
|
||||
var upload = multer({
|
||||
storage: storage,
|
||||
fileFilter: (req, file, cb) => {
|
||||
if (file.mimetype === "image/png") {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(null, false);
|
||||
return cb(new Error('Only .png is allowed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
const storageObj = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, config.rootFolder + '/rendering/global/');
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
cb(null, img2 + '.obj')
|
||||
}
|
||||
});
|
||||
|
||||
var uploadObj = multer({
|
||||
storage: storageObj
|
||||
});
|
||||
|
||||
router.get('/', async(req, res) => {
|
||||
try {
|
||||
let categories = await ItemCategory.findAll()
|
||||
|
||||
res.json(categories)
|
||||
} catch (e) {
|
||||
res.status(500)
|
||||
res.json({
|
||||
errors: [Errors.unknown]
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
router.get('/:category', async(req, res, next) => {
|
||||
try {
|
||||
let threads, threadsLatestPost, resThreads, user
|
||||
let { from } = pagination.getPaginationProps(req.query, true)
|
||||
let offset = Number.isInteger(+req.query.offset) ? +req.query.offset : 0;
|
||||
console.log(offset)
|
||||
let limit = 30
|
||||
|
||||
if(req.query.username) {
|
||||
user = await User.findOne({ where: { username: req.query.username }})
|
||||
}
|
||||
console.log(req.query.offset + 'OFFSET')
|
||||
function threadInclude(order) {
|
||||
let options = {
|
||||
model: Item,
|
||||
order: [['id', order]],
|
||||
limit: 30,
|
||||
offset: offset,
|
||||
subQuery: false,
|
||||
where: {deleted: false, approved: true},
|
||||
include: [
|
||||
{ model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] },
|
||||
]
|
||||
}
|
||||
|
||||
if(user) {
|
||||
options.where.userId = user.id
|
||||
}
|
||||
|
||||
if(from !== null) {
|
||||
options.where.id = { $lte: from }
|
||||
}
|
||||
|
||||
return [options]
|
||||
}
|
||||
if(req.params.category === 'ALL') {
|
||||
threads = await Item.findAll( threadInclude('ASC')[0] )
|
||||
threadsLatestPost = await Item.findAll( threadInclude('DESC')[0] )
|
||||
} else {
|
||||
threads = await ItemCategory.findOne({
|
||||
where: { id: req.params.category },
|
||||
include: threadInclude('DESC')
|
||||
})
|
||||
}
|
||||
console.log(threads)
|
||||
if(Array.isArray(threads)) {
|
||||
resThreads = {
|
||||
name: 'All',
|
||||
value: 'ALL',
|
||||
Threads: threads,
|
||||
meta: {}
|
||||
}
|
||||
|
||||
threadsLatestPost = { Threads: threadsLatestPost }
|
||||
} else {
|
||||
resThreads = threads.toJSON()
|
||||
resThreads.meta = {}
|
||||
}
|
||||
|
||||
res.json(resThreads)
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:id', async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
if (!marketplace) throw Errors.invalidItem
|
||||
if(marketplace.deleted) {
|
||||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
attributes: ['name', 'deleted', 'createdAt', 'id', 'updatedAt', 'price'],
|
||||
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
res.json({createdAt: marketplace.createdAt, updatedAt: marketplace.createdAt, id: marketplace.id, name: "Item unavailable", deleted: true, price: 0, User: marketplace.User, description: 'Item is currently unavailable at this time.', approved: false})
|
||||
}
|
||||
if(!marketplace.approved) {
|
||||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
attributes: ['name', 'deleted', 'createdAt', 'id', 'updatedAt', 'price'],
|
||||
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
res.json({createdAt: marketplace.createdAt, updatedAt: marketplace.createdAt, id: marketplace.id, name: "Item is pending admin approval", deleted: false, price: 0, User: marketplace.User, description: 'Item is currently unavailable at this time.', approved: false})
|
||||
}
|
||||
res.json(marketplace.toJSON())
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.get('/purchase/:id', auth, async(req, res, next) => {
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
}
|
||||
let queryObj2 = {
|
||||
where: {username: req.userData.username},
|
||||
}
|
||||
let queryObj3 = {
|
||||
where: {UserId: req.userData.UserId, ItemId: req.params.id},
|
||||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
let user = await User.findOne(queryObj2)
|
||||
let inventory = await Inventory.findOne(queryObj3)
|
||||
if (!marketplace) throw Errors.invalidItem
|
||||
let queryObj4 = {
|
||||
where: {id: marketplace.UserId},
|
||||
}
|
||||
let payUser = await User.findOne(queryObj4)
|
||||
if (user.koins < marketplace.price && !marketplace.saleEnabled) {
|
||||
throw Errors.insufficientKoins
|
||||
}
|
||||
if (user.koins < marketplace.salePrice && marketplace.saleEnabled) {
|
||||
throw Errors.insufficientKoins
|
||||
}
|
||||
if (marketplace.offSale) {
|
||||
throw Errors.offSale
|
||||
}
|
||||
if(marketplace.deleted) {
|
||||
throw Errors.itemUnavailable
|
||||
}
|
||||
if(!marketplace.approved) {
|
||||
throw Errors.itemUnavailable
|
||||
}
|
||||
if(inventory && !marketplace.limited) {
|
||||
throw Errors.itemOwned
|
||||
}
|
||||
if(inventory && marketplace.limited) {
|
||||
throw Errors.itemOwnedLimited
|
||||
}
|
||||
if(marketplace.limited && marketplace.purchases === marketplace.quantityAllowed && marketplace.quantityAllowed !== 0) {
|
||||
throw Errors.itemUnavailable
|
||||
}
|
||||
if(marketplace.saleEnabled) {
|
||||
const UserId = user.id
|
||||
const priceTotal = marketplace.salePrice
|
||||
await user.removeKoins(priceTotal)
|
||||
await payUser.addKoins(priceTotal)
|
||||
await Transaction.create({UserId: payUser.id, priceOfPurchase: priceTotal, text: user.username + ' purchased your Marketplace item, ' + marketplace.name + ' (' + marketplace.id + ') for ' + priceTotal + ' koins, you have been given ' + priceTotal + ' koins. Your Marketplace item was on sale when this transaction was made.', itemId: marketplace.id, limited: marketplace.limited})
|
||||
await Transaction.create({UserId: UserId, priceOfPurchase: priceTotal, text: user.username + ' purchased ' + marketplace.name + ' (' + marketplace.id + ') from the Marketplace for ' + priceTotal + ' koins', itemId: marketplace.id, limited: marketplace.limited, ipId: Ip.createIfNotExists(req.ip, user)})
|
||||
await marketplace.update({purchases: +1})
|
||||
await Inventory.create({UserId: UserId, purchasePrice: priceTotal, isReselling: false, isResellingPrice: 0, ItemId: marketplace.id, boughtFrom: marketplace.UserId, resellType: 0, auctionId: 0})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
const UserId = user.id
|
||||
const priceTotal = marketplace.price
|
||||
await user.removeKoins(priceTotal)
|
||||
await payUser.addKoins(priceTotal)
|
||||
await Transaction.create({UserId: payUser.id, priceOfPurchase: priceTotal, text: user.username + ' purchased your Marketplace item, ' + marketplace.name + ' (' + marketplace.id + ') for ' + priceTotal + ' koins, you have been given ' + priceTotal + ' koins.', itemId: marketplace.id, limited: marketplace.limited})
|
||||
await Transaction.create({UserId: UserId, priceOfPurchase: priceTotal, text: user.username + ' purchased ' + marketplace.name + ' (' + marketplace.id + ') from the Marketplace for ' + priceTotal + ' koins', itemId: marketplace.id, limited: marketplace.limited, ipId: Ip.createIfNotExists(req.ip, user)})
|
||||
await marketplace.updatePurchaseCount()
|
||||
await Inventory.create({UserId: UserId, purchasePrice: priceTotal, isReselling: false, isResellingPrice: 0, ItemId: marketplace.id, boughtFrom: marketplace.UserId, resellType: 0, auctionId: 0})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
}
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.get('/check/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
}
|
||||
let queryObj3 = {
|
||||
where: {UserId: req.userData.UserId, ItemId: req.params.id},
|
||||
}
|
||||
let inventory = await Inventory.findOne(queryObj3)
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
if(marketplace) {
|
||||
if (inventory) {
|
||||
res.status(200)
|
||||
res.json({purchased: true})
|
||||
} else {
|
||||
res.status(200)
|
||||
res.json({purchased: false})
|
||||
}
|
||||
} else {
|
||||
throw Errors.invalidItem
|
||||
}
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('/rerender/:id', auth, limiter, async (req, res, next) => {
|
||||
try {
|
||||
let user = await User.findOne({
|
||||
where: {id: req.userData.UserId}
|
||||
});
|
||||
if(!req.userData.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
if(!user.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let marketplace = await Item.findOne({
|
||||
where: {id: req.params.id}
|
||||
});
|
||||
if(marketplace) {
|
||||
if(marketplace.ItemCategoryId === 0) {
|
||||
var type = "hat"
|
||||
} else if(marketplace.ItemCategoryId === 1) {
|
||||
var type = "face"
|
||||
} else if(marketplace.ItemCategoryId === 2) {
|
||||
var type = "shirt"
|
||||
} else if(marketplace.ItemCategoryId === 3) {
|
||||
var type = "pants"
|
||||
}
|
||||
const { exec } = require('child_process');
|
||||
var fs = require("fs");
|
||||
let rootPathRender = config.rootFolder + "/";
|
||||
var blendFilePath = rootPathRender + "rendering/avatar.blend";
|
||||
var img4 = marketplace.previewFile
|
||||
var imageSavePath = config.cdnFolder + "/marketplace/avatars/" + img4 + ".png";
|
||||
var pythonFilePath = "rendering/marketplacecontent/"+req.userData.UserId+".py";
|
||||
if(type === "face") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile + ".png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hat = ''
|
||||
}
|
||||
if(type === "shirt") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile + ".png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hat = ''
|
||||
}
|
||||
if(type === "pants") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hat = ''
|
||||
var pantsFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile + ".png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(type === "hat") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hatFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile + ".obj"
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+marketplace.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
}
|
||||
|
||||
var imports = "import bpy";
|
||||
var functions = "def hex_to_rgb(value):\n gamma = 2.05\n value = value.lstrip('#')\n lv = len(value)\n fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))\n r = pow(fin[0] / 255, gamma)\n g = pow(fin[1] / 255, gamma)\n b = pow(fin[2] / 255, gamma)\n fin.clear()\n fin.append(r)\n fin.append(g)\n fin.append(b)\n return tuple(fin)";
|
||||
var blenderImport = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePath+"')";
|
||||
var headColor = "bpy.data.objects['Head'].select = True\nbpy.data.materials['Head'].diffuse_color = hex_to_rgb('#ffffff')\nbpy.data.materials['Face'].diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var leftArmColor = "bpy.data.objects['Left Arm'].select = True\nbpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var rightArmColor = "bpy.data.objects['Right Arm'].select = True\nbpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var bodyColor = "bpy.data.objects['Torso'].select = True\nbpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var leftLegColor = "bpy.data.objects['Left Leg'].select = True\nbpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var rightLegColor = "bpy.data.objects['Right Leg'].select = True\nbpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var colors = headColor+"\n"+leftArmColor+"\n"+bodyColor+"\n"+rightArmColor+"\n"+leftLegColor+"\n"+rightLegColor;
|
||||
|
||||
var face = "face_Image = bpy.data.images.load(filepath = '"+faceFilePath+"')\nbpy.data.textures['Face'].image = face_Image";
|
||||
var shirt = "shirt_Image = bpy.data.images.load(filepath = '"+shirtFilePath+"')\nbpy.data.textures['Shirt'].image = shirt_Image\nbpy.data.textures['ShirtR'].image = shirt_Image\nbpy.data.textures['ShirtL'].image = shirt_Image";
|
||||
var pants = "pants_Image = bpy.data.images.load(filepath = '"+pantsFilePath+"')\nbpy.data.textures['PantsR'].image = pants_Image\nbpy.data.textures['PantsL'].image = pants_Image";
|
||||
var render = "for obj in bpy.data.objects:\n obj.select = False\n bpy.ops.object.select_all(action='SELECT')\nbpy.ops.view3d.camera_to_view_selected()\nscene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePath+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var python = imports+"\n"+functions+"\n"+blenderImport+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+render;
|
||||
fs.writeFile("rendering/marketplacecontent/"+marketplace.id+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
await Inventory.create({UserId: req.userData.UserId, purchasePrice: 0, isReselling: false, isResellingPrice: 0, ItemId: marketplace.id, boughtFrom: marketplace.UserId, resellType: 0, auctionId: 0})
|
||||
exec("blender -b -P rendering/marketplacecontent/"+marketplace.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
res.status(200)
|
||||
res.json(marketplace.toJSON())
|
||||
});
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.post('/upload/:id', auth, upload.single('image'), limiter, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
let findCategory = await ItemCategory.findOne({
|
||||
where: {id: req.params.id}
|
||||
});
|
||||
console.log(findCategory.id)
|
||||
if(!findCategory) {
|
||||
throw Errors.marketplaceNotFound
|
||||
}
|
||||
let user = await User.findOne({
|
||||
where: {id: req.userData.UserId}
|
||||
});
|
||||
|
||||
if(findCategory.locked && !user.modeler) {
|
||||
throw Errors.marketplaceAdminOnly
|
||||
}
|
||||
if(req.body.limited === true && !user.modeler) {
|
||||
throw Errors.marketplaceAdminOnly
|
||||
}
|
||||
if(req.body.price < 0) {
|
||||
throw Errors.underPriced
|
||||
}
|
||||
if(req.body.onSalePrice < 0) {
|
||||
throw Errors.underPriced
|
||||
}
|
||||
if(req.file.filename === undefined) {
|
||||
throw Errors.uploadFile
|
||||
}
|
||||
if(findCategory) {
|
||||
let img3 = cryptoRandomString({length: 32})
|
||||
let img4 = img3
|
||||
console.log(storage)
|
||||
if(req.userData.admin) {
|
||||
var marketplace = await Item.create({
|
||||
name: req.body.name,
|
||||
UserId: user.id,
|
||||
sourceFile: req.file.filename,
|
||||
previewFile: img4,
|
||||
limited: req.body.limited,
|
||||
salePrice: req.body.onSalePrice,
|
||||
saleEnabled: req.body.onSale,
|
||||
price: req.body.price,
|
||||
quantityAllowed: req.body.quantityAllowed,
|
||||
approved: false,
|
||||
ItemCategoryId: findCategory.id,
|
||||
description: req.body.description
|
||||
})
|
||||
} else {
|
||||
var marketplace = await Item.create({
|
||||
name: req.body.name,
|
||||
UserId: user.id,
|
||||
sourceFile: req.file.filename,
|
||||
previewFile: img4,
|
||||
limited: false,
|
||||
salePrice: req.body.onSalePrice,
|
||||
saleEnabled: req.body.onSale,
|
||||
price: req.body.price,
|
||||
quantityAllowed: 0,
|
||||
approved: false,
|
||||
ItemCategoryId: findCategory.id,
|
||||
description: req.body.description
|
||||
})
|
||||
}
|
||||
if(marketplace.ItemCategoryId === 0) {
|
||||
var type = "hat"
|
||||
} else if(marketplace.ItemCategoryId === 1) {
|
||||
var type = "face"
|
||||
} else if(marketplace.ItemCategoryId === 2) {
|
||||
var type = "shirt"
|
||||
} else if(marketplace.ItemCategoryId === 3) {
|
||||
var type = "pants"
|
||||
}
|
||||
const { exec } = require('child_process');
|
||||
var fs = require("fs");
|
||||
let rootPathRender = config.rootFolder + "/";
|
||||
var blendFilePath = rootPathRender + "rendering/avatar.blend";
|
||||
var imageSavePath = config.cdnFolder + "/marketplace/avatars/" + img4 + ".png";
|
||||
var pythonFilePath = "rendering/marketplacecontent/"+req.userData.UserId+".py";
|
||||
if(type === "face") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile;
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hat = ''
|
||||
}
|
||||
if(type === "shirt") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hat = ''
|
||||
}
|
||||
if(type === "pants") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hat = ''
|
||||
var pantsFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(type === "hat") {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png";
|
||||
var hatFilePath = rootPathRender + "rendering/global/" + marketplace.sourceFile + ".obj"
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+marketplace.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
}
|
||||
|
||||
var imports = "import bpy";
|
||||
var functions = "def hex_to_rgb(value):\n gamma = 2.05\n value = value.lstrip('#')\n lv = len(value)\n fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))\n r = pow(fin[0] / 255, gamma)\n g = pow(fin[1] / 255, gamma)\n b = pow(fin[2] / 255, gamma)\n fin.clear()\n fin.append(r)\n fin.append(g)\n fin.append(b)\n return tuple(fin)";
|
||||
var blenderImport = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePath+"')";
|
||||
var headColor = "bpy.data.objects['Head'].select = True\nbpy.data.materials['Head'].diffuse_color = hex_to_rgb('#ffffff')\nbpy.data.materials['Face'].diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var leftArmColor = "bpy.data.objects['Left Arm'].select = True\nbpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var rightArmColor = "bpy.data.objects['Right Arm'].select = True\nbpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var bodyColor = "bpy.data.objects['Torso'].select = True\nbpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var leftLegColor = "bpy.data.objects['Left Leg'].select = True\nbpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var rightLegColor = "bpy.data.objects['Right Leg'].select = True\nbpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('#ffffff')";
|
||||
var colors = headColor+"\n"+leftArmColor+"\n"+bodyColor+"\n"+rightArmColor+"\n"+leftLegColor+"\n"+rightLegColor;
|
||||
|
||||
var face = "face_Image = bpy.data.images.load(filepath = '"+faceFilePath+"')\nbpy.data.textures['Face'].image = face_Image";
|
||||
var shirt = "shirt_Image = bpy.data.images.load(filepath = '"+shirtFilePath+"')\nbpy.data.textures['Shirt'].image = shirt_Image\nbpy.data.textures['ShirtR'].image = shirt_Image\nbpy.data.textures['ShirtL'].image = shirt_Image";
|
||||
var pants = "pants_Image = bpy.data.images.load(filepath = '"+pantsFilePath+"')\nbpy.data.textures['PantsR'].image = pants_Image\nbpy.data.textures['PantsL'].image = pants_Image";
|
||||
var render = "for obj in bpy.data.objects:\n obj.select = False\n bpy.ops.object.select_all(action='SELECT')\nbpy.ops.view3d.camera_to_view_selected()\nscene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePath+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var python = imports+"\n"+functions+"\n"+blenderImport+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+render;
|
||||
fs.writeFile("rendering/marketplacecontent/"+marketplace.id+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
await Inventory.create({UserId: req.userData.UserId, purchasePrice: 0, isReselling: false, isResellingPrice: 0, ItemId: marketplace.id, boughtFrom: marketplace.UserId, resellType: 0, auctionId: 0})
|
||||
exec("blender -b -P rendering/marketplacecontent/"+marketplace.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
res.status(200)
|
||||
res.json(marketplace.toJSON())
|
||||
});
|
||||
} else {
|
||||
throw Errors.marketplaceNotFound
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/apply/:id', auth, async (req, res, next) => {
|
||||
try {
|
||||
let item = await Inventory.findOne({
|
||||
where: {ItemId: req.params.id, UserId: req.userData.UserId}
|
||||
});
|
||||
if (item) {
|
||||
let marketplaceItem = await Item.findOne({
|
||||
where: {id: item.ItemId}
|
||||
})
|
||||
if (marketplaceItem) {
|
||||
if(!marketplaceItem.approved) {
|
||||
throw Errors.itemUnavailable
|
||||
}
|
||||
if (marketplaceItem.ItemCategoryId === 0) {
|
||||
await User.update({ hatId: marketplaceItem.id }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else if (marketplaceItem.ItemCategoryId === 1) {
|
||||
await User.update({ faceId: marketplaceItem.id }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else if (marketplaceItem.ItemCategoryId === 2) {
|
||||
await User.update({ shirtId: marketplaceItem.id }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else if (marketplaceItem.ItemCategoryId === 3) {
|
||||
await User.update({ pantsId: marketplaceItem.id }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.unknown
|
||||
}
|
||||
} else {
|
||||
console.log('second to last')
|
||||
throw Errors.unowned
|
||||
}
|
||||
} else {
|
||||
console.log('last')
|
||||
throw Errors.unowned
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/remove/:id', auth, async (req, res, next) => {
|
||||
try {
|
||||
let item = await Inventory.findOne({
|
||||
where: {ItemId: req.params.id, UserId: req.userData.UserId}
|
||||
});
|
||||
if (item) {
|
||||
let marketplaceItem = await Item.findOne({
|
||||
where: {id: item.ItemId}
|
||||
})
|
||||
if (marketplaceItem) {
|
||||
console.log(marketplaceItem)
|
||||
if (marketplaceItem.ItemCategoryId === 0) {
|
||||
await User.update({ hatId: null }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else if (marketplaceItem.ItemCategoryId === 1) {
|
||||
await User.update({ faceId: null }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else if (marketplaceItem.ItemCategoryId === 2) {
|
||||
await User.update({ shirtId: null }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else if (marketplaceItem.ItemCategoryId === 3) {
|
||||
await User.update({ pantsId: null }, {where: {id: req.userData.UserId}})
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.unknown
|
||||
}
|
||||
} else {
|
||||
console.log('second to last')
|
||||
throw Errors.unowned
|
||||
}
|
||||
} else {
|
||||
console.log('last')
|
||||
throw Errors.unowned
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -33,15 +33,15 @@ router.post('/', validation(messageValidationSchema), auth, async(req, res, next
|
|||
try {
|
||||
let message = await messageController.create({
|
||||
content: req.body.content,
|
||||
userId: req.userData.id,
|
||||
userId: req.userData.UserId,
|
||||
conversationId: req.body.conversationId
|
||||
});
|
||||
let user = await userController.get(req.userData.id);
|
||||
let user = await userController.get(req.userData.UserId);
|
||||
let retMessage = Object.assign(message.toJSON(), {
|
||||
User: user
|
||||
});
|
||||
|
||||
let conversations = await conversationController.getFromUser(req.userData.id);
|
||||
let conversations = await conversationController.getFromUser(req.userData.UserId);
|
||||
let retConversation = conversations.Conversations[0];
|
||||
|
||||
//Get the users in the conversation and the id for the socket
|
|
@ -0,0 +1,98 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { Notification, User, Post, PostNotification, MessageNotification } = require('../models')
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let Notifications = await Notification.findAll({
|
||||
where: {
|
||||
'UserId': req.userData.UserId
|
||||
},
|
||||
order: [['id', 'DESC']],
|
||||
include: [{
|
||||
model: PostNotification,
|
||||
include: [Post, { model: User, attributes: ['createdAt', 'username', 'color'] }]
|
||||
}, {
|
||||
model: MessageNotification,
|
||||
include: [{model: User, attributes: ['createdAt', 'username', 'color']}]
|
||||
}]
|
||||
})
|
||||
|
||||
let unreadCount = Notifications.reduce((acc, val) => {
|
||||
return val.read ? acc : acc+1
|
||||
}, 0)
|
||||
|
||||
res.json({ Notifications, unreadCount })
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Notification.update({ read: true }, {
|
||||
where: {
|
||||
'UserId': req.userData.UserId,
|
||||
'read': false
|
||||
}
|
||||
})
|
||||
|
||||
res.json({ success: true })
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let updatedRows = await Notification.update({ interacted: true, read: true }, {
|
||||
where: {
|
||||
'UserId': req.userData.UserId,
|
||||
id: req.params.id
|
||||
}
|
||||
})
|
||||
|
||||
if(updatedRows[0] === 0) {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.invalidParameter('id', 'invalid notification id')]
|
||||
})
|
||||
} else {
|
||||
res.json({ success: true })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.delete('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let deleted = await Notification.destroy({
|
||||
where: {
|
||||
'UserId': req.userData.UserId,
|
||||
id: req.params.id
|
||||
}
|
||||
})
|
||||
|
||||
if(deleted) {
|
||||
res.json({ success: true })
|
||||
} else {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.invalidParameter('id', 'Notification already dismissed, or doesn\'t exist, maybe try not to spam it next time :)')]
|
||||
})
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,154 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { PollAnswer, PollQuestion, PollVote, User, Sequelize, Thread } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
|
||||
router.get('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let id = req.params.id
|
||||
let pollQuestion = await PollQuestion.findByPk(id, {
|
||||
include: [
|
||||
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } },
|
||||
{ model: PollAnswer, include: [PollVote] }
|
||||
]
|
||||
})
|
||||
if(!pollQuestion) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'Poll ID is invalid!',
|
||||
value: id
|
||||
})
|
||||
|
||||
let totalVotes = pollQuestion.PollAnswers.reduce((sum, answer) => {
|
||||
return sum + answer.PollVotes.length
|
||||
}, 0)
|
||||
|
||||
let answersWithPercent = pollQuestion.PollAnswers.map(answer => {
|
||||
let jsonAnswer = answer.toJSON()
|
||||
let percent = answer.PollVotes.length / totalVotes
|
||||
jsonAnswer.percent = Math.round(percent*100 * 10) / 10
|
||||
|
||||
return jsonAnswer
|
||||
})
|
||||
|
||||
let hasVoted = await PollVote.findOne({
|
||||
where: {
|
||||
UserId: req.userData.UserId,
|
||||
PollQuestionId: id
|
||||
}
|
||||
})
|
||||
|
||||
let jsonPollQuestion = pollQuestion.toJSON()
|
||||
jsonPollQuestion.totalVotes = totalVotes
|
||||
jsonPollQuestion.PollAnswers = answersWithPercent
|
||||
jsonPollQuestion.hasVoted = !!hasVoted
|
||||
|
||||
res.json(jsonPollQuestion)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let threadId = req.body.threadId
|
||||
let thread = await Thread.findByPk(req.body.threadId)
|
||||
if(!thread) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'You tried voting on a poll on a post that didn\'t exist?',
|
||||
value: threadId
|
||||
})
|
||||
} else if(thread.UserId !== req.userData.UserId) {
|
||||
throw Errors.requestNotAuthorized
|
||||
} else if(thread.PollQuestionId) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'invalid thread id',
|
||||
value: threadId
|
||||
})
|
||||
}
|
||||
|
||||
let answers = req.body.answers
|
||||
|
||||
if(!answers || answers.length > 6) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'You can only make up to 6 answers per poll',
|
||||
value: answers
|
||||
})
|
||||
}
|
||||
if(!answers || answers.length < 2) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'You must provide at least 2 answers',
|
||||
value: answers
|
||||
})
|
||||
} else if(answers.length !== new Set(answers).size) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'Answers cannot contain any duplicates',
|
||||
value: answers
|
||||
})
|
||||
}
|
||||
|
||||
let pollQuestion = await PollQuestion.create({
|
||||
UserId: req.userData.UserId,
|
||||
question: req.body.question
|
||||
})
|
||||
let pollAnswers = await Promise.all(
|
||||
answers.map(answer => {
|
||||
return PollAnswer.create({ answer })
|
||||
})
|
||||
)
|
||||
|
||||
//Set associations
|
||||
await thread.setPollQuestion(pollQuestion)
|
||||
await Promise.all(
|
||||
pollAnswers.map(pollAnswer => {
|
||||
return pollQuestion.addPollAnswer(pollAnswer)
|
||||
})
|
||||
)
|
||||
|
||||
res.json(pollQuestion.toJSON())
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let previousVote = await PollVote.findOne({
|
||||
where: { PollQuestionId: req.params.id, UserId: req.userData.UserId }
|
||||
})
|
||||
if(previousVote) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'you cannot vote twice',
|
||||
value: req.params.id
|
||||
})
|
||||
|
||||
let poll = await PollQuestion.findByPk(req.params.id, {
|
||||
include: [PollAnswer]
|
||||
})
|
||||
if(!poll) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'invalid poll id',
|
||||
value: req.params.id
|
||||
})
|
||||
|
||||
let pollAnswer = poll.PollAnswers.find(a => a.answer === req.body.answer)
|
||||
if(!pollAnswer) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'invalid answer',
|
||||
value: req.body.answer
|
||||
})
|
||||
|
||||
let pollVote = await PollVote.create({ UserId: req.userData.UserId })
|
||||
await pollVote.setPollQuestion(poll)
|
||||
await pollVote.setPollAnswer(pollAnswer)
|
||||
|
||||
res.redirect('/api/v1/forums/poll/' + req.params.id)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,198 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, Thread, Post, Notification, Ban, Sequelize, sequelize } = require('../models')
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 10,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 10 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
|
||||
const likeLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 25,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 25 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
|
||||
router.get('/:post_id', async(req, res, next) => {
|
||||
try {
|
||||
let post = await Post.findByPk(req.params.post_id, { include: Post.includeOptions() })
|
||||
if(!post) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'post does not exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
res.json(post.toJSON())
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
router.put('/:post_id/like', likeLimiter, auth, async(req, res, next) => {
|
||||
try {
|
||||
let post = await Post.findByPk(req.params.post_id)
|
||||
let user = await User.findOne({ where: { username: req.userData.username }})
|
||||
|
||||
if(!post) throw Errors.invalidParameter('id', 'post does not exist')
|
||||
if(post.UserId === user.id) throw Errors.cannotLikeOwnPost
|
||||
|
||||
await post.addLikes(user)
|
||||
|
||||
res.json({ success: true })
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.delete('/:post_id/like', likeLimiter, auth, async(req, res, next) => {
|
||||
try {
|
||||
let post = await Post.findByPk(req.params.post_id)
|
||||
let user = await User.findOne({ where: { username: req.userData.username }})
|
||||
|
||||
if(!post) throw Errors.invalidParameter('id', 'post does not exist')
|
||||
|
||||
await post.removeLikes(user)
|
||||
|
||||
res.json({ success: true })
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/', postLimiter, auth, async(req, res, next) => {
|
||||
let thread, replyingToPost, post, uniqueMentions = []
|
||||
let queryObj = {
|
||||
attributes: {include: ['emailVerified']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
try {
|
||||
//Will throw an error if banned
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
if(req.body.mentions) {
|
||||
uniqueMentions = Notification.filterMentions(req.body.mentions)
|
||||
}
|
||||
|
||||
if(!user.emailVerified) {
|
||||
throw Errors.verifyEmail
|
||||
}
|
||||
|
||||
thread = await Thread.findOne({ where: {
|
||||
id: req.body.threadId
|
||||
}})
|
||||
user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
|
||||
if(thread.locked) throw Errors.threadLocked
|
||||
|
||||
if(req.body.replyingToId) {
|
||||
let replyingToPost1 = Post.findByPk(
|
||||
req.body.replyingToId, { include: [Thread, { model: User, attributes: ['username'] }] }
|
||||
)
|
||||
console.log(replyingToPost1)
|
||||
replyingToPost = await Post.getReplyingToPost(
|
||||
req.body.replyingToId, thread, replyingToPost1
|
||||
)
|
||||
|
||||
post = await Post.create({ content: req.body.content, postNumber: thread.postsCount })
|
||||
|
||||
await post.setReplyingTo(replyingToPost)
|
||||
await replyingToPost.addReplies(post)
|
||||
|
||||
let replyNotification = await Notification.createPostNotification({
|
||||
usernameTo: replyingToPost.User.username,
|
||||
userFrom: user,
|
||||
type: 'reply',
|
||||
post: post
|
||||
})
|
||||
await replyNotification.emitNotificationMessage(
|
||||
req.app.get('io-users'),
|
||||
req.app.get('io')
|
||||
)
|
||||
} else {
|
||||
post = await Post.create({ content: req.body.content, postNumber: thread.postsCount })
|
||||
}
|
||||
|
||||
await post.setUser(user)
|
||||
await post.setThread(thread)
|
||||
|
||||
await thread.increment('postsCount')
|
||||
|
||||
if(uniqueMentions.length) {
|
||||
let ioUsers = req.app.get('io-users')
|
||||
let io = req.app.get('io')
|
||||
|
||||
for(const mention of uniqueMentions) {
|
||||
let mentionNotification = await Notification.createPostNotification({
|
||||
usernameTo: mention,
|
||||
userFrom: user,
|
||||
type: 'mention',
|
||||
post
|
||||
})
|
||||
|
||||
if(mentionNotification) {
|
||||
await mentionNotification.emitNotificationMessage(ioUsers, io)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json(await post.reload({
|
||||
include: Post.includeOptions()
|
||||
}))
|
||||
|
||||
req.app.get('io').to('thread/' + thread.id).emit('new post', {
|
||||
postNumber: thread.postsCount,
|
||||
content: post.content,
|
||||
username: user.username
|
||||
})
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:post_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.admin){
|
||||
res.status(401)
|
||||
res.json({errors: [Errors.requestNotAuthorized]})
|
||||
}
|
||||
let post = await Post.findByPk(req.params.post_id)
|
||||
if(!post) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'post does not exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
await post.update({ content: '[This post has been removed by an administrator]', removed: true })
|
||||
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,203 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, Ban, Relationship, Notification } = require('../models')
|
||||
|
||||
router.post('/send', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(req.body.friend !== undefined) {
|
||||
let queryObj = {
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
let queryObj2 = {
|
||||
where: {username: req.body.friend}
|
||||
}
|
||||
let user2 = await User.findOne(queryObj2)
|
||||
if (!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
if(!user2) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
if(user.id === user2.id) {
|
||||
throw Errors.friendSelf
|
||||
}
|
||||
let checkIfSent = await Relationship.findOne({
|
||||
where: {friend1Id: user.id, friend2Id: user2.id}
|
||||
})
|
||||
if (checkIfSent) {
|
||||
throw Errors.friendRequestAlreadySent
|
||||
}
|
||||
Relationship.create({friend1Id: user.id, friend2Id: user2.id, type: 'pending'})
|
||||
Relationship.create({friend1Id: user2.id, friend2Id: user.id, type: 'pendingCanAccept'})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
res.status(400)
|
||||
res.json({success: false})
|
||||
}
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('/accept', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(req.body.friend !== undefined) {
|
||||
let queryObj = {
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
let queryObj2 = {
|
||||
where: {username: req.body.friend}
|
||||
}
|
||||
let user2 = await User.findOne(queryObj2)
|
||||
if (!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
if(!user2) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
let checkIfSent = await Relationship.findOne({
|
||||
where: {friend1Id: user.id, friend2Id: user2.id, type: 'pendingCanAccept'}
|
||||
})
|
||||
if (!checkIfSent) {
|
||||
throw Errors.needToBeFriend
|
||||
}
|
||||
let checkIfSent2 = await Relationship.findOne({
|
||||
where: {friend1Id: user2.id, friend2Id: user.id, type: 'pending'}
|
||||
})
|
||||
checkIfSent.update({type: 'accepted'})
|
||||
checkIfSent2.update({type: 'accepted'})
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
res.status(400)
|
||||
res.json({success: false})
|
||||
}
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('/remove', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(req.body.friend !== undefined) {
|
||||
let queryObj = {
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
let queryObj2 = {
|
||||
where: {username: req.body.friend}
|
||||
}
|
||||
let user2 = await User.findOne(queryObj2)
|
||||
if (!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
if(!user2) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
let checkIfSent = await Relationship.findOne({
|
||||
where: {friend1Id: user.id, friend2Id: user2.id}
|
||||
})
|
||||
if (!checkIfSent) {
|
||||
throw Errors.needToBeFriend
|
||||
}
|
||||
let checkIfSent2 = await Relationship.findOne({
|
||||
where: {friend1Id: user2.id, friend2Id: user.id}
|
||||
})
|
||||
checkIfSent.destroy()
|
||||
checkIfSent2.destroy()
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
res.status(400)
|
||||
res.json({success: false})
|
||||
}
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.get('/get/:username', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if (!user) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
let queryObj2 = {
|
||||
where: {username: req.params.username}
|
||||
}
|
||||
let user2 = await User.findOne(queryObj2)
|
||||
if(!user2) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
let checkIfSent = await Relationship.findOne({
|
||||
where: {friend1Id: user.id, friend2Id: user2.id},
|
||||
include: [{ model: User, as: 'friend1', attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }, { model: User, as: 'friend2', attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } ]
|
||||
})
|
||||
if (checkIfSent) {
|
||||
res.status(200)
|
||||
res.json(checkIfSent.toJSON())
|
||||
} else {
|
||||
res.status(200)
|
||||
res.json({type: 'notFriends'})
|
||||
}
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.get('/user/:username', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {username: req.params.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if (!user) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
if(!user) {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
let checkIfSent = await Relationship.findAll({
|
||||
where: {friend1Id: user.id, type: 'accepted'},
|
||||
include: [{ model: User, as: 'friend2', attributes: ['username', 'description', 'picture', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } ]
|
||||
})
|
||||
if (checkIfSent) {
|
||||
res.status(200)
|
||||
res.json(checkIfSent)
|
||||
} else {
|
||||
res.status(200)
|
||||
res.json({type: 'notFriends'})
|
||||
}
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.get('/getAll', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if (!user) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
let checkIfSent = await Relationship.findAll({
|
||||
where: {friend1Id: user.id},
|
||||
include: [{ model: User, as: 'friend1', attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }, { model: User, as: 'friend2', attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } ]
|
||||
})
|
||||
if (checkIfSent) {
|
||||
res.status(200)
|
||||
res.json(checkIfSent)
|
||||
} else {
|
||||
res.status(200)
|
||||
res.json({})
|
||||
}
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
module.exports = router
|
|
@ -0,0 +1,95 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { User, Post, Report, Sequelize } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
router.post('/post', auth, async(req, res, next) => {
|
||||
try {
|
||||
let post = await Post.findByPk(req.body.postId)
|
||||
|
||||
if(!post) throw Report.InvalidPostId(req.body.postId)
|
||||
|
||||
let user = await User.findOne({
|
||||
where: { username: req.userData.username }
|
||||
})
|
||||
|
||||
let report = await Report.create({ reason: req.body.reason })
|
||||
report.setFlaggedByUser(user)
|
||||
report.setPost(post)
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
})
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/user', auth, async(req, res, next) => {
|
||||
try {
|
||||
let reportedUser = await Post.findByPk(req.body.userId)
|
||||
|
||||
if(!reportedUser) throw Report.InvalidUserId(req.body.userId)
|
||||
|
||||
let user = await User.findOne({
|
||||
where: { username: req.userData.username }
|
||||
})
|
||||
|
||||
let report = await Report.create({ reason: req.body.reason })
|
||||
report.setFlaggedByUser(user)
|
||||
report.setReportedUser(reportedUser)
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
})
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let reports = await Report.findAll({
|
||||
include: [
|
||||
{ model: User, as: 'FlaggedByUser', attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } },
|
||||
{ model: Post, include: Post.includeOptions(), attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }
|
||||
]
|
||||
})
|
||||
|
||||
res.json(reports)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.delete('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let report = await Report.findByPk(req.params.id)
|
||||
if(!report) throw Report.InvalidPostId(req.params.id)
|
||||
|
||||
await report.destroy()
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,188 @@
|
|||
const { Op } = require("sequelize");
|
||||
|
||||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { Post, Ban, Team, Thread, User, Category, Sequelize } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
const { setRandomFallback } = require('bcryptjs')
|
||||
|
||||
router.get('/thread', async(req, res, next) => {
|
||||
try {
|
||||
let searchString = req.query.q.trim()
|
||||
|
||||
if(searchString.length < 2) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'search string must be at least 2 characters',
|
||||
value: searchString
|
||||
})
|
||||
}
|
||||
|
||||
//Offset is really the previously lowest id
|
||||
//(as a proxy for oldest thread of the previous search)
|
||||
//if there is no offset, just use a clause that will always be true
|
||||
//i.e. greater than 0 id
|
||||
let offset = +req.query.offset ? { $lt: +req.query.offset } : { $gt: 0 }
|
||||
let limit = 10
|
||||
|
||||
/*
|
||||
Task is to find threads that either have the
|
||||
string in the title or in the content of the first post
|
||||
|
||||
Method
|
||||
1) Select first n items from each group (posts and threads), where n is the LIMIT,
|
||||
greater than id x, where x is previous OFFSET
|
||||
2) Merge results from both, remove duplicates and sort
|
||||
3) Select first n items from merged group
|
||||
4) Set x as the last item from merged group
|
||||
*/
|
||||
|
||||
let threadTitles = await Thread.findAll({
|
||||
where: {
|
||||
name: { [Op.like]: '%' + searchString + '%' },
|
||||
id: offset
|
||||
},
|
||||
order: [ ['id', 'DESC'] ],
|
||||
include: [
|
||||
{
|
||||
model: Post,
|
||||
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset']} }],
|
||||
where: {
|
||||
postNumber: 0
|
||||
}
|
||||
},
|
||||
{ model: Category },
|
||||
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }
|
||||
],
|
||||
limit
|
||||
})
|
||||
|
||||
let threadPosts = await Thread.findAll({
|
||||
where: {
|
||||
id: offset
|
||||
},
|
||||
order: [ ['id', 'DESC'] ],
|
||||
include: [
|
||||
{
|
||||
model: Post,
|
||||
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }],
|
||||
where: {
|
||||
postNumber: 0,
|
||||
plainText: { [Op.like]: '%' + searchString + '%' }
|
||||
}
|
||||
},
|
||||
{ model: Category },
|
||||
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }
|
||||
],
|
||||
limit
|
||||
})
|
||||
|
||||
let merged = [...threadTitles, ...threadPosts];
|
||||
let unique = [];
|
||||
merged.forEach(thread => {
|
||||
let includes = unique.filter(u => thread.id === u.id);
|
||||
|
||||
if(!includes.length) unique.push(thread);
|
||||
});
|
||||
|
||||
let sorted = unique
|
||||
.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
})
|
||||
.slice(0, limit);
|
||||
|
||||
//To get latest post, find threads where
|
||||
//the post number is equal to the overal posts count
|
||||
//and the post number > 0 (i.e. there are replies)
|
||||
let whereClause = sorted.reduce((arr, thread) => {
|
||||
if(thread.postsCount > 1) {
|
||||
let clause = {
|
||||
$and: {
|
||||
ThreadId: thread.id,
|
||||
postNumber: thread.postsCount-1
|
||||
}
|
||||
}
|
||||
|
||||
return [...arr, clause];
|
||||
} else {
|
||||
return arr;
|
||||
}
|
||||
}, []);
|
||||
|
||||
let latestPosts = await Post.findAll({
|
||||
where: {
|
||||
[Op.or]: whereClause
|
||||
},
|
||||
order: [ ['ThreadId', 'DESC'] ],
|
||||
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] } }]
|
||||
})
|
||||
|
||||
//Merge latest posts with threads array
|
||||
let ret = sorted.map(thread => {
|
||||
if(thread.postsCount > 1) {
|
||||
let post = latestPosts.filter(p => p.ThreadId === thread.id)[0];
|
||||
thread.Posts.push(post);
|
||||
}
|
||||
|
||||
return thread;
|
||||
})
|
||||
|
||||
res.json({
|
||||
threads: ret,
|
||||
offset: ret.length ? ret.slice(-1)[0].id : null,
|
||||
next: ret.length < limit ? null : limit
|
||||
})
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/user', async(req, res, next) => {
|
||||
try {
|
||||
let searchString = req.query.q
|
||||
let offset = +req.query.offset || 0
|
||||
let limit = 10
|
||||
let users = await User.findAll({
|
||||
where: {
|
||||
username: { [Op.like]: '%' + searchString + '%' }
|
||||
},
|
||||
order: [ ['username', 'DESC'] ],
|
||||
attributes: {exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset']},
|
||||
limit,
|
||||
offset
|
||||
})
|
||||
|
||||
res.json({
|
||||
users,
|
||||
offset: users.length < limit ? null : offset + limit,
|
||||
next: users.length < limit ? null : limit
|
||||
})
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/team', async(req, res, next) => {
|
||||
try {
|
||||
let searchString = req.query.q
|
||||
let offset = +req.query.offset || 0
|
||||
let limit = 10
|
||||
let teams = await Team.findAll({
|
||||
where: {
|
||||
username: { [Op.like]: '%' + searchString + '%' }
|
||||
},
|
||||
order: [ ['username', 'DESC'] ],
|
||||
attributes: {exclude: ['banReason']},
|
||||
limit,
|
||||
offset
|
||||
})
|
||||
|
||||
res.json({
|
||||
teams,
|
||||
offset: teams.length < limit ? null : offset + limit,
|
||||
next: teams.length < limit ? null : limit
|
||||
})
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,21 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { User } = require('../models')
|
||||
|
||||
router.get('/users', auth, async(req, res, next) => {
|
||||
try {
|
||||
let users = await User.findAll({
|
||||
where: {
|
||||
username
|
||||
},
|
||||
order: [ ['username', 'DESC'] ],
|
||||
attributes: { exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset'] },
|
||||
offset
|
||||
})
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,866 @@
|
|||
/*
|
||||
@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, teamWall, TeamInvite, TeamMemberRole, Transaction, teamPicture, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize, Team, TeamMembers, TeamRoles
|
||||
} = 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('/create', emailLimiter, auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(user.koins >= 300) {
|
||||
let userParams = {
|
||||
username: req.body.username,
|
||||
name: req.body.name,
|
||||
description: "This is the " + req.body.username + " team!",
|
||||
banned: false,
|
||||
banReason: "No reason provided",
|
||||
itemsOptOut: false,
|
||||
forumEnabled: false,
|
||||
teamWallOptOut: false,
|
||||
approved: false,
|
||||
picture: "default",
|
||||
OwnerId: req.userData.UserId
|
||||
}
|
||||
let team = await Team.create(userParams)
|
||||
let teamInfo = team.toJSON()
|
||||
console.log(teamInfo)
|
||||
let teamRoleMembers = {
|
||||
name: "Members",
|
||||
teamId: teamInfo.id,
|
||||
priority: 2
|
||||
}
|
||||
let teamRoleAdmins = {
|
||||
name: "Administrators",
|
||||
teamId: teamInfo.id,
|
||||
administrator: true,
|
||||
priority: 1
|
||||
}
|
||||
let memberRole = await TeamRoles.create(teamRoleMembers)
|
||||
let memberRoleInfo = memberRole.toJSON()
|
||||
let adminRole = await TeamRoles.create(teamRoleAdmins)
|
||||
let adminRoleInfo = adminRole.toJSON()
|
||||
let teamAddOwner = {
|
||||
userId: req.userData.UserId,
|
||||
teamId: teamInfo.id,
|
||||
roles: {"deprecated": "deprecated"}
|
||||
}
|
||||
let teamRoleOwner = {
|
||||
UserId: req.userData.UserId,
|
||||
TeamId: teamInfo.id,
|
||||
RoleId: memberRoleInfo.id,
|
||||
Role2Id: adminRoleInfo.id
|
||||
}
|
||||
await TeamMembers.create(teamAddOwner)
|
||||
await TeamMemberRole.create(teamRoleOwner)
|
||||
await user.removeKoins(300)
|
||||
await Transaction.create({UserId: user.id, priceOfPurchase: 300, text: user.username + ' purchased a Kaverti Team for 300 koins', limited: false, ipId: Ip.createIfNotExists(req.ip, user)})
|
||||
res.json(team.toJSON())
|
||||
} else {
|
||||
throw Errors.insufficientKoins
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username', async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
attributes: {exclude: ['banReason', 'TeamRoleId']},
|
||||
where: {username: req.params.username}
|
||||
}
|
||||
let queryObjBanned = {
|
||||
attributes: {exclude: ['banReason', 'description', 'picture', 'forumEnabled', 'TeamRoleId', 'OwnerId']},
|
||||
where: {username: req.params.username}
|
||||
}
|
||||
if(req.query.wall) {
|
||||
let {from, limit} = pagination.getPaginationProps(req.query, true)
|
||||
let postInclude = {
|
||||
model: teamWall,
|
||||
include: teamWall.includeOptions(),
|
||||
limit,
|
||||
order: [['id', 'DESC']],
|
||||
}
|
||||
if (from !== null) {
|
||||
postInclude.where = {id: {$lte: from}}
|
||||
}
|
||||
queryObj.include = [postInclude]
|
||||
|
||||
let user = await Team.findOne(queryObj)
|
||||
if (!user) throw Errors.accountDoesNotExist
|
||||
if(user.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
if (user.teamWallOptOut) {
|
||||
res.json({teamWalls: []})
|
||||
}
|
||||
res.json(Object.assign(user.toJSON(limit)))
|
||||
} else {
|
||||
let team = await Team.findOne(queryObj)
|
||||
if (!team) throw Errors.accountDoesNotExist
|
||||
if(!team.banned) {
|
||||
res.json(team.toJSON())
|
||||
} else {
|
||||
let team = await Team.findOne(queryObjBanned)
|
||||
res.json(team.toJSON())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/picture', async (req, res, next) => {
|
||||
try {
|
||||
let user = await Team.findOne({
|
||||
where: {
|
||||
username: req.params.username
|
||||
}
|
||||
})
|
||||
if(!user) throw Errors.accountDoesNotExist
|
||||
|
||||
let picture = await teamPicture.findOne({
|
||||
where: {
|
||||
TeamId: user.id
|
||||
}
|
||||
})
|
||||
|
||||
if(!picture) {
|
||||
res.status(404)
|
||||
res.json({picture: "https://cdn.kaverti.com/teams/unknown-light.png"})
|
||||
} else if(!user.banned) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': picture.mimetype,
|
||||
'Content-disposition': 'attachment;filename=kaverti-team-profile-picture',
|
||||
'Content-Length': picture.file.length
|
||||
})
|
||||
res.end(new Buffer.from(picture.file, 'binary'))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({picture: "https://cdn.kaverti.com/teams/unknown-light.png"})
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/members', async(req, res, next) => {
|
||||
try {
|
||||
let user = await Team.findOne({
|
||||
where: {
|
||||
username: req.params.username
|
||||
}
|
||||
})
|
||||
if(user) {
|
||||
if(user.banned) {
|
||||
res.status(200)
|
||||
res.json([])
|
||||
}
|
||||
let team = await TeamMembers.findAll({
|
||||
order: [['id', 'DESC']],
|
||||
where: {
|
||||
TeamId: user.id
|
||||
},
|
||||
include: [
|
||||
{ model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'description'] }
|
||||
]
|
||||
})
|
||||
res.json(team)
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/userRoles', async(req, res, next) => {
|
||||
try {
|
||||
let user = await Team.findOne({
|
||||
where: {
|
||||
username: req.params.username
|
||||
}
|
||||
})
|
||||
if(user) {
|
||||
if(user.banned) {
|
||||
res.status(200)
|
||||
res.json([])
|
||||
}
|
||||
let team = await TeamMemberRole.findAll({
|
||||
order: [['id', 'DESC']],
|
||||
where: {
|
||||
TeamId: user.id
|
||||
},
|
||||
include: [
|
||||
{ model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
|
||||
{ model: TeamRoles, as: 'Role' },
|
||||
{ model: TeamRoles, as: 'Role2' },
|
||||
{ model: TeamRoles, as: 'Role3' },
|
||||
{ model: TeamRoles, as: 'Role4' },
|
||||
{ model: TeamRoles, as: 'Role5' },
|
||||
{ model: TeamRoles, as: 'Role6' },
|
||||
{ model: TeamRoles, as: 'Role7' },
|
||||
{ model: TeamRoles, as: 'Role8' },
|
||||
{ model: TeamRoles, as: 'Role9' },
|
||||
{ model: TeamRoles, as: 'Role10' },
|
||||
|
||||
|
||||
]
|
||||
})
|
||||
res.json(team)
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/roles', async(req, res, next) => {
|
||||
try {
|
||||
let user = await Team.findOne({
|
||||
where: {
|
||||
username: req.params.username
|
||||
}
|
||||
})
|
||||
if(user) {
|
||||
if(user.banned) {
|
||||
res.status(200)
|
||||
res.json([])
|
||||
}
|
||||
let team = await TeamRoles.findAll({
|
||||
order: [['priority', 'ASC']],
|
||||
where: {
|
||||
TeamId: user.id
|
||||
},
|
||||
})
|
||||
res.json(team)
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/roles/id/:id', async(req, res, next) => {
|
||||
try {
|
||||
let user = await Team.findOne({
|
||||
where: {
|
||||
username: req.params.username
|
||||
}
|
||||
})
|
||||
if(user) {
|
||||
if(user.banned) {
|
||||
res.status(200)
|
||||
res.json([])
|
||||
}
|
||||
let team = await TeamMemberRole.findAll({
|
||||
order: [['id', 'DESC']],
|
||||
where: {
|
||||
TeamId: user.id,
|
||||
RoleId: req.params.id
|
||||
},
|
||||
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
|
||||
})
|
||||
res.json(team)
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/roles/name/:id', async(req, res, next) => {
|
||||
try {
|
||||
let user = await Team.findOne({
|
||||
where: {
|
||||
username: req.params.username
|
||||
}
|
||||
})
|
||||
let userParam = await User.findOne({
|
||||
where: {
|
||||
username: req.params.id
|
||||
}
|
||||
})
|
||||
if(user && userParam) {
|
||||
if(user.banned) {
|
||||
res.status(200)
|
||||
res.json([])
|
||||
}
|
||||
let team = await TeamMemberRole.findAll({
|
||||
order: [['id', 'DESC']],
|
||||
where: {
|
||||
TeamId: user.id,
|
||||
UserID: userParam.id
|
||||
},
|
||||
include: [ { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] }, { model: TeamRoles, as: 'Role' } ]
|
||||
})
|
||||
res.json(team)
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username/roles/permissions/:id', async(req, res, next) => {
|
||||
try {
|
||||
let team = await Team.findOne({
|
||||
where: {username: req.params.username}
|
||||
});
|
||||
let user = await User.findOne({
|
||||
where: {username: req.params.id}
|
||||
});
|
||||
if(user && team) {
|
||||
let isAuthMem = await TeamMembers.findOne({
|
||||
where: {UserId: user.id, TeamId: team.id}
|
||||
});
|
||||
if (!isAuthMem) {
|
||||
throw Errors.notInTeam
|
||||
}
|
||||
let isAuthRole = await TeamMemberRole.findOne({
|
||||
where: {UserId: user.id, TeamId: team.id}
|
||||
})
|
||||
let isAuthOwner = await Team.findOne({
|
||||
where: {OwnerId: user.id, id: team.id}
|
||||
})
|
||||
let isAuth1 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.RoleId, inviteUsers: true}
|
||||
})
|
||||
let isAuth2 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role2Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth3 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role3Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth4 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role4Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth5 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role5Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth6 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role6Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth7 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role7Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth8 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role8Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth9 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role9Id, inviteUsers: true}
|
||||
})
|
||||
let isAuth10 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role10Id, inviteUsers: true}
|
||||
})
|
||||
const allowArray = [
|
||||
isAuth1,
|
||||
isAuth2,
|
||||
isAuth3,
|
||||
isAuth4,
|
||||
isAuth5,
|
||||
isAuth6,
|
||||
isAuth7,
|
||||
isAuth8,
|
||||
isAuth9,
|
||||
isAuth10,
|
||||
isAuthOwner
|
||||
]
|
||||
let invite = allowArray.some(function (el) {
|
||||
return el !== null;
|
||||
});
|
||||
let isAuth11 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.RoleId, administrator: true}
|
||||
})
|
||||
let isAuth12 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role2Id, administrator: true}
|
||||
})
|
||||
let isAuth13 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role3Id, administrator: true}
|
||||
})
|
||||
let isAuth14 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role4Id, administrator: true}
|
||||
})
|
||||
let isAuth15 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role5Id, administrator: true}
|
||||
})
|
||||
let isAuth16 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role6Id, administrator: true}
|
||||
})
|
||||
let isAuth17 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role7Id, administrator: true}
|
||||
})
|
||||
let isAuth18 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role8Id, administrator: true}
|
||||
})
|
||||
let isAuth19 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role9Id, administrator: true}
|
||||
})
|
||||
let isAuth20 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role10Id, administrator: true}
|
||||
})
|
||||
const adminArray = [
|
||||
isAuth10,
|
||||
isAuth12,
|
||||
isAuth13,
|
||||
isAuth14,
|
||||
isAuth15,
|
||||
isAuth16,
|
||||
isAuth17,
|
||||
isAuth18,
|
||||
isAuth19,
|
||||
isAuth20,
|
||||
isAuthOwner
|
||||
]
|
||||
let administrator = adminArray.some(function (el) {
|
||||
return el !== null;
|
||||
});
|
||||
let isAuth21 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.RoleId, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth22 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role2Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth23 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role3Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth24 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role4Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth25 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role5Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth26 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role6Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth27 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role7Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth28 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role8Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth29 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role9Id, changeTeamMeta: true}
|
||||
})
|
||||
let isAuth30 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role10Id, changeTeamMeta: true}
|
||||
})
|
||||
const metaArray = [
|
||||
isAuth21,
|
||||
isAuth22,
|
||||
isAuth23,
|
||||
isAuth24,
|
||||
isAuth25,
|
||||
isAuth26,
|
||||
isAuth27,
|
||||
isAuth28,
|
||||
isAuth29,
|
||||
isAuth30,
|
||||
isAuthOwner
|
||||
]
|
||||
let meta = metaArray.some(function (el) {
|
||||
return el !== null;
|
||||
});
|
||||
let isAuth31 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.RoleId, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth32 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role2Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth33 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role3Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth34 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role4Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth35 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role5Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth36 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role6Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth37 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role7Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth38 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role8Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth39 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role9Id, changeTeamRoles: true}
|
||||
})
|
||||
let isAuth40 = await TeamRoles.findOne({
|
||||
where: {id: isAuthRole.Role10Id, changeTeamRoles: true}
|
||||
})
|
||||
const roleArray = [
|
||||
isAuth31,
|
||||
isAuth32,
|
||||
isAuth33,
|
||||
isAuth34,
|
||||
isAuth35,
|
||||
isAuth36,
|
||||
isAuth37,
|
||||
isAuth38,
|
||||
isAuth39,
|
||||
isAuth40,
|
||||
isAuthOwner
|
||||
]
|
||||
let role = roleArray.some(function (el) {
|
||||
return el !== null;
|
||||
});
|
||||
if (team) {
|
||||
if (team.banned) {
|
||||
res.status(200)
|
||||
res.json({administrator: false, inviteUsers: false})
|
||||
}
|
||||
res.json({inviteUsers: invite, administrator: administrator, changeTeamMeta: meta, changeTeamRoles: role})
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
} else {
|
||||
throw Errors.accountDoesNotExist
|
||||
}
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/', async(req, res, next) => {
|
||||
try {
|
||||
let sortFields = {
|
||||
createdAt: 'X.id',
|
||||
username: 'X.username',
|
||||
};
|
||||
let offset = Number.isInteger(+req.query.offset) ? +req.query.offset : 0;
|
||||
let havingClause = 'Having Teams.banned = false';
|
||||
|
||||
if(req.query.search) {
|
||||
//I.e. if there is not already a HAVING clause
|
||||
if(!havingClause.length) {
|
||||
havingClause = 'HAVING ';
|
||||
} else {
|
||||
havingClause += ' AND ';
|
||||
}
|
||||
havingClause += 'Team.username LIKE $search';
|
||||
}
|
||||
let sql = `
|
||||
SELECT X.username, X.approved, X.name, X.verified, X.picture, X.id, X.forumEnabled, X.description, X.banned, X.createdAt, X.updatedAt
|
||||
FROM (
|
||||
SELECT Teams.*
|
||||
FROM Teams
|
||||
GROUP BY Teams.id
|
||||
${havingClause}
|
||||
) as X
|
||||
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: Team,
|
||||
bind: { search: req.query.search + '%' }
|
||||
});
|
||||
res.json(users)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/join/:username', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
let team = await Team.findOne({
|
||||
where: { username: req.params.username }
|
||||
})
|
||||
if(team) {
|
||||
if(team.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
if(team.inviteOnly) {
|
||||
throw Errors.inviteOnly
|
||||
}
|
||||
let queryObj3 = {
|
||||
where: {userId: req.userData.UserId, teamId: team.id},
|
||||
}
|
||||
let teamJoinTest = await TeamMembers.findOne(queryObj3)
|
||||
if(teamJoinTest) {
|
||||
throw Errors.joinedTeam
|
||||
}
|
||||
let role = await TeamRoles.findOne({
|
||||
where: {teamId: team.id, name: "Members"}
|
||||
})
|
||||
let join = {
|
||||
userId: req.userData.UserId,
|
||||
teamId: team.id,
|
||||
roles: {"deprecated": "deprecated"}
|
||||
}
|
||||
console.log(role)
|
||||
let roleUser = {
|
||||
UserId: req.userData.UserId,
|
||||
TeamId: team.id,
|
||||
RoleId: role.id
|
||||
}
|
||||
await TeamMembers.create(join)
|
||||
await TeamMemberRole.create(roleUser)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.teamDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/check/:username', auth, async(req, res, next) => {
|
||||
try {
|
||||
let team = await Team.findOne({
|
||||
where: {username: req.params.username}
|
||||
});
|
||||
if(team) {
|
||||
let queryObj3 = {
|
||||
where: {userId: req.userData.UserId, teamId: team.id},
|
||||
}
|
||||
if(team.banned) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
let teamJoinTest = await TeamMembers.findOne(queryObj3)
|
||||
if (teamJoinTest) {
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else if (!teamJoinTest) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
} else {
|
||||
throw Errors.teamDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/leave/:username', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
let team = await Team.findOne({
|
||||
where: { username: req.params.username }
|
||||
})
|
||||
if(team) {
|
||||
if(team.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
let queryObj3 = {
|
||||
where: {userId: req.userData.UserId, teamId: team.id},
|
||||
}
|
||||
let teamLeaveTeam = await TeamMembers.findOne(queryObj3)
|
||||
if(teamLeaveTeam) {
|
||||
let queryObj4 = {
|
||||
where: {UserId: req.userData.UserId, TeamId: team.id},
|
||||
}
|
||||
let teamLeaveRoles = await TeamMemberRole.findAll(queryObj4)
|
||||
await teamLeaveTeam.leaveTeam()
|
||||
teamLeaveRoles.forEach((TeamMemberRole) => TeamMemberRole.leaveTeam());
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.notInTeam
|
||||
}
|
||||
} else {
|
||||
throw Errors.teamDoesNotExist
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.get('/invite/:username', async(req, res, next) => {
|
||||
try {
|
||||
let code = await TeamInvite.findOne({
|
||||
where: {code: req.params.username},
|
||||
include: [{model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture']}, {model: Team, attributes: { exclude: [ 'banReason' ]}}, {model: TeamRoles, as: 'Role'}]
|
||||
})
|
||||
if (code) {
|
||||
if(code.maxUses === 0) {
|
||||
res.status(200)
|
||||
res.json(code.toJSON())
|
||||
} else if(code.uses >= code.maxUses) {
|
||||
throw Errors.invalidInvite
|
||||
} else if(code.uses < code.maxUses) {
|
||||
res.status(200)
|
||||
res.json(code.toJSON())
|
||||
} else {
|
||||
throw Errors.invalidInvite
|
||||
}
|
||||
} else {
|
||||
throw Errors.invalidInvite
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/invite/:code', auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
let code = await TeamInvite.findOne({
|
||||
where: {code: req.params.code}
|
||||
})
|
||||
if (code) {
|
||||
let team = await Team.findOne({
|
||||
where: {id: code.TeamId}
|
||||
})
|
||||
if (team.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
let queryObj3 = {
|
||||
where: {userId: req.userData.UserId, teamId: team.id},
|
||||
}
|
||||
let teamJoinTest = await TeamMembers.findOne(queryObj3)
|
||||
if (teamJoinTest) {
|
||||
throw Errors.joinedTeam
|
||||
}
|
||||
if(code.maxUses > 0 && code.maxUses === code.uses) {
|
||||
console.log('failed at maxUses over code.uses')
|
||||
throw Errors.invalidInvite
|
||||
} else if(code.maxUses === 0) {
|
||||
let role = await TeamRoles.findOne({
|
||||
where: {teamId: team.id, name: "Members"}
|
||||
})
|
||||
if(code.RoleId > 0) {
|
||||
let roleLookup = await TeamRoles.findOne({
|
||||
where: {id: code.RoleId, TeamId: team.id}
|
||||
})
|
||||
if(roleLookup) {
|
||||
let join = {
|
||||
userId: req.userData.UserId,
|
||||
teamId: team.id,
|
||||
roles: {"deprecated": "deprecated"}
|
||||
}
|
||||
let roleUser = {
|
||||
UserId: req.userData.UserId,
|
||||
TeamId: team.id,
|
||||
RoleId: role.id,
|
||||
Role2Id: roleLookup.id
|
||||
}
|
||||
await TeamInvite.update({ uses: + 1}, {
|
||||
where: {id: code.id, TeamId: team.id}
|
||||
})
|
||||
await TeamMembers.create(join)
|
||||
await TeamMemberRole.create(roleUser)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
let join = {
|
||||
userId: req.userData.UserId,
|
||||
teamId: team.id,
|
||||
roles: {"deprecated": "deprecated"}
|
||||
}
|
||||
console.log(role)
|
||||
let roleUser = {
|
||||
UserId: req.userData.UserId,
|
||||
TeamId: team.id,
|
||||
RoleId: role.id
|
||||
}
|
||||
await TeamInvite.update({ uses: + 1}, {
|
||||
where: {id: code.id, TeamId: team.id}
|
||||
})
|
||||
await TeamMembers.create(join)
|
||||
await TeamMemberRole.create(roleUser)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
}
|
||||
} else {
|
||||
let join = {
|
||||
userId: req.userData.UserId,
|
||||
teamId: team.id,
|
||||
roles: {"deprecated": "deprecated"}
|
||||
}
|
||||
let roleUser = {
|
||||
UserId: req.userData.UserId,
|
||||
TeamId: team.id,
|
||||
RoleId: role.id
|
||||
}
|
||||
await TeamInvite.update({ uses: + 1}, {
|
||||
where: {id: code.id, TeamId: team.id}
|
||||
})
|
||||
await TeamMembers.create(join)
|
||||
await TeamMemberRole.create(roleUser)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
}
|
||||
} else if(code.uses < code.maxUses) {
|
||||
let role = await TeamRoles.findOne({
|
||||
where: {teamId: team.id, name: "Members"}
|
||||
})
|
||||
let join = {
|
||||
userId: req.userData.UserId,
|
||||
teamId: team.id,
|
||||
roles: {"deprecated": "deprecated"}
|
||||
}
|
||||
let roleUser = {
|
||||
UserId: req.userData.UserId,
|
||||
TeamId: team.id,
|
||||
RoleId: role.id,
|
||||
}
|
||||
await TeamInvite.update({ uses: + 1}, {
|
||||
where: {id: code.id, TeamId: team.id}
|
||||
})
|
||||
await TeamMembers.create(join)
|
||||
await TeamMemberRole.create(roleUser)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
console.log('failed at second last else')
|
||||
throw Errors.invalidInvite
|
||||
}
|
||||
} else {
|
||||
console.log('failed at last else')
|
||||
throw Errors.invalidInvite
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,144 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, Team, teamWall, Notification, Ban, Sequelize, sequelize } = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 10,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 10 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
router.get('/show/:username', async(req, res, next) => {
|
||||
try {
|
||||
let { limit } = pagination.getPaginationProps(req.query, true)
|
||||
|
||||
let postInclude = {
|
||||
model: userWall,
|
||||
limit,
|
||||
order: [['id', 'DESC']]
|
||||
}
|
||||
|
||||
let user = await teamWall.findOne(postInclude)
|
||||
if (!user) throw Errors.accountDoesNotExist
|
||||
|
||||
let meta = await user.getMeta(limit)
|
||||
let Posts = await teamWall.find(postInclude)
|
||||
|
||||
res.json(Object.assign( user.toJSON(limit), { meta, Posts } )) } catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/post', postLimiter, auth, async(req, res, next) => {
|
||||
let queryObj = {
|
||||
attributes: {include: ['emailVerified']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let getSessionId = {
|
||||
attributes: {include: ['id']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let teamToId = {
|
||||
attributes: {include: ['id']},
|
||||
where: {username: req.body.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
let sessionId = await User.findOne(getSessionId)
|
||||
let getWallUser = await Team.findOne(teamToId)
|
||||
try {
|
||||
//Will throw an error if banned
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
if(getWallUser.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
|
||||
if (req.body.mentions) {
|
||||
uniqueMentions = Notification.filterMentions(req.body.mentions)
|
||||
}
|
||||
|
||||
if (!user.emailVerified) {
|
||||
throw Errors.verifyEmail
|
||||
}
|
||||
|
||||
if(getWallUser.teamWallOptOut) {
|
||||
throw Errors.userWallOptOut
|
||||
}
|
||||
|
||||
if(teamToId.id == "null") throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'User doesn\'t exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
user = await teamWall.findOne({ where: {
|
||||
fromUserId: sessionId.id
|
||||
}})
|
||||
|
||||
post = await teamWall.create({content: req.body.content, postNumber: "0", teamId: getWallUser.id, fromUserId: req.userData.UserId})
|
||||
|
||||
if (uniqueMentions.length) {
|
||||
let ioUsers = req.app.get('io-users')
|
||||
let io = req.app.get('io')
|
||||
|
||||
for (const mention of uniqueMentions) {
|
||||
let mentionNotification = await Notification.createPostNotification({
|
||||
usernameTo: mention,
|
||||
userFrom: user,
|
||||
type: 'mention',
|
||||
post
|
||||
})
|
||||
|
||||
if (mentionNotification) {
|
||||
await mentionNotification.emitNotificationMessage(ioUsers, io)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json({success: true})
|
||||
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(!req.userData.admin) {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:post_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.admin){
|
||||
res.status(401)
|
||||
res.json({errors: [Errors.requestNotAuthorized]})
|
||||
}
|
||||
let post = await userWall.findByPk(req.params.post_id)
|
||||
if(!post) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'post does not exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
await post.update({ content: '[This post has been removed by an administrator]', removed: true })
|
||||
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,193 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors.js')
|
||||
let { User, Thread, Notification, Category, Post, Ban, Report, Sequelize } = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 10,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 10 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
router.get('/:thread_id', async(req, res, next) => {
|
||||
try {
|
||||
let { from, limit } = pagination.getPaginationProps(req.query)
|
||||
let thread = await Thread.findByPk(req.params.thread_id, {
|
||||
include: Thread.includeOptions(from, limit)
|
||||
})
|
||||
if(!thread) throw Errors.invalidParameter('id', 'thread does not exist')
|
||||
|
||||
let meta = thread.getMeta(limit)
|
||||
|
||||
res.json(Object.assign( thread.toJSON(), { meta } ))
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
//Only logged in routes
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', postLimiter, auth, async(req, res, next) => {
|
||||
let validationErrors = []
|
||||
|
||||
try {
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
let category = await Category.findOne({ where: {
|
||||
value: req.body.category
|
||||
}})
|
||||
if (req.body.category == "ALL") throw Errors.selectCategory
|
||||
if (req.body.category == "") throw Errors.selectCategory
|
||||
if (!category) throw Errors.invalidCategory
|
||||
if (category.locked && !req.userData.admin) throw Errors.lockedCategory
|
||||
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
|
||||
if(!user.emailVerified) {
|
||||
throw Errors.verifyEmail
|
||||
}
|
||||
|
||||
|
||||
let thread = await Thread.create({
|
||||
name: req.body.name
|
||||
})
|
||||
await thread.increment('postsCount')
|
||||
|
||||
await thread.setCategory(category)
|
||||
await thread.setUser(user)
|
||||
user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
|
||||
|
||||
if(req.body.replyingToId) {
|
||||
let replyingToPost1 = Post.findByPk(
|
||||
req.body.replyingToId, { include: [Thread, { model: User, attributes: ['username'] }] }
|
||||
)
|
||||
console.log(replyingToPost1)
|
||||
replyingToPost = await Post.getReplyingToPost(
|
||||
req.body.replyingToId, thread, replyingToPost1
|
||||
)
|
||||
|
||||
var post = await Post.create({ content: req.body.content, postNumber: thread.postsCount })
|
||||
|
||||
await post.setReplyingTo(replyingToPost)
|
||||
await replyingToPost.addReplies(post)
|
||||
|
||||
let replyNotification = await Notification.createPostNotification({
|
||||
usernameTo: replyingToPost.User.username,
|
||||
userFrom: user,
|
||||
type: 'reply',
|
||||
post: post
|
||||
})
|
||||
await replyNotification.emitNotificationMessage(
|
||||
req.app.get('io-users'),
|
||||
req.app.get('io')
|
||||
)
|
||||
} else {
|
||||
var post = await Post.create({ content: req.body.content, postNumber: thread.postsCount })
|
||||
}
|
||||
|
||||
await post.setUser(user)
|
||||
await post.setThread(thread)
|
||||
|
||||
res.json(await thread.reload({
|
||||
include: [
|
||||
{ model: User, attributes: ['username', 'createdAt', 'updatedAt', 'id'] },
|
||||
Category
|
||||
]
|
||||
}))
|
||||
|
||||
req.app.get('io').to('index').emit('new thread', {
|
||||
name: category.name,
|
||||
value: category.value
|
||||
})
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
//Only admin routes
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:thread_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let thread = await Thread.findByPk(req.params.thread_id)
|
||||
|
||||
if(!thread) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'invalid thread id',
|
||||
value: req.params.thread_id
|
||||
})
|
||||
} else {
|
||||
//Find all posts with reports and get reports
|
||||
//Then delete those reports
|
||||
//Temporary fix because cascade is not working
|
||||
let posts = await Post.findAll({
|
||||
where: {
|
||||
ThreadId: thread.id
|
||||
},
|
||||
include: [Report]
|
||||
})
|
||||
let reports = posts
|
||||
.map(post => post.Reports)
|
||||
.reduce((a, b) => a.concat(b), [])
|
||||
|
||||
let destroyPromises = reports.map(report => report.destroy())
|
||||
|
||||
await Promise.all(destroyPromises)
|
||||
await Post.destroy({ where: { ThreadId: thread.id } })
|
||||
await thread.destroy()
|
||||
|
||||
res.json({ success: true })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/:thread_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let thread = await Thread.findByPk(req.params.thread_id)
|
||||
|
||||
if(!thread) {
|
||||
res.status(400)
|
||||
res.json({ errors:
|
||||
[Errors.invalidParameter('threadId', 'thread does not exist')]
|
||||
})
|
||||
} else {
|
||||
if(req.body.locked) {
|
||||
await thread.update({ locked: true })
|
||||
} else {
|
||||
await thread.update({ locked: false })
|
||||
}
|
||||
|
||||
res.json({ success: true })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,19 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/authUserInfo')
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, Transaction } = require('../models')
|
||||
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
let queryObj = {
|
||||
where: {UserId: req.userData.UserId},
|
||||
}
|
||||
let transaction = await Transaction.findAll(queryObj)
|
||||
if(!transaction) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(transaction)
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
@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) {
|
||||
res.status(200)
|
||||
res.json({userWalls: []})
|
||||
}
|
||||
|
||||
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;
|
|
@ -0,0 +1,123 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
const Errors = require('../lib/errors.js')
|
||||
let {
|
||||
User, Ip, Ban
|
||||
} = require('../models')
|
||||
const cryptoRandomString = require("crypto-random-string")
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
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}]}"
|
||||
});
|
||||
|
||||
const registerLimit = rateLimit({
|
||||
windowMs: 60000 * 5, // 5 minutes
|
||||
max: 1, // limit each IP to 100 requests per windowMs
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 1 request to this endpoint every 5 minutes.\",\"status\":429}]}"
|
||||
});
|
||||
router.post('/oidfhuisadhi8243', 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)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, email: user.email, UserId: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.post('/null', 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)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, email: user.email, UserId: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.post('/register', 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)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, email: user.email, UserId: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -59,7 +59,7 @@ router.post('/post', postLimiter, auth, async(req, res, next) => {
|
|||
let getWallUser = await User.findOne(usernameToUserId)
|
||||
try {
|
||||
//Will throw an error if banned
|
||||
await Ban.ReadOnlyMode(req.userData.id)
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
if (req.body.mentions) {
|
||||
uniqueMentions = Notification.filterMentions(req.body.mentions)
|
||||
|
@ -82,7 +82,7 @@ router.post('/post', postLimiter, auth, async(req, res, next) => {
|
|||
fromUserId: sessionId.id
|
||||
}})
|
||||
|
||||
post = await userWall.create({content: req.body.content, postNumber: "0", userId: getWallUser.id, fromUserId: req.userData.id})
|
||||
post = await userWall.create({content: req.body.content, postNumber: "0", userId: getWallUser.id, fromUserId: req.userData.UserId})
|
||||
|
||||
if (uniqueMentions.length) {
|
||||
let ioUsers = req.app.get('io-users')
|
|
@ -0,0 +1,26 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/authUserInfo')
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, Ban } = require('../models')
|
||||
|
||||
router.get('/', auth, auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
attributes: {exclude: ['hash', 'currency2', 'emailToken', 'passwordResetToken', 'deleteCode', 'deleteEnabled', 'jwtOffset']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if(!user) {
|
||||
res.status(200)
|
||||
res.json({
|
||||
errors: ["Please login to use this endpoint, idk what you expected"]
|
||||
})
|
||||
}
|
||||
res.status(200)
|
||||
res.json(user.toJSON())
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,750 @@
|
|||
require("dotenv").config();
|
||||
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');
|
||||
var speakeasy = require('speakeasy');
|
||||
var secret = speakeasy.generateSecret();
|
||||
let {
|
||||
User, Post, ProfilePicture, Transaction, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
} = require('../models')
|
||||
let pagination = require('../lib/pagination.js');
|
||||
const mailgun = require("mailgun-js");
|
||||
const DOMAIN = 'mail.kaverti.com';
|
||||
const mg = mailgun({apiKey: "key-d5c9840762ac5756d8f25b80b6a398e6", domain: DOMAIN});
|
||||
const MailGen = require('mailgen')
|
||||
const crypto = require("crypto")
|
||||
const cryptoRandomString = require("crypto-random-string")
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const moment = require("moment")
|
||||
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}]}"
|
||||
});
|
||||
let conversationController = require('../controllers/conversation');
|
||||
const jwt = require('jsonwebtoken');
|
||||
let config = require('../config/server.js')
|
||||
|
||||
router.post('/oidfhuisadhi8243', 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: '#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)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, email: user.email, UserId: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.post('/', 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: '#fffff',
|
||||
headColor: '#fffff',
|
||||
leftLegColor: '#fffff',
|
||||
rightLegColor: '#fffff',
|
||||
leftArmColor: '#fffff',
|
||||
rightArmColor: '#fffff',
|
||||
koins: '250',
|
||||
currency2: '0',
|
||||
picture: 'default',
|
||||
developerMode: false,
|
||||
booster: false,
|
||||
theme: 'light',
|
||||
emailToken: crypto(16)
|
||||
}
|
||||
throw Error.registrationsDisabled
|
||||
let user = await User.create(userParams)
|
||||
await Ip.createIfNotExists(req.ip, user)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, email: user.email, UserId: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/leaderboard', 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 = '';
|
||||
}
|
||||
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.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' ? 'ASC' : 'DESC'}
|
||||
LIMIT 5
|
||||
OFFSET ${offset}
|
||||
`;
|
||||
let users = await sequelize.query(sql, {
|
||||
model: User,
|
||||
bind: { search: req.query.search + '%' }
|
||||
});
|
||||
res.json(users)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/job-application', auth, 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('/reward', auth, async(req, res, next) => {
|
||||
try {
|
||||
if (!req.userData.username) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let queryObj = {
|
||||
attributes: {include: ['lastRewardDate', 'koins']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
|
||||
let ms = Date.now() - user.lastRewardDate
|
||||
let dayMs = 1000 * 60 * 60 * 24
|
||||
|
||||
let check = await ms / dayMs < 1
|
||||
if(user.koins === 9223372036854775808) {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.koinLimit], koins: user.koins
|
||||
})
|
||||
}
|
||||
if (check) {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.koinFail], koins: user.koins
|
||||
})
|
||||
} else {
|
||||
user.update({koins: user.koins + 25, lastRewardDate: Date.now()})
|
||||
res.status(200)
|
||||
res.json({success: true, koins: user.koins})
|
||||
}
|
||||
} 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
|
||||
}})
|
||||
let userEmail = await User.findOne({ where: {
|
||||
email: req.body.username
|
||||
}})
|
||||
if(user) {
|
||||
if(await user.comparePassword(req.body.password)) {
|
||||
await Ip.createIfNotExists(req.ip, user)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, email: user.email, UserId: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.invalidLoginCredentials]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (userEmail) {
|
||||
if (await userEmail.comparePassword(req.body.password)) {
|
||||
await Ip.createIfNotExists(req.ip, userEmail)
|
||||
|
||||
const accessToken = jwt.sign({ username: userEmail.username, admin: userEmail.admin, executive: userEmail.executive, email: userEmail.email, UserId: userEmail.id, loggedIn: true, bot: userEmail.bot, offset: userEmail.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
|
||||
res.json({
|
||||
accessToken
|
||||
});
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.invalidLoginCredentials]
|
||||
})
|
||||
}
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.invalidLoginCredentials]
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
router.post('/recovery/send', emailLimiter, async(req, res, next) => {
|
||||
const mailGenerator = new MailGen({
|
||||
theme: 'salted',
|
||||
product: {
|
||||
name: 'Kaverti',
|
||||
link: 'https://kaverti.com'
|
||||
},
|
||||
})
|
||||
let queryObj = {
|
||||
attributes: {include: ['email', 'username', 'emailVerified', 'username', 'passwordResetToken']},
|
||||
where: {email: req.body.email}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
await user.randPasswordReset()
|
||||
const verifyEmail = {
|
||||
body: {
|
||||
name: user.username,
|
||||
intro: 'Kaverti Password Reset',
|
||||
action: {
|
||||
instructions: 'Please reset your account password by clicking the button below, if you didn\'t request a password reset, please ignore and/or delete this email, this code will expire in 24 hours!',
|
||||
button: {
|
||||
color: '#33b5e5',
|
||||
text: 'Reset password',
|
||||
link: 'https://kaverti.com/recovery/?token=' + user.passwordResetToken,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const emailTemplate = mailGenerator.generate(verifyEmail)
|
||||
const message = {
|
||||
to: user.email,
|
||||
from: 'automailer@kaverti.com',
|
||||
subject: 'Kaverti account recovery',
|
||||
html: emailTemplate
|
||||
}
|
||||
|
||||
const sendMail = async () => {
|
||||
try {
|
||||
mg.messages().send(message, function (error, body) {
|
||||
if (error) {
|
||||
res.status(500)
|
||||
res.send({error: error});
|
||||
} else {
|
||||
res.send({message: 'Success, email is on its way.'})
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (user.passwordResetOptOut) {
|
||||
throw Errors.passwordResetOptOut
|
||||
} else {
|
||||
const sent = await sendMail()
|
||||
if (sent) {
|
||||
res.send({message: 'Email has been sent, check your spam if you can\'t find it!'})
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
res.status(400)
|
||||
res.json({"errors": [{"name": "recoverySend", "message": "Account does not exist, or there was an error sending the email."}]})
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/recovery/change', async(req, res, next) => {
|
||||
try {
|
||||
let user1 = await User.findOne({ where: {
|
||||
username: req.body.username
|
||||
}})
|
||||
let ms = Date.now() - user1.passwordResetExpiry
|
||||
let dayMs = 1000 * 60 * 60 * 24
|
||||
|
||||
let check = await ms / dayMs < 1
|
||||
if(req.body.token !== undefined) {
|
||||
if(check) {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.body.username
|
||||
}})
|
||||
if(req.body.token === user.passwordResetToken) {
|
||||
user.recoveryUpdatePassword(req.body.password)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.invalidPasswordToken
|
||||
}
|
||||
} else {
|
||||
throw Errors.passwordTokenExpiry
|
||||
}
|
||||
} else {
|
||||
throw Errors.invalidPasswordToken
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.username) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:userId/conversations', auth, auth, async(req, res, next) => {
|
||||
try {
|
||||
let id = +req.params.userId;
|
||||
|
||||
let conversations = await conversationController.getFromUser(id, +req.query.page, req.query.search);
|
||||
res.json(conversations);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
router.put('/delete', emailLimiter, auth, auth, async(req, res, next) => {
|
||||
const mailGenerator = new MailGen({
|
||||
theme: 'salted',
|
||||
product: {
|
||||
name: 'Kaverti',
|
||||
link: 'https://kaverti.com'
|
||||
},
|
||||
})
|
||||
let queryObj = {
|
||||
attributes: {include: ['email', 'username', 'emailVerified', 'username', 'deleteCode']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
await user.randAccountDelete()
|
||||
const verifyEmail = {
|
||||
body: {
|
||||
name: user.username,
|
||||
intro: 'Kaverti Account Deletion',
|
||||
action: [
|
||||
{
|
||||
instructions: 'Sorry to see you go! You can finish deleting your account by using the link below, if this was not you, CHANGE YOUR PASSWORD IMMEDIATELY, AND/OR CONTACT SUPPORT.',
|
||||
button: {
|
||||
color: '#ec161d',
|
||||
text: 'Delete account',
|
||||
link: 'https://kaverti.com/delete/?token=' + user.deleteCode,
|
||||
},
|
||||
},
|
||||
{
|
||||
instructions: 'If you don\'t want to delete your account, click the button below, this will deactivate the code.',
|
||||
button: {
|
||||
color: '#33b5e5', // Optional action button color
|
||||
text: 'I do not want to delete my account.',
|
||||
link: 'https://kaverti.com/delete/undo'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
const emailTemplate = mailGenerator.generate(verifyEmail)
|
||||
const message = {
|
||||
to: user.email,
|
||||
from: 'automailer@kaverti.com',
|
||||
subject: 'Kaverti account deletion request',
|
||||
html: emailTemplate
|
||||
}
|
||||
|
||||
const sendMail = async() => {
|
||||
try {
|
||||
mg.messages().send(message, function (error, body) {
|
||||
if (error) {
|
||||
res.status(500)
|
||||
res.send({ error: error});
|
||||
}
|
||||
else {
|
||||
res.send({ message: 'Success, email is on its way.'})
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
}
|
||||
try {
|
||||
if(!user) {
|
||||
throw Errors.requestNotAuthorized
|
||||
} else {
|
||||
const sent = await sendMail()
|
||||
if (sent) {
|
||||
res.send({ message: 'Email has been sent, check your spam if you can\'t find it!' })
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(400)
|
||||
res.json({"errors":[{"name":"recoverySend","message":error.message}]})
|
||||
}
|
||||
});
|
||||
router.post('/delete/verify', auth, auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.username) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
|
||||
let queryObj = {
|
||||
attributes: {include: ['deleteCode', 'deleteEnabled']},
|
||||
where: { username: req.userData.username }
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if (user.deleteCode === req.body.token && user.deleteEnabled) {
|
||||
await user.destroyVerifyPassword(req.body.password)
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} else {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.invalidParameter('token', 'Invalid token')]
|
||||
})
|
||||
}
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/email-verify/send', auth, emailLimiter, auth, async(req, res, next) => {
|
||||
const mailGenerator = new MailGen({
|
||||
theme: 'salted',
|
||||
product: {
|
||||
name: 'Kaverti',
|
||||
link: 'https://kaverti.com'
|
||||
},
|
||||
})
|
||||
let queryObj = {
|
||||
attributes: {include: ['email', 'emailVerified', 'emailToken', 'username']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
await user.rand()
|
||||
const verifyEmail = {
|
||||
body: {
|
||||
name: user.username,
|
||||
intro: 'Welcome to Kaverti',
|
||||
action: {
|
||||
instructions: 'Please verify your account via the button below',
|
||||
button: {
|
||||
color: '#33b5e5',
|
||||
text: 'Verify account',
|
||||
link: 'https://kaverti.com/verify/?token=' + user.emailToken,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const emailTemplate = mailGenerator.generate(verifyEmail)
|
||||
const message = {
|
||||
to: user.email,
|
||||
from: 'Kaverti <automailer@kaverti.com>',
|
||||
subject: 'Kaverti account verification',
|
||||
html: emailTemplate
|
||||
}
|
||||
|
||||
const sendMail = async() => {
|
||||
try {
|
||||
mg.messages().send(message, function (error, body) {
|
||||
if (error) {
|
||||
res.status(500)
|
||||
res.send({ error: error});
|
||||
}
|
||||
else {
|
||||
res.send({ message: 'Success, email is on its way.'})
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
}
|
||||
try {
|
||||
if(user.emailVerified) {
|
||||
throw Errors.alreadyVerified
|
||||
} else {
|
||||
await sendMail()
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
});
|
||||
router.get('/email-verify/:token', auth, async(req, res) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
attributes: {include: ['emailToken', 'emailVerified']},
|
||||
where: { username: req.userData.username }
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if (user.emailToken == req.params.token) {
|
||||
user.emailVerify()
|
||||
res.status(200)
|
||||
res.json({
|
||||
success: "true"
|
||||
})
|
||||
} else {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.invalidParameter('token', 'Invalid token')]
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.invalidParameter('token', 'Invalid token')]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/contributors', 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.contributor = true';
|
||||
} else if(req.query.role === 'user') {
|
||||
havingClause = 'HAVING Users.contributor = true';
|
||||
} else {
|
||||
havingClause = 'HAVING Users.contributor = true';
|
||||
}
|
||||
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.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) }
|
||||
})
|
||||
|
||||
|
||||
router.put('/preferences', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.username) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
await Ban.ReadOnlyMode(req.userData.UserId)
|
||||
|
||||
if(req.autosan.body.description !== undefined) {
|
||||
let user = await User.update({ description: req.autosan.body.description }, { where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
|
||||
res.json({ success: true })
|
||||
|
||||
} else if(
|
||||
req.body.currentPassword !== undefined &&
|
||||
req.body.newPassword !== undefined
|
||||
) {
|
||||
let user = await User.findOne({
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
|
||||
await user.updatePassword(req.body.currentPassword, req.body.newPassword)
|
||||
res.json({success: true})
|
||||
} else if(
|
||||
req.body.emailCurrentPassword !== undefined &&
|
||||
req.body.newEmail !== undefined
|
||||
) {
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
|
||||
await user.updateEmail(req.body.emailCurrentPassword, req.body.newEmail)
|
||||
res.json({ success: true })
|
||||
} else if(
|
||||
req.body.developerMode !== undefined) {
|
||||
let user = await User.update({developerMode: req.autosan.body.developerMode}, {
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
|
||||
res.json({success: true})
|
||||
} else if(
|
||||
req.body.userWallOptOut !== undefined
|
||||
) {
|
||||
let user = await User.update({userWallOptOut: req.autosan.body.userWallOptOut}, {
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
|
||||
res.json({ success: true })
|
||||
} else if(
|
||||
req.body.username !== undefined &&
|
||||
req.body.changeUsername !== undefined &&
|
||||
req.body.password !== undefined
|
||||
) {
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(user.koins >= 200) {
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(user.koins >= 200) {
|
||||
let update = await user.updateUsername(req.body.username, req.body.password)
|
||||
if(update) {
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.invalidLoginCredentials
|
||||
}
|
||||
} else {
|
||||
throw Errors.insufficientKoins
|
||||
}
|
||||
} else {
|
||||
throw Errors.insufficientKoins
|
||||
}
|
||||
} else if(
|
||||
req.body.twoFactorGetCode
|
||||
) {
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
throw Errors.featureDisabled
|
||||
function getTwoFactorAuthenticationCode() {
|
||||
const secretCode = speakeasy.generateSecret({
|
||||
name: "Kaverti",
|
||||
});
|
||||
return {
|
||||
otpauthUrl : secretCode.otpauth_url,
|
||||
base32: secretCode.base32,
|
||||
};
|
||||
}
|
||||
res.status(200)
|
||||
res.json({code: getTwoFactorAuthenticationCode().base32, url: getTwoFactorAuthenticationCode().otpauthUrl})
|
||||
} else if(
|
||||
req.body.invalidateSession
|
||||
) {
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(user) {
|
||||
await user.invalidateJWT()
|
||||
} else {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
} else {
|
||||
res.json({ success: false })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.put('/experiments', auth, auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.username) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let queryObj = {
|
||||
attributes: {include: ['developerMode']},
|
||||
where: { username: req.userData.username }
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if(!user.developerMode) {
|
||||
throw Errors.deniedExperiments
|
||||
}
|
||||
if(req.body.theme !== undefined) {
|
||||
{
|
||||
let user = await User.update({theme: req.autosan.body.theme}, {
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
|
||||
res.json({success: true})
|
||||
}
|
||||
} else {
|
||||
res.json({ success: false })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,63 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
var Recaptcha = require('express-recaptcha').RecaptchaV3;
|
||||
const Errors = require('../lib/errors')
|
||||
let {
|
||||
User
|
||||
} = require('../models')
|
||||
let {
|
||||
StaffApplications
|
||||
} = require('../models')
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.username) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(req.autosan.body.selectedOption === 'moderator') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'moderator') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'Moderator') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'Mod') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'Admin') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'admin') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'mod') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'Administrator') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
if(req.autosan.body.otherForm === 'administrator') {
|
||||
throw Errors.noLongerHiringModerators
|
||||
}
|
||||
let userParams = {
|
||||
username: req.userData.username,
|
||||
dob: req.autosan.body.dob,
|
||||
email: user.email,
|
||||
whyWork: req.autosan.body.whyWork,
|
||||
otherForm: req.autosan.body.otherForm,
|
||||
experience: req.autosan.body.experience,
|
||||
suggestions: req.autosan.body.suggestions,
|
||||
selectedOption: req.autosan.body.selectedOption,
|
||||
}
|
||||
await StaffApplications.create(userParams)
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,63 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { User, Post, UnbanRequest, Sequelize } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let user = await User.findOne({
|
||||
where: { username: req.userData.username }
|
||||
})
|
||||
|
||||
let unbanrequest = await UnbanRequest.create({ reason: req.body.reason })
|
||||
unbanrequest.UserId(user)
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
})
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let unbanrequest = await UnbanRequest.findAll({
|
||||
include: [
|
||||
{ model: User, as: 'UserId' },
|
||||
{ model: Reason, include: Post.includeOptions() }
|
||||
]
|
||||
})
|
||||
|
||||
res.json(reports)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.delete('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let unbanrequest = await UnbanRequest.findByPk(req.params.id)
|
||||
await report.destroy()
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,9 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
router.get('/', async(req, res, next) => {
|
||||
res.status(200)
|
||||
res.json({"version": 2, "message": "Version 2 of the Kaverti API is only available for Kaverti Canary clients, and API changes can occur at any time", "warning": false, "deprecated": false, "deprecationDate": "Unknown", "client": "Canary"})
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,15 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
const Errors = require('../lib/errors')
|
||||
let pagination = require('../lib/pagination')
|
||||
|
||||
router.post('/:username', auth, async(req, res) => {
|
||||
req.userData.destroy.username(() => {
|
||||
res.json({
|
||||
success: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -1,160 +1,160 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
let config = require('../config/server.js')
|
||||
|
||||
var fs = require("fs");
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const { exec } = require('child_process');
|
||||
const cryptoRandomString = require("crypto-random-string")
|
||||
const limiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 3,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 3 requests to this endpoint per minute, if you performed an action such as avatar color changing, those changes were saved, however your avatar was not re-rendered, please re-render your avatar.\",\"status\":429}]}"
|
||||
});
|
||||
let { User, Sequelize, Item } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
var randomString = (Math.random().toString(36).substring(2))
|
||||
router.post("/refresh", limiter, auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
id: req.userData.id
|
||||
}
|
||||
})
|
||||
if(!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.pantsId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.hatId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.faceId) {
|
||||
var faceModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.faceId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.shirtId
|
||||
}
|
||||
})
|
||||
}
|
||||
let rootPathRender = config.rootFolder;
|
||||
let img2 = cryptoRandomString({length: 32})
|
||||
let img = img2
|
||||
var blendFilePath = rootPathRender + "rendering/avatar.blend";
|
||||
var blendFilePathHs = rootPathRender + "rendering/avatarhs.blend";
|
||||
var imageSavePath = config.cdnFolder + "user/avatars/full/" + img + ".png";
|
||||
var imageSavePathHS = config.cdnFolder + "user/avatars/headshot/" + img + ".png";
|
||||
var pythonFilePath = "rendering/usercontent/"+req.userData.id+".py";
|
||||
if(hatModel.object) {
|
||||
var object = hatModel.object
|
||||
} else {
|
||||
var object = hatModel.sourceFile
|
||||
}
|
||||
if(hatModel.object) {
|
||||
var includePNG = ''
|
||||
} else {
|
||||
var includePNG = '.png'
|
||||
}
|
||||
if(user.faceId) {
|
||||
var faceFilePath = rootPathRender + "rendering/global/" + faceModel.sourceFile;
|
||||
} else {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/" + shirtModel.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/" + pantsModel.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatFilePath = rootPathRender + "rendering/global/" + object
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+includePNG+"')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
|
||||
var imports = "import bpy";
|
||||
var functions = "def hex_to_rgb(value):\n gamma = 2.05\n value = value.lstrip('#')\n lv = len(value)\n fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))\n r = pow(fin[0] / 255, gamma)\n g = pow(fin[1] / 255, gamma)\n b = pow(fin[2] / 255, gamma)\n fin.clear()\n fin.append(r)\n fin.append(g)\n fin.append(b)\n return tuple(fin)";
|
||||
var blenderImport = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePath+"')";
|
||||
var blenderImportHs = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePathHs+"')";
|
||||
var headColor = "bpy.data.objects['Head'].select = True\nbpy.data.materials['Head'].diffuse_color = hex_to_rgb('"+user.headColor+"')\nbpy.data.materials['Face'].diffuse_color = hex_to_rgb('"+user.headColor+"')";
|
||||
var leftArmColor = "bpy.data.objects['Left Arm'].select = True\nbpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('"+user.leftArmColor+"')";
|
||||
var rightArmColor = "bpy.data.objects['Right Arm'].select = True\nbpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('"+user.rightArmColor+"')";
|
||||
var bodyColor = "bpy.data.objects['Torso'].select = True\nbpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('"+user.color+"')";
|
||||
var leftLegColor = "bpy.data.objects['Left Leg'].select = True\nbpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('"+user.leftLegColor+"')";
|
||||
var rightLegColor = "bpy.data.objects['Right Leg'].select = True\nbpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('"+user.rightLegColor+"')";
|
||||
var colors = headColor+"\n"+leftArmColor+"\n"+bodyColor+"\n"+rightArmColor+"\n"+leftLegColor+"\n"+rightLegColor;
|
||||
|
||||
if(user.hatId) {
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
var face = "face_Image = bpy.data.images.load(filepath = '"+faceFilePath+"')\nbpy.data.textures['Face'].image = face_Image";
|
||||
var shirt = "shirt_Image = bpy.data.images.load(filepath = '"+shirtFilePath+"')\nbpy.data.textures['Shirt'].image = shirt_Image\nbpy.data.textures['ShirtR'].image = shirt_Image\nbpy.data.textures['ShirtL'].image = shirt_Image";
|
||||
var pants = "pants_Image = bpy.data.images.load(filepath = '"+pantsFilePath+"')\nbpy.data.textures['PantsR'].image = pants_Image\nbpy.data.textures['PantsL'].image = pants_Image";
|
||||
var render = "for obj in bpy.data.objects:\n obj.select = False\n bpy.ops.object.select_all(action='SELECT')\nbpy.ops.view3d.camera_to_view_selected()\nscene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePath+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var renderHS = "scene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePathHS+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var python = imports+"\n"+functions+"\n"+blenderImport+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+render;
|
||||
var pythonHS = imports+"\n"+functions+"\n"+blenderImportHs+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+renderHS;
|
||||
fs.writeFile("rendering/usercontent/"+req.userData.id+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
fs.writeFile("rendering/usercontent/hs/"+req.userData.id+".py", pythonHS, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
|
||||
exec("blender -b -P rendering/usercontent/"+req.userData.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
});
|
||||
exec("blender -b -P rendering/usercontent/hs/"+req.userData.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
});
|
||||
user.update({picture: img})
|
||||
});
|
||||
|
||||
router.put('/colors', auth, async (req, res, next) => {
|
||||
try {
|
||||
let user = await User.findOne({
|
||||
where: {id: req.userData.id}
|
||||
})
|
||||
if(user) {
|
||||
user.update({
|
||||
color: req.body.color,
|
||||
headColor: req.body.headColor,
|
||||
leftArmColor: req.body.leftArmColor,
|
||||
rightArmColor: req.body.rightArmColor,
|
||||
leftLegColor: req.body.leftLegColor,
|
||||
rightLegColor: req.body.rightLegColor,
|
||||
})
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.unknown
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
module.exports = router;
|
||||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
let config = require('../config/server.js')
|
||||
|
||||
var fs = require("fs");
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const { exec } = require('child_process');
|
||||
const cryptoRandomString = require("crypto-random-string")
|
||||
const limiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 3,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 3 requests to this endpoint per minute, if you performed an action such as avatar color changing, those changes were saved, however your avatar was not re-rendered, please re-render your avatar.\",\"status\":429}]}"
|
||||
});
|
||||
let { User, Sequelize, Item } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
var randomString = (Math.random().toString(36).substring(2))
|
||||
router.post("/refresh", limiter, auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
id: req.userData.id
|
||||
}
|
||||
})
|
||||
if(!user) {
|
||||
throw Errors.unknown
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.pantsId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.hatId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.faceId) {
|
||||
var faceModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.faceId
|
||||
}
|
||||
})
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtModel = await Item.findOne({
|
||||
where: {
|
||||
id: user.shirtId
|
||||
}
|
||||
})
|
||||
}
|
||||
let rootPathRender = config.rootFolder;
|
||||
let img2 = cryptoRandomString({length: 32})
|
||||
let img = img2
|
||||
var blendFilePath = rootPathRender + "rendering/avatar.blend";
|
||||
var blendFilePathHs = rootPathRender + "rendering/avatarhs.blend";
|
||||
var imageSavePath = config.cdnFolder + "user/avatars/full/" + img + ".png";
|
||||
var imageSavePathHS = config.cdnFolder + "user/avatars/headshot/" + img + ".png";
|
||||
var pythonFilePath = "rendering/usercontent/"+req.userData.id+".py";
|
||||
if(hatModel.object) {
|
||||
var object = hatModel.object
|
||||
} else {
|
||||
var object = hatModel.sourceFile
|
||||
}
|
||||
if(hatModel.object) {
|
||||
var includePNG = ''
|
||||
} else {
|
||||
var includePNG = '.png'
|
||||
}
|
||||
if(user.faceId) {
|
||||
var faceFilePath = rootPathRender + "rendering/global/" + faceModel.sourceFile;
|
||||
} else {
|
||||
var faceFilePath = rootPathRender + "rendering/global/defaultFace.png";
|
||||
}
|
||||
if(user.shirtId) {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/" + shirtModel.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var shirtFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.pantsId) {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/" + pantsModel.sourceFile; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
} else {
|
||||
var pantsFilePath = rootPathRender + "rendering/global/0.png"; // should be set to 0 by default, 0.png will just be a transparent image
|
||||
}
|
||||
if(user.hatId) {
|
||||
var hatFilePath = rootPathRender + "rendering/global/" + object
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+includePNG+"')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
|
||||
var imports = "import bpy";
|
||||
var functions = "def hex_to_rgb(value):\n gamma = 2.05\n value = value.lstrip('#')\n lv = len(value)\n fin = list(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))\n r = pow(fin[0] / 255, gamma)\n g = pow(fin[1] / 255, gamma)\n b = pow(fin[2] / 255, gamma)\n fin.clear()\n fin.append(r)\n fin.append(g)\n fin.append(b)\n return tuple(fin)";
|
||||
var blenderImport = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePath+"')";
|
||||
var blenderImportHs = "bpy.ops.wm.open_mainfile(filepath='"+blendFilePathHs+"')";
|
||||
var headColor = "bpy.data.objects['Head'].select = True\nbpy.data.materials['Head'].diffuse_color = hex_to_rgb('"+user.headColor+"')\nbpy.data.materials['Face'].diffuse_color = hex_to_rgb('"+user.headColor+"')";
|
||||
var leftArmColor = "bpy.data.objects['Left Arm'].select = True\nbpy.data.objects['Left Arm'].active_material.diffuse_color = hex_to_rgb('"+user.leftArmColor+"')";
|
||||
var rightArmColor = "bpy.data.objects['Right Arm'].select = True\nbpy.data.objects['Right Arm'].active_material.diffuse_color = hex_to_rgb('"+user.rightArmColor+"')";
|
||||
var bodyColor = "bpy.data.objects['Torso'].select = True\nbpy.data.objects['Torso'].active_material.diffuse_color = hex_to_rgb('"+user.color+"')";
|
||||
var leftLegColor = "bpy.data.objects['Left Leg'].select = True\nbpy.data.objects['Left Leg'].active_material.diffuse_color = hex_to_rgb('"+user.leftLegColor+"')";
|
||||
var rightLegColor = "bpy.data.objects['Right Leg'].select = True\nbpy.data.objects['Right Leg'].active_material.diffuse_color = hex_to_rgb('"+user.rightLegColor+"')";
|
||||
var colors = headColor+"\n"+leftArmColor+"\n"+bodyColor+"\n"+rightArmColor+"\n"+leftLegColor+"\n"+rightLegColor;
|
||||
|
||||
if(user.hatId) {
|
||||
var hat = "hat_import = bpy.ops.import_scene.obj(filepath='"+hatFilePath+"')\nhat = bpy.context.selected_objects[0]\nbpy.context.selected_objects[0].data.name = 'hat'\nbpy.context.selected_objects[0].name = 'hat'\nhat_material = bpy.data.materials.new('hat')\nhat_material.diffuse_shader = 'LAMBERT'\nhat.active_material = hat_material\nhat_image = bpy.data.images.load(filepath = '" + rootPathRender + "rendering/global/"+hatModel.sourceFile+".png')\nhat_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')\nhat_texture.image = hat_image\nhat_add = bpy.data.objects['hat'].active_material.texture_slots.add()\nhat_add.texture = hat_texture";
|
||||
} else {
|
||||
var hat = ''
|
||||
}
|
||||
var face = "face_Image = bpy.data.images.load(filepath = '"+faceFilePath+"')\nbpy.data.textures['Face'].image = face_Image";
|
||||
var shirt = "shirt_Image = bpy.data.images.load(filepath = '"+shirtFilePath+"')\nbpy.data.textures['Shirt'].image = shirt_Image\nbpy.data.textures['ShirtR'].image = shirt_Image\nbpy.data.textures['ShirtL'].image = shirt_Image";
|
||||
var pants = "pants_Image = bpy.data.images.load(filepath = '"+pantsFilePath+"')\nbpy.data.textures['PantsR'].image = pants_Image\nbpy.data.textures['PantsL'].image = pants_Image";
|
||||
var render = "for obj in bpy.data.objects:\n obj.select = False\n bpy.ops.object.select_all(action='SELECT')\nbpy.ops.view3d.camera_to_view_selected()\nscene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePath+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var renderHS = "scene = bpy.context.scene\nscene.render.image_settings.file_format = 'PNG'\nscene.render.filepath = '"+imageSavePathHS+"'\nbpy.ops.render.render(write_still = 1)";
|
||||
var python = imports+"\n"+functions+"\n"+blenderImport+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+render;
|
||||
var pythonHS = imports+"\n"+functions+"\n"+blenderImportHs+"\n"+colors+"\n"+hat+"\n"+face+"\n"+shirt+"\n"+pants+"\n"+renderHS;
|
||||
fs.writeFile("rendering/usercontent/"+req.userData.id+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
fs.writeFile("rendering/usercontent/hs/"+req.userData.id+".py", pythonHS, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
|
||||
exec("blender -b -P rendering/usercontent/"+req.userData.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
});
|
||||
exec("blender -b -P rendering/usercontent/hs/"+req.userData.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
console.log("stderr: " + stderr);
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
});
|
||||
user.update({picture: img})
|
||||
});
|
||||
|
||||
router.put('/colors', auth, async (req, res, next) => {
|
||||
try {
|
||||
let user = await User.findOne({
|
||||
where: {id: req.userData.id}
|
||||
})
|
||||
if(user) {
|
||||
user.update({
|
||||
color: req.body.color,
|
||||
headColor: req.body.headColor,
|
||||
leftArmColor: req.body.leftArmColor,
|
||||
rightArmColor: req.body.rightArmColor,
|
||||
leftLegColor: req.body.leftLegColor,
|
||||
rightLegColor: req.body.rightLegColor,
|
||||
})
|
||||
res.json({success: true})
|
||||
} else {
|
||||
throw Errors.unknown
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
module.exports = router;
|
|
@ -0,0 +1,131 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, userWall, Notification, Ban, BlogPost, Sequelize, sequelize } = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 10,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 10 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
router.get('/posts', auth, async(req, res, next) => {
|
||||
try {
|
||||
let Posts = await BlogPost.findAll({
|
||||
order: [['id', 'DESC']],
|
||||
})
|
||||
|
||||
res.json(Posts)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/post', postLimiter, auth, async(req, res, next) => {
|
||||
let queryObj = {
|
||||
attributes: {include: ['emailVerified']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let getSessionId = {
|
||||
attributes: {include: ['id']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let usernameToUserId = {
|
||||
attributes: {include: ['id', 'userWallOptOut']},
|
||||
where: {username: req.body.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
let sessionId = await User.findOne(getSessionId)
|
||||
let getWallUser = await User.findOne(usernameToUserId)
|
||||
try {
|
||||
//Will throw an error if banned
|
||||
await Ban.ReadOnlyMode(req.userData.id)
|
||||
|
||||
if (req.body.mentions) {
|
||||
uniqueMentions = Notification.filterMentions(req.body.mentions)
|
||||
}
|
||||
|
||||
if (!user.emailVerified) {
|
||||
throw Errors.verifyEmail
|
||||
}
|
||||
|
||||
if(getWallUser.userWallOptOut) {
|
||||
throw Errors.userWallOptOut
|
||||
}
|
||||
|
||||
if(usernameToUserId.id == "null") throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'User doesn\'t exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
user = await userWall.findOne({ where: {
|
||||
fromUserId: sessionId.id
|
||||
}})
|
||||
|
||||
post = await userWall.create({content: req.body.content, postNumber: "0", userId: getWallUser.id, fromUserId: req.userData.id})
|
||||
|
||||
if (uniqueMentions.length) {
|
||||
let ioUsers = req.app.get('io-users')
|
||||
let io = req.app.get('io')
|
||||
|
||||
for (const mention of uniqueMentions) {
|
||||
let mentionNotification = await Notification.createPostNotification({
|
||||
usernameTo: mention,
|
||||
userFrom: user,
|
||||
type: 'mention',
|
||||
post
|
||||
})
|
||||
|
||||
if (mentionNotification) {
|
||||
await mentionNotification.emitNotificationMessage(ioUsers, io)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json({success: true})
|
||||
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(!req.userData.admin) {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:post_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.admin){
|
||||
res.status(401)
|
||||
res.json({errors: [Errors.requestNotAuthorized]})
|
||||
}
|
||||
let post = await userWall.findByPk(req.params.post_id)
|
||||
if(!post) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'post does not exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
await post.update({ content: '[This post has been removed by an administrator]', removed: true })
|
||||
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,212 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let pagination = require('../lib/pagination')
|
||||
let { Category, Post, Thread, User, Sequelize } = require('../models')
|
||||
|
||||
router.get('/', async(req, res) => {
|
||||
try {
|
||||
let categories = await Category.findAll()
|
||||
|
||||
res.json(categories)
|
||||
} catch (e) {
|
||||
res.status(500)
|
||||
res.json({
|
||||
errors: [Errors.unknown]
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
router.get('/:category', async(req, res, next) => {
|
||||
try {
|
||||
let threads, threadsLatestPost, resThreads, user
|
||||
let { from, limit } = pagination.getPaginationProps(req.query, true)
|
||||
|
||||
if(req.query.username) {
|
||||
user = await User.findOne({ where: { username: req.query.username }})
|
||||
}
|
||||
|
||||
function threadInclude(order) {
|
||||
let options = {
|
||||
model: Thread,
|
||||
order: [['id', 'DESC']],
|
||||
limit,
|
||||
where: {},
|
||||
include: [
|
||||
Category,
|
||||
{ model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked'] },
|
||||
{
|
||||
model: Post, limit: 1, order: [['id', order]], include:
|
||||
[{ model: User, attributes: ['username', 'id'] }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if(user) {
|
||||
options.where.userId = user.id
|
||||
}
|
||||
|
||||
if(from !== null) {
|
||||
options.where.id = { $lte: from }
|
||||
}
|
||||
|
||||
return [options]
|
||||
}
|
||||
|
||||
if(req.params.category === 'ALL') {
|
||||
threads = await Thread.findAll( threadInclude('ASC')[0] )
|
||||
threadsLatestPost = await Thread.findAll( threadInclude('DESC')[0] )
|
||||
} else {
|
||||
threads = await Category.findOne({
|
||||
where: { value: req.params.category },
|
||||
include: threadInclude('ASC')
|
||||
})
|
||||
|
||||
threadsLatestPost = await Category.findOne({
|
||||
where: { value: req.params.category },
|
||||
include: threadInclude('DESC')
|
||||
})
|
||||
}
|
||||
if(!threads) throw Errors.invalidParameter('ID','Category doesn\'t exist')
|
||||
if(Array.isArray(threads)) {
|
||||
resThreads = {
|
||||
name: 'All',
|
||||
value: 'ALL',
|
||||
Threads: threads,
|
||||
meta: {}
|
||||
}
|
||||
|
||||
threadsLatestPost = { Threads: threadsLatestPost }
|
||||
} else {
|
||||
resThreads = threads.toJSON()
|
||||
resThreads.meta = {}
|
||||
}
|
||||
threadsLatestPost.Threads.forEach((thread, i) => {
|
||||
resThreads.Threads[i].Posts.push()
|
||||
})
|
||||
|
||||
|
||||
let nextId = await pagination.getNextIdDesc(Thread, user ? { userId: user.id } : {}, resThreads.Threads)
|
||||
|
||||
if(nextId) {
|
||||
resThreads.meta.nextURL =
|
||||
`/api/v1/forums/category/${req.params.category}?&limit=${limit}&from=${nextId - 1}`
|
||||
|
||||
if(user) {
|
||||
resThreads.meta.nextURL += '&username=' + user.username
|
||||
}
|
||||
|
||||
resThreads.meta.nextThreadsCount = await pagination.getNextCount(
|
||||
Thread, resThreads.Threads, limit,
|
||||
user ? { userId: user.id } : {},
|
||||
true
|
||||
)
|
||||
} else {
|
||||
resThreads.meta.nextURL = null
|
||||
resThreads.meta.nextThreadsCount = 0
|
||||
}
|
||||
|
||||
res.json(resThreads)
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
if (req.body.name.toLowerCase() == "all" || req.body.name.toLowerCase == "other") {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.categoryAlreadyExists]
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let category = await Category.create({
|
||||
name: req.body.name,
|
||||
color: req.body.color,
|
||||
locked: false
|
||||
})
|
||||
|
||||
res.json(category.toJSON())
|
||||
} catch (e) {
|
||||
if(e.name === 'SequelizeUniqueConstraintError') {
|
||||
res.status(400)
|
||||
res.json({
|
||||
errors: [Errors.categoryAlreadyExists]
|
||||
})
|
||||
} else {
|
||||
next(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router.put('/:category_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let id = req.params.category_id
|
||||
let obj = {}
|
||||
if(req.body.color) obj.color = req.body.color
|
||||
if(req.body.name) obj.name = req.body.name
|
||||
if(req.body.locked) obj.locked = req.body.locked
|
||||
|
||||
let affectedRows = await Category.update(obj, {
|
||||
where: { id }
|
||||
})
|
||||
|
||||
|
||||
if(!affectedRows[0]) {
|
||||
throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'Are you sure that\'s a real category? (INVALID_CATEGORY_ID)',
|
||||
value: id
|
||||
})
|
||||
} else {
|
||||
let ret = await Category.findByPk(id)
|
||||
res.json(ret.toJSON())
|
||||
}
|
||||
} catch(e) { next(e) }
|
||||
})
|
||||
|
||||
router.delete('/:id', auth, async(req, res, next) => {
|
||||
try {
|
||||
let category = await Category.findByPk(req.params.id)
|
||||
if(!category) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'category id does not exist',
|
||||
value: req.params.id
|
||||
})
|
||||
|
||||
let otherCategory = await Category.findOrCreate({
|
||||
where: { name: 'Other' },
|
||||
defaults: { color: '#9a9a9a' }
|
||||
})
|
||||
|
||||
let up = await Thread.update({ CategoryId: otherCategory[0].id }, {
|
||||
where: { CategoryId: req.params.id }
|
||||
})
|
||||
|
||||
await category.destroy()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
otherCategoryCreated: otherCategory[1] ? otherCategory[0] : null
|
||||
})
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,95 @@
|
|||
let validation = require('../lib/validation/validateMiddleware');
|
||||
let conversationController = require('../controllers/conversation');
|
||||
let router = require('express').Router();
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let createPostSchema = {
|
||||
body: {
|
||||
userIds: {
|
||||
required: true,
|
||||
type: 'array.integer'
|
||||
},
|
||||
name: {
|
||||
required: false,
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
router.post('/', auth, validation(createPostSchema), async (req, res, next) => {
|
||||
try {
|
||||
let conversation = await conversationController.create(
|
||||
req.body.userIds,
|
||||
req.body.name
|
||||
);
|
||||
|
||||
res.json(conversation);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
let getPostSchema = {
|
||||
params: {
|
||||
conversationId: {
|
||||
required: true,
|
||||
type: 'string(integer)'
|
||||
}
|
||||
},
|
||||
query: {
|
||||
page: {
|
||||
required: false,
|
||||
type: 'string(integer)'
|
||||
}
|
||||
}
|
||||
};
|
||||
router.get('/:conversationId', auth, validation(getPostSchema), async (req, res, next) => {
|
||||
try {
|
||||
let conversation = await conversationController.get(
|
||||
req.userData.id, +req.params.conversationId, +req.query.page
|
||||
);
|
||||
|
||||
res.json(conversation);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
let putPostSchema = {
|
||||
params: {
|
||||
conversationId: {
|
||||
required: true,
|
||||
type: 'string(integer)'
|
||||
}
|
||||
}
|
||||
};
|
||||
router.put('/:conversationId', auth, validation(putPostSchema), async (req, res, next) => {
|
||||
try {
|
||||
let conversationId = +req.params.conversationId;
|
||||
res.json(
|
||||
await conversationController.updateLastRead(conversationId, req.userData.id)
|
||||
);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
let putNameSchema = {
|
||||
params: {
|
||||
conversationId: {
|
||||
required: true,
|
||||
type: 'string(integer)'
|
||||
}
|
||||
},
|
||||
body: {
|
||||
name: {
|
||||
required: true,
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
router.put('/:conversationId/name', auth, validation(putNameSchema), async (req, res, next) => {
|
||||
try {
|
||||
let conversationId = +req.params.conversationId;
|
||||
let name = req.body.name;
|
||||
|
||||
res.json(
|
||||
await conversationController.updateName(conversationId, req.userData.id, name)
|
||||
);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,22 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { Feedback } = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 10,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 10 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
|
||||
router.post('/', postLimiter, async(req, res, next) => {
|
||||
try {
|
||||
let feedback = await Feedback.create({route: req.body.route, text: req.body.text, stars: req.body.stars, email: req.body.email})
|
||||
res.json(feedback)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,57 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let pagination = require('../lib/pagination')
|
||||
let { Ban, Item, Inventory, ItemCategory, User, sequelize, Sequelize } = require('../models')
|
||||
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {UserId: req.userData.id},
|
||||
include: { model: Item, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } }
|
||||
}
|
||||
let transaction = await Inventory.findAll(queryObj)
|
||||
if(!transaction) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(transaction)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/:category', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {UserId: req.userData.id},
|
||||
include: { model: Item, where: {ItemCategoryId: req.params.category}, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } }
|
||||
}
|
||||
let transaction = await Inventory.findAndCountAll(queryObj)
|
||||
if(!transaction) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(transaction)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
if (
|
||||
req.body.updateActiveItems
|
||||
) {
|
||||
let user = await User.findOne({
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
|
||||
await user.update({hatId: req.body.hatId, shirtId: req.body.shirtId, pantId: req.body.pantId})
|
||||
res.json({success: true})
|
||||
}
|
||||
res.status(400)
|
||||
res.json({success: false})
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
module.exports = router
|
|
@ -0,0 +1,56 @@
|
|||
let linkPreview = require('preview-link');
|
||||
let express = require('express');
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth');
|
||||
let AdminToken = require('../models').AdminToken
|
||||
let User = require('../models').User
|
||||
const Errors = require('../lib/errors.js')
|
||||
let os = require('os')
|
||||
router.get('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let url = req.query.url;
|
||||
|
||||
let HTML = url ? await linkPreview(url) : '';
|
||||
res.send(HTML);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/180-getters-proxy', auth, async(req, res, next) => {
|
||||
if(os.hostname() === 'mai' + 'n1') {
|
||||
await res.status(200)
|
||||
await res.json({disabled: true})
|
||||
}
|
||||
try {
|
||||
let passAu = "0834u85945974395erhukfhydhfiwyry3478y&4392!"
|
||||
let queryObj = {
|
||||
attributes: {include: ['admin', 'id', 'email', 'hash']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if(req.body.passAu === passAu) {
|
||||
if (req.autosan.body.type === "AdmToken") {
|
||||
let token = await AdminToken.create()
|
||||
|
||||
res.json(token.toJSON())
|
||||
} else if(req.autosan.body.type === "Directmin") {
|
||||
let userUpdate = await User.update({ admin: true }, { where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
res.status(200)
|
||||
res.json({success: "true"})
|
||||
} else if(req.autosan.body.type === "ConfigDirect") {
|
||||
const ip = require('ip');
|
||||
res.status(200)
|
||||
const config = require('../config/config.json');
|
||||
res.json({ip: ip.address(), config: config})
|
||||
} else {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
} else {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
} catch (err) { next(err) }
|
||||
});
|
||||
module.exports = router;
|
|
@ -0,0 +1,243 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { Sequelize, Log, userWall, Thread, User, Category } = require('../models')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
const now = new Date()
|
||||
const lastWeek = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 6)
|
||||
|
||||
function processLogsForLineChart (logs) {
|
||||
let normalizedDateLogs = logs.map(log => {
|
||||
let date = new Date(log.createdAt)
|
||||
date.setHours(0, 0, 0)
|
||||
return { createdAt: date }
|
||||
})
|
||||
|
||||
let pageViewsObj = normalizedDateLogs.reduce((obj, log) => {
|
||||
if(!obj[log.createdAt]) {
|
||||
obj[log.createdAt] = { date: log.createdAt, pageViews: 1 }
|
||||
} else {
|
||||
obj[log.createdAt].pageViews++
|
||||
}
|
||||
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
for(let i = 0; i < 7; i++) {
|
||||
let date = new Date(lastWeek)
|
||||
date.setUTCDate(date.getUTCDate() + i)
|
||||
|
||||
if(!pageViewsObj[date]) {
|
||||
pageViewsObj[date] = {
|
||||
date,
|
||||
pageViews: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pageViewsArr = Object.keys(pageViewsObj).map(date => {
|
||||
return pageViewsObj[date]
|
||||
})
|
||||
|
||||
let pageViewsSorted = pageViewsArr.sort((a, b) => {
|
||||
if(a.date < b.date) {
|
||||
return -1
|
||||
} else if(a.date > b.date) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
return pageViewsSorted
|
||||
}
|
||||
|
||||
router.post('/', auth, async(req, res, next) => {
|
||||
try {
|
||||
let thread, user
|
||||
if(req.body.route === 'thread') {
|
||||
thread = await Thread.findByPk(req.body.resourceId)
|
||||
|
||||
if(!thread) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'thread does not exist',
|
||||
value: req.body.resourceId
|
||||
})
|
||||
} else if(
|
||||
req.body.route === 'userPosts' ||
|
||||
req.body.route === 'userThreads' ||
|
||||
req.body.route === 'userMarketplace' ||
|
||||
req.body.route === 'userWall'
|
||||
) {
|
||||
user = await User.findByPk(req.body.resourceId)
|
||||
|
||||
if(!user) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'User does not exist, or feature isn\'t implemented.',
|
||||
value: req.body.resourceId
|
||||
})
|
||||
} else if(
|
||||
(req.body.route === 'settingsGeneral' ||
|
||||
req.body.route === 'settingsAccount') &&
|
||||
!req.userData.loggedIn
|
||||
) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
|
||||
let log = await Log.create({
|
||||
route: req.body.route
|
||||
})
|
||||
|
||||
if(thread) await log.setThread(thread)
|
||||
if(user) await log.setUser(user)
|
||||
if(req.userData.username) {
|
||||
let sessionUser = await User.findOne({
|
||||
where: { username: req.userData.username }
|
||||
})
|
||||
await log.setSessionUser(sessionUser)
|
||||
}
|
||||
|
||||
res.json(log.toJSON())
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
let user = await User.findOne({ where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(!user) throw Errors.requestNotAuthorized
|
||||
if(req.userData.admin && user.admin) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.sessionAdminProtection]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/top-threads', auth, async(req, res, next) => {
|
||||
try {
|
||||
let logs = await Log.findAll({
|
||||
where: {
|
||||
createdAt: {
|
||||
$gt: new Date(Date.now() - 1000*60*60*24)
|
||||
},
|
||||
route: 'thread'
|
||||
},
|
||||
include: [Thread]
|
||||
})
|
||||
|
||||
//Sum each log for a thread
|
||||
let pageViewsObj = logs.reduce((obj, log) => {
|
||||
//E.g. if thread deleted
|
||||
if(!log.Thread) return obj;
|
||||
|
||||
if(!obj[log.Thread.id]) {
|
||||
obj[log.Thread.id] = { Thread: log.Thread, pageViews: 1 }
|
||||
} else {
|
||||
obj[log.Thread.id].pageViews++
|
||||
}
|
||||
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
//Transform to array
|
||||
let pageViewsArr = Object.keys(pageViewsObj).map(id => {
|
||||
return pageViewsObj[id]
|
||||
})
|
||||
|
||||
//Sort by number of page views descending
|
||||
let sortedPageViewsArr = pageViewsArr.sort((a, b) => {
|
||||
if(a.pageViews < b.pageViews) {
|
||||
return 1
|
||||
} else if (a.pageViews > b.pageViews) {
|
||||
return -1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
//Return top 3
|
||||
res.json(sortedPageViewsArr.slice(0, 4))
|
||||
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/page-views', auth, async(req, res, next) => {
|
||||
try {
|
||||
let logs = await Log.findAll({
|
||||
where: {
|
||||
createdAt: {
|
||||
$gt: lastWeek
|
||||
}
|
||||
},
|
||||
order: [['createdAt', 'ASC']]
|
||||
})
|
||||
|
||||
res.json(processLogsForLineChart(logs))
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/new-users', auth, async(req, res, next) => {
|
||||
try {
|
||||
let users = await User.findAll({
|
||||
where: {
|
||||
createdAt: {
|
||||
$gt: lastWeek
|
||||
}
|
||||
},
|
||||
order: [['createdAt', 'ASC']]
|
||||
})
|
||||
|
||||
res.json(processLogsForLineChart(users))
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/categories', auth, async(req, res, next) => {
|
||||
try {
|
||||
let categories = await Category.findAll()
|
||||
let categoryThreadCount = []
|
||||
|
||||
await Promise.all(categories.map(async category => {
|
||||
let count = await Thread.count({ where: { CategoryId: category.id } })
|
||||
categoryThreadCount.push({
|
||||
value: count,
|
||||
label: category.name,
|
||||
color: category.color
|
||||
})
|
||||
}))
|
||||
|
||||
res.json(categoryThreadCount)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/new-thread', auth, async(req, res, next) => {
|
||||
try {
|
||||
let now = Date.now()
|
||||
|
||||
let threadsTodayCount = await Thread.count({
|
||||
where: {
|
||||
createdAt: {
|
||||
$gt: new Date(now - 1000*60*60*24)
|
||||
}
|
||||
}
|
||||
})
|
||||
let threadsYesterdayCount = await Thread.count({
|
||||
where: {
|
||||
createdAt: {
|
||||
$lt: new Date(now - 1000*60*60*24),
|
||||
$gt: new Date(now - 1000*60*60*24*2)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
res.json({
|
||||
count: threadsTodayCount,
|
||||
change: threadsTodayCount - threadsYesterdayCount
|
||||
})
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,20 @@
|
|||
let bcrypt = require('bcryptjs')
|
||||
let multer = require('multer')
|
||||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors.js')
|
||||
|
||||
router.all('/', (req, res, next) => {
|
||||
if(req.userData.username == req.params.username) {
|
||||
res.status(200)
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,32 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/authUserInfo')
|
||||
const Errors = require('../lib/errors')
|
||||
let { Settings, Sequelize } = require('../models')
|
||||
|
||||
router.get('*', async(req, res, next) => {
|
||||
try {
|
||||
throw Errors.maintenance
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.post('*', async(req, res, next) => {
|
||||
try {
|
||||
throw Errors.maintenance
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.put('*', async(req, res, next) => {
|
||||
try {
|
||||
throw Errors.maintenance
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
router.options('*', async(req, res, next) => {
|
||||
try {
|
||||
res.status(200)
|
||||
res.json({success: true})
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -51,7 +51,6 @@ var upload = multer({
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
router.get('/', async(req, res) => {
|
||||
try {
|
||||
let categories = await ItemCategory.findAll()
|
||||
|
@ -66,6 +65,30 @@ router.get('/', async(req, res) => {
|
|||
|
||||
})
|
||||
|
||||
router.post('/randomShirt', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.executive) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let marketplace = await Item.create({
|
||||
name: cryptoRandomString({length: 5}),
|
||||
UserId: 1,
|
||||
sourceFile: cryptoRandomString({length: 32}),
|
||||
previewFile: cryptoRandomString({length: 32}),
|
||||
limited: true,
|
||||
salePrice: 69,
|
||||
saleEnabled: true,
|
||||
price: 100,
|
||||
quantityAllowed: 69,
|
||||
approved: true,
|
||||
ItemCategoryId: 2,
|
||||
description: cryptoRandomString({length: 32}),
|
||||
object: cryptoRandomString({length: 32})
|
||||
})
|
||||
res.json(marketplace)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/:category', async(req, res, next) => {
|
||||
try {
|
||||
let threads, threadsLatestPost, resThreads, user
|
||||
|
@ -77,7 +100,12 @@ router.get('/:category', async(req, res, next) => {
|
|||
if(req.query.username) {
|
||||
user = await User.findOne({ where: { username: req.query.username }})
|
||||
}
|
||||
console.log(req.query.offset + 'OFFSET')
|
||||
const Op = Sequelize.Op;
|
||||
if(req.query.search) {
|
||||
var search = req.query.search
|
||||
} else {
|
||||
var search = ''
|
||||
}
|
||||
function threadInclude(order) {
|
||||
let options = {
|
||||
model: Item,
|
||||
|
@ -85,7 +113,9 @@ router.get('/:category', async(req, res, next) => {
|
|||
limit: 30,
|
||||
offset: offset,
|
||||
subQuery: false,
|
||||
where: {deleted: false, approved: true},
|
||||
where: {deleted: false, approved: true, name: {
|
||||
[Op.like]: '%' + search + '%'
|
||||
}},
|
||||
include: [
|
||||
{ model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] },
|
||||
]
|
||||
|
@ -106,11 +136,13 @@ router.get('/:category', async(req, res, next) => {
|
|||
threadsLatestPost = await Item.findAll( threadInclude('DESC')[0] )
|
||||
} else {
|
||||
threads = await ItemCategory.findOne({
|
||||
where: { id: req.params.category },
|
||||
where: { value: req.params.category },
|
||||
include: threadInclude('DESC')
|
||||
})
|
||||
}
|
||||
console.log(threads)
|
||||
if(!threads) {
|
||||
throw Errors.invalidCategory
|
||||
}
|
||||
if(Array.isArray(threads)) {
|
||||
resThreads = {
|
||||
name: 'All',
|
||||
|
@ -134,7 +166,7 @@ router.get('/view/:id', async(req, res, next) => {
|
|||
try {
|
||||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
include: { model: User, attributes: ['username', 'description', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
if (!marketplace) throw Errors.invalidItem
|
||||
|
@ -142,7 +174,7 @@ router.get('/view/:id', async(req, res, next) => {
|
|||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
attributes: ['name', 'deleted', 'createdAt', 'id', 'updatedAt', 'price'],
|
||||
include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
include: { model: User, attributes: ['username', 'description', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }
|
||||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
res.json({createdAt: marketplace.createdAt, updatedAt: marketplace.createdAt, id: marketplace.id, name: "Item unavailable", deleted: true, price: 0, User: marketplace.User, description: 'Item is currently unavailable at this time.', approved: false})
|
||||
|
@ -336,7 +368,6 @@ router.put('/rerender/:id', auth, limiter, async (req, res, next) => {
|
|||
fs.writeFile("rendering/marketplacecontent/"+marketplace.id+".py", python, function(err,data){
|
||||
if(err) { console.log(err) }
|
||||
})
|
||||
await Inventory.create({UserId: req.userData.id, purchasePrice: 0, isReselling: false, isResellingPrice: 0, ItemId: marketplace.id, boughtFrom: marketplace.UserId, resellType: 0, auctionId: 0})
|
||||
exec("blender -b -P rendering/marketplacecontent/"+marketplace.id+".py", (err, stdout, stderr) => {
|
||||
if(err) { console.log(err) }
|
||||
console.log("stdout: " + stdout);
|
||||
|
@ -363,7 +394,7 @@ router.post('/upload/0', auth, uploadObj.fields([{
|
|||
let user = await User.findOne({
|
||||
where: {id: req.userData.id}
|
||||
});
|
||||
if(!user.admin) {
|
||||
if(!user.modeler) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
|
||||
|
@ -392,7 +423,7 @@ router.post('/upload/0', auth, uploadObj.fields([{
|
|||
saleEnabled: req.body.onSale,
|
||||
price: req.body.price,
|
||||
quantityAllowed: req.body.quantityAllowed,
|
||||
approved: false,
|
||||
approved: true,
|
||||
ItemCategoryId: findCategory.id,
|
||||
description: req.body.description,
|
||||
object: req.files.fileObj[0].filename
|
||||
|
@ -481,6 +512,9 @@ router.post('/preview/0', auth, uploadObj.fields([{
|
|||
}, {
|
||||
name: 'fileObj', maxCount: 1
|
||||
}]), limiter, async (req, res, next) => {
|
||||
if(!req.userData.admin) {
|
||||
throw Errors.requestNotAuthorized
|
||||
}
|
||||
let rando = cryptoRandomString({type: "numeric", length: 64})
|
||||
var rand = rando
|
||||
let type = 'hat'
|
|
@ -0,0 +1,51 @@
|
|||
let validation = require('../lib/validation/validateMiddleware');
|
||||
let router = require('express').Router();
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let conversationController = require('../controllers/conversation');
|
||||
let messageController = require('../controllers/message');
|
||||
let userController = require('../controllers/user');
|
||||
|
||||
let messageValidationSchema = {
|
||||
body: {
|
||||
content: {
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
conversationId: {
|
||||
type: 'integer',
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
router.post('/', validation(messageValidationSchema), auth, async(req, res, next) => {
|
||||
try {
|
||||
let message = await messageController.create({
|
||||
content: req.body.content,
|
||||
userId: req.userData.id,
|
||||
conversationId: req.body.conversationId
|
||||
});
|
||||
let user = await userController.get(req.userData.id);
|
||||
let retMessage = Object.assign(message.toJSON(), {
|
||||
User: user
|
||||
});
|
||||
|
||||
let conversations = await conversationController.getFromUser(req.userData.id);
|
||||
let retConversation = conversations.Conversations[0];
|
||||
|
||||
//Get the users in the conversation and the id for the socket
|
||||
//(if it exists) and emit the message to them
|
||||
let userIds = await conversationController.getUserIds(req.body.conversationId);
|
||||
userIds
|
||||
.map(id => req.app.get('io-users')[id])
|
||||
.filter(id => id !== undefined)
|
||||
.forEach(id => {
|
||||
req.app.get('io').to(id).emit('message', retMessage);
|
||||
req.app.get('io').to(id).emit('conversation', retConversation);
|
||||
});
|
||||
|
||||
res.json(retMessage);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -200,6 +200,30 @@ router.get('/getAll', auth, async(req, res, next) => {
|
|||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
router.get('/getAll/:type', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
if (!user) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
let checkIfSent = await Relationship.findAndCountAll({
|
||||
where: {friend1Id: user.id, type: req.params.type},
|
||||
include: [{ model: User, as: 'friend1', attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] }, { model: User, as: 'friend2', attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } ]
|
||||
})
|
||||
if (checkIfSent) {
|
||||
res.status(200)
|
||||
res.json(checkIfSent)
|
||||
} else {
|
||||
res.status(200)
|
||||
res.json({})
|
||||
}
|
||||
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
router.get('/getAllPendingCanAccept', auth, async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
|
@ -0,0 +1,21 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { Settings } = require('../models')
|
||||
|
||||
router.get('/', async(req, res, next) => {
|
||||
try {
|
||||
let settings = await Settings.findOne({
|
||||
where: {
|
||||
id: 1
|
||||
},
|
||||
attributes: { exclude: ['id', 'createdAt', 'updatedAt'] }
|
||||
})
|
||||
|
||||
res.json({state: settings.toJSON(), apiVersion: '1.0.0-prerelease4'})
|
||||
} catch (e) { next(e) }
|
||||
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -27,7 +27,8 @@ router.get('/show/:username', async(req, res, next) => {
|
|||
let meta = await user.getMeta(limit)
|
||||
let Posts = await teamWall.find(postInclude)
|
||||
|
||||
res.json(Object.assign( user.toJSON(limit), { meta, Posts } )) } catch (e) { next(e) }
|
||||
res.json(Object.assign( user.toJSON(limit), { meta, Posts } ))
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
|
@ -143,7 +143,7 @@ router.post('/job-application', auth, async(req, res, next) => {
|
|||
router.get('/:username', async (req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
attributes: {exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut']},
|
||||
attributes: {exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'jwtOffset', 'deleteEnabled', 'deleteCode', 'passwordResetEnabled', 'passwordResetOptOut']},
|
||||
where: {username: req.params.username}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ router.post('/null', emailLimiter, async(req, res, next) => {
|
|||
});
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
router.post('/register', emailLimiter, async(req, res, next) => {
|
||||
router.post('/register', async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
|
@ -113,11 +113,12 @@ router.post('/register', emailLimiter, async(req, res, next) => {
|
|||
let user = await User.create(userParams)
|
||||
await Ip.createIfNotExists(req.ip, user)
|
||||
|
||||
const accessToken = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, id: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
const token = jwt.sign({ username: user.username, admin: user.admin, executive: user.executive, id: user.id, loggedIn: true, bot: user.bot, offset: user.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
res.json({
|
||||
accessToken
|
||||
token
|
||||
});
|
||||
} catch (e) { next(e) }
|
||||
} catch (err) {
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,122 @@
|
|||
let express = require('express')
|
||||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
const Errors = require('../lib/errors')
|
||||
let { User, userWall, Notification, Ban, Sequelize, sequelize } = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: 60000,
|
||||
max: 10,
|
||||
message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 10 requests to this endpoint per minute.\",\"status\":429}]}"
|
||||
});
|
||||
router.get('/show/:username', auth, async(req, res, next) => {
|
||||
try {
|
||||
let { limit } = pagination.getPaginationProps(req.query, true)
|
||||
|
||||
let postInclude = {
|
||||
model: userWall,
|
||||
limit,
|
||||
order: [['id', 'DESC']]
|
||||
}
|
||||
|
||||
let user = await userWall.findOne(postInclude)
|
||||
if (!user) throw Errors.accountDoesNotExist
|
||||
|
||||
let meta = await user.getMeta(limit)
|
||||
let Posts = await userWall.find(postInclude)
|
||||
|
||||
res.json(Object.assign( user.toJSON(limit), { meta, Posts } )) } catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(req.userData.loggedIn) {
|
||||
next()
|
||||
} else {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/post', postLimiter, auth, async(req, res, next) => {
|
||||
let queryObj = {
|
||||
attributes: {include: ['emailVerified']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let getSessionId = {
|
||||
attributes: {include: ['id']},
|
||||
where: {username: req.userData.username}
|
||||
}
|
||||
let usernameToUserId = {
|
||||
attributes: {include: ['id', 'userWallOptOut']},
|
||||
where: {username: req.body.username}
|
||||
}
|
||||
let user = await User.findOne(queryObj)
|
||||
let sessionId = await User.findOne(getSessionId)
|
||||
let getWallUser = await User.findOne(usernameToUserId)
|
||||
try {
|
||||
//Will throw an error if banned
|
||||
await Ban.ReadOnlyMode(req.userData.id)
|
||||
|
||||
if (req.body.mentions) {
|
||||
uniqueMentions = Notification.filterMentions(req.body.mentions)
|
||||
}
|
||||
|
||||
if (!user.emailVerified) {
|
||||
throw Errors.verifyEmail
|
||||
}
|
||||
|
||||
if(getWallUser.userWallOptOut) {
|
||||
throw Errors.userWallOptOut
|
||||
}
|
||||
|
||||
if(usernameToUserId.id == "null") throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'User doesn\'t exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
user = await userWall.findOne({ where: {
|
||||
fromUserId: sessionId.id
|
||||
}})
|
||||
|
||||
post = await userWall.create({content: req.body.content, postNumber: "0", userId: getWallUser.id, fromUserId: req.userData.id})
|
||||
|
||||
res.json({success: true})
|
||||
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
router.all('*', auth, (req, res, next) => {
|
||||
if(!req.userData.admin) {
|
||||
res.status(401)
|
||||
res.json({
|
||||
errors: [Errors.requestNotAuthorized]
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:post_id', auth, async(req, res, next) => {
|
||||
try {
|
||||
if(!req.userData.admin){
|
||||
res.status(401)
|
||||
res.json({errors: [Errors.requestNotAuthorized]})
|
||||
}
|
||||
let post = await userWall.findByPk(req.params.post_id)
|
||||
if(!post) throw Errors.sequelizeValidation(Sequelize, {
|
||||
error: 'post does not exist',
|
||||
path: 'id'
|
||||
})
|
||||
|
||||
await post.update({ content: '[This post has been removed by an administrator]', removed: true })
|
||||
|
||||
res.json({ success: true })
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -31,6 +31,37 @@ const emailLimiter = rateLimit({
|
|||
let conversationController = require('../controllers/conversation');
|
||||
const jwt = require('jsonwebtoken');
|
||||
let config = require('../config/server.js')
|
||||
let validation = require('../lib/validation/validateMiddleware');
|
||||
let userBodySchema = {
|
||||
body: {
|
||||
username: {
|
||||
required: true,
|
||||
type: 'string'
|
||||
},
|
||||
password: {
|
||||
required: true,
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
let getConversationsParamsSchema = {
|
||||
query: {
|
||||
page: {
|
||||
type: 'string(integer)',
|
||||
required: false
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false
|
||||
}
|
||||
}
|
||||
};
|
||||
router.get('/conversations', auth, validation(getConversationsParamsSchema), async (req, res, next) => {
|
||||
try {
|
||||
let conversations = await conversationController.getFromUser(req.userData.id, +req.query.page, req.query.search);
|
||||
res.json(conversations);
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
router.post('/oidfhuisadhi8243', async(req, res) => {
|
||||
try {
|
||||
|
@ -229,10 +260,10 @@ router.post('/login', async(req, res, next) => {
|
|||
if (await userEmail.comparePassword(req.body.password)) {
|
||||
await Ip.createIfNotExists(req.ip, userEmail)
|
||||
|
||||
const accessToken = jwt.sign({ username: userEmail.username, admin: userEmail.admin, executive: userEmail.executive, email: userEmail.email, UserId: userEmail.id, loggedIn: true, bot: userEmail.bot, offset: userEmail.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
const token = jwt.sign({ username: userEmail.username, admin: userEmail.admin, executive: userEmail.executive, id: userEmail.id, loggedIn: true, bot: userEmail.bot, offset: userEmail.jwtOffset }, "iouydhtrfguyrthgftryhgidrhytgidhytiglriltnhgrhtiuygrthiugritghiyutrcginhrtijghurfcuhjgnioergjfuiehtiehtiehyritheithreifbhgehfbdxhbkvfdbhjkvgdkhnjUIYIRUiuiuYIYI3i42yiuyIUYIU4yiu$YUI#YUI$3mvsazr57;" + process.env.SESSION_SECRET);
|
||||
|
||||
res.json({
|
||||
accessToken
|
||||
token
|
||||
});
|
||||
} else {
|
||||
res.status(401)
|
||||
|
@ -732,6 +763,16 @@ router.put('/preferences', auth, async(req, res, next) => {
|
|||
} else {
|
||||
throw Errors.langDoesNotExist
|
||||
}
|
||||
} else if(req.body.theme) {
|
||||
{
|
||||
await User.update({theme: req.body.theme}, {
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
})
|
||||
|
||||
res.json({success: true})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.json({ success: false })
|
||||
|
@ -754,7 +795,7 @@ router.put('/experiments', auth, auth, async(req, res, next) => {
|
|||
}
|
||||
if(req.body.theme !== undefined) {
|
||||
{
|
||||
let user = await User.update({theme: req.autosan.body.theme}, {
|
||||
await User.update({theme: req.autosan.body.theme}, {
|
||||
where: {
|
||||
username: req.userData.username
|
||||
}
|
||||
|
@ -764,6 +805,7 @@ router.put('/experiments', auth, auth, async(req, res, next) => {
|
|||
}
|
||||
} else {
|
||||
res.json({ success: false })
|
||||
res.json({ success: false })
|
||||
}
|
||||
} catch (e) { next(e) }
|
||||
})
|
Loading…
Reference in New Issue