forked from kaverti/website
TeamInvites API
This commit is contained in:
parent
5539f6f77f
commit
bc0c3ca626
|
@ -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 => {
|
||||
|
|
|
@ -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>
|
|
@ -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 },
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
};
|
|
@ -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
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = {
|
||||
up(queryInterface, Sequelize) {
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'TeamInvites',
|
||||
'code',
|
||||
{
|
||||
type: Sequelize.TEXT
|
||||
},
|
||||
)
|
||||
]);
|
||||
},
|
||||
}
|
|
@ -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
|
||||
},
|
||||
)
|
||||
]);
|
||||
},
|
||||
}
|
|
@ -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: {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue