TeamInvites API

This commit is contained in:
Troplo 2020-11-22 01:55:26 +11:00
parent 5539f6f77f
commit bc0c3ca626
12 changed files with 414 additions and 9 deletions

View File

@ -183,16 +183,15 @@ export default {
leaveTeam () {
this.axios
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/leave/' + this.user.username)
.then(res => {
.then(() => {
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
res(1)
})
.catch(e => {
e()
AjaxErrorHandler(this.$store)(e)
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
@ -203,16 +202,15 @@ export default {
joinTeam () {
this.axios
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/join/' + this.user.username)
.then(res => {
.then(() => {
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
res(1)
})
.catch(e => {
e()
AjaxErrorHandler(this.$store)(e)
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {

View File

@ -0,0 +1,143 @@
<template>
<main>
<section class="hero is-info is-large">
<div class="hero-body">
<div class="container" v-if="!expired && !loading">
<h2>
You have been invited to:
</h2>
<h1>
{{ team.name }}
</h1>
</div>
<div class="container" v-if="expired && !loading">
<h1>This invite has expired</h1>
</div>
<div class="container" v-if="loading">
<h1>Loading...</h1>
</div>
</div>
</section>
</main>
</template>
<script>
import AjaxErrorHandler from "@/assets/js/errorHandler";
export default {
name: 'TeamInvite',
// eslint-disable-next-line vue/no-unused-components
data () {
return {
team: {
name: '',
picture: ''
},
expired: false,
loading: true,
code: this.$route.params.code
}
},
watch: {
$route (to) {
this.selected = this.getIndexFromRoute(to.path)
}
},
computed: {
userColor () {
if(this.user) {
return this.user.color
} else {
return null
}
},
userPicture () {
if(this.user && this.user.picture) {
return 'https://cdn.kaverti.com/user/avatars/full/' + this.user.picture + '.png'
} else {
return null
}
}
},
methods: {
resetFetchData () {
this.offset = 0;
this.users = [];
this.fetchData();
},
fetchData () {
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `teams/invite/${this.$route.params.code}`)
.then(res => this.user = res.data, this.loading = false)
.catch(() => {
this.team.name = "Invite has expired or is invalid."
this.loading = false
this.expired = true
})
},
leaveTeam () {
this.axios
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/leave/' + this.user.username)
.then(() => {
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
})
.catch(e => {
AjaxErrorHandler(this.$store)(e)
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
})
},
joinTeam () {
this.axios
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/join/' + this.user.username)
.then(() => {
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
})
.catch(e => {
AjaxErrorHandler(this.$store)(e)
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
})
},
getIndexFromRoute (path) {
let selectedIndex
let route = path.split('/')[3]
this.menuItems.forEach((item, index) => {
if(item.route === route) {
selectedIndex = index
}
})
return selectedIndex
}
},
created () {
this.resetFetchData()
this.selected = this.getIndexFromRoute(this.$route.path)
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `teams/view/${this.$route.params.username}`)
.then(res => this.user = res.data)
this.axios
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'teams/check/' + this.$route.params.username)
.then(res => {
this.joined = res.data.success
})
}
}
</script>

View File

@ -79,6 +79,7 @@ const TeamRequests = () => import('./components/routes/TeamRequests')
const TeamUsers = () => import('./components/routes/TeamUsers')
const TeamWall = () => import('./components/routes/TeamWall')
const TeamMembers = () => import('./components/routes/TeamMembers')
const TeamInvite = () => import('./components/routes/TeamInvite')
const User = () => import('./components/routes/User')
const UserPosts = () => import('./components/routes/UserPosts')
@ -250,6 +251,7 @@ const router = new VueRouter({
{ path: '/user/:username/threads', component: User, redirect: '/u/:username/threads' },
{ path: '/user/:username/items', component: User, redirect: '/u/:username/items' },
{ path: '/user/:username/wall', component: User, redirect: '/u/:username/wall' },
{ path: '/invite/:code', component: TeamInvite },
{ path: '/notifications', component: Notifications },
{ path: '/inventory', component: Inventory },
{

View File

@ -63,6 +63,14 @@ let Errors = {
'Kaverti is currently under routine maintenance, website access has been restricted, please try again later.',
400
],
inviteOnly: [
'This Team is invite only',
401
],
invalidInvite: [
'This Team invite is invalid.',
401
],
accountDoesNotExist: [
'This account does not exist',
400

View File

@ -0,0 +1,37 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('TeamInvites', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true
},
TeamId: {
type: Sequelize.BIGINT,
allowNull: false
},
RoleId: {
type: Sequelize.BIGINT,
allowNull: false
},
UserId: {
type: Sequelize.BIGINT,
allowNull: false
},
uses: {
type: Sequelize.BIGINT,
default: 0,
defaultValue: 0
},
maxUses: {
type: Sequelize.BIGINT,
default: 0,
defaultValue: 0
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('TeamInvites');
}
};

View File

@ -0,0 +1,24 @@
module.exports = {
up(queryInterface, Sequelize) {
return Promise.all([
queryInterface.addColumn(
'Teams',
'inviteOnly',
{
type: Sequelize.BOOLEAN,
default: false,
defaultValue: false
},
),
queryInterface.addColumn(
'Teams',
'teamWallMemberOnly',
{
type: Sequelize.BOOLEAN,
default: false,
defaultValue: false
},
),
]);
},
}

View File

@ -0,0 +1,13 @@
module.exports = {
up(queryInterface, Sequelize) {
return Promise.all([
queryInterface.addColumn(
'TeamInvites',
'code',
{
type: Sequelize.TEXT
},
)
]);
},
}

View File

@ -0,0 +1,20 @@
module.exports = {
up(queryInterface, Sequelize) {
return Promise.all([
queryInterface.addColumn(
'TeamInvites',
'createdAt',
{
type: Sequelize.DATE
},
),
queryInterface.addColumn(
'TeamInvites',
'updatedAt',
{
type: Sequelize.DATE
},
)
]);
},
}

View File

@ -108,6 +108,11 @@ module.exports = (sequelize, DataTypes) => {
default: 1,
defaultValue: 1,
},
inviteOnly: {
type: DataTypes.BOOLEAN,
default: false,
defaultValue: false
},
picture: {
type: DataTypes.TEXT('long'),
validate: {

75
models/team_invite.js Normal file
View File

@ -0,0 +1,75 @@
let createDOMPurify = require('dompurify');
let { JSDOM } = require('jsdom');
let window = new JSDOM('').window;
let DOMPurify = createDOMPurify(window);
var escaped_str = require('querystring')
const Errors = require('../lib/errors')
let pagination = require('../lib/pagination.js')
let crypto = require("crypto");
let inviteCode = crypto.randomBytes(20).toString('hex');
module.exports = (sequelize, DataTypes) => {
let TeamInvite = sequelize.define('TeamInvite', {
TeamId: {
type: DataTypes.BIGINT,
allowNull: false
},
createdAt: {
type: DataTypes.DATE
},
updatedAt: {
type: DataTypes.DATE
},
code: {
type: DataTypes.TEXT,
default: inviteCode,
defaultValue: inviteCode,
unique: true
},
maxUses: {
type: DataTypes.BIGINT,
default: 0,
defaultValue: 0,
validate: {
len: {
args: [0, 1000],
msg: "The maximum amount of invite uses are from 0 (unlimited) to 1000!"
}
}
},
uses: {
type: DataTypes.BIGINT,
default: 0,
defaultValue: 0
},
RoleId: {
type: DataTypes.BIGINT
}
}, {
classMethods: {
associate(models) {
TeamInvite.belongsTo(models.Team)
TeamInvite.belongsTo(models.User)
},
includeOptions() {
let models = sequelize.models
return [
{model: models.User, as: 'User', attributes: ['username', 'createdAt', 'id', 'color', 'picture']},
{model: models.TeamRoles},
]
}
},
instanceMethods: {
async killInvite() {
await this.destroy()
},
async createInvite(team) {
await this.create({TeamId: team.id, RoleId: team.RoleId, maxUses: team.maxUses})
}
}
})
return TeamInvite
}

View File

@ -41,7 +41,7 @@ var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
const Errors = require('../lib/errors.js')
var format = require('date-format');
let {
User, Post, teamWall, TeamMemberRole, Transaction, teamPicture, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize, Team, TeamMembers, TeamRoles
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');
@ -405,6 +405,9 @@ router.put('/join/:username', auth, async(req, res, next) => {
if(team.banned) {
throw Errors.teamBanned
}
if(team.inviteOnly) {
throw Errors.inviteOnly
}
let queryObj3 = {
where: {userId: req.userData.UserId, teamId: team.id},
}
@ -494,5 +497,63 @@ router.put('/leave/:username', auth, async(req, res, next) => {
}
} catch (e) { next(e) }
})
router.get('/invite/:username', auth, async(req, res, next) => {
try {
await Ban.ReadOnlyMode(req.userData.username)
let code = await TeamInvite.findOne({
where: {code: req.params.username},
include: {model: Team, attributes: { exclude: [ 'banReason' ]}}
})
if (code) {
res.status(200)
res.json(code.toJSON())
} else {
throw Errors.invalidInvite
}
} catch (e) { next(e) }
})
router.post('/invite/:code', auth, async(req, res, next) => {
try {
await Ban.ReadOnlyMode(req.userData.username)
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
}
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.inviteInvalid
}
} catch (e) { next(e) }
})
module.exports = router;

View File

@ -41,7 +41,7 @@ var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
const Errors = require('../lib/errors.js')
var format = require('date-format');
let {
User, Post, teamPicture, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize, Team, TeamMembers, TeamRoles
User, Post, teamPicture, TeamInvite, 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');
@ -247,6 +247,25 @@ router.put('/roles/modify/:username/:id', auth, async(req, res, next) => {
} catch (e) { next(e) }
})
router.put('/:username/invites/create', auth, async(req, res, next) => {
try {
let team = await Team.findOne({
where: {username: req.params.username}
});
if(team) {
let create = await TeamInvite.create({
maxUses: req.body.maxUses,
RoleId: req.body.RoleId,
TeamId: team.id,
UserId: req.userData.UserId
})
let createJSON = create.toJSON()
res.status(200)
res.json(createJSON)
} else {
throw Errors.teamDoesNotExist
}
} catch (e) { next(e) }
})
module.exports = router;