forked from kaverti/website
Transaction, Inventory, Marketplace complete
This commit is contained in:
parent
e94a89b160
commit
7012da56b8
|
@ -660,7 +660,7 @@ blockquote {
|
|||
</div>
|
||||
</section>
|
||||
<b-navbar>
|
||||
<template slot="brand" v-if="!$store.state.username">
|
||||
<template slot="brand">
|
||||
<b-navbar-item tag="router-link" :to="{ path: '/' }">
|
||||
<img
|
||||
:src = $store.state.meta.icon
|
||||
|
@ -668,14 +668,6 @@ blockquote {
|
|||
<h2 style="padding-left: 3px;">{{name}}</h2>
|
||||
</b-navbar-item>
|
||||
</template>
|
||||
<template slot="brand" v-if="$store.state.username">
|
||||
<b-navbar-item tag="router-link" :to="{ path: '/dashboard' }">
|
||||
<img
|
||||
:src = $store.state.meta.icon
|
||||
>
|
||||
<h2 style="padding-left: 3px;">{{name}}</h2>
|
||||
</b-navbar-item>
|
||||
</template>
|
||||
<template slot="start">
|
||||
<b-navbar-item tag="router-link" :to="{ path: '/forums' }"><b>
|
||||
Forums
|
||||
|
@ -734,6 +726,8 @@ blockquote {
|
|||
<a class="navbar-item is-hidden-mobile" v-if="$store.state.experimentsStore.newsettings" @click="settingsModal = true">Settings</a>
|
||||
<router-link class="navbar-item is-hidden-desktop is-hidden-tablet" to='/settings' v-if="$store.state.experimentsStore.newsettings">Settings</router-link>
|
||||
<router-link class="navbar-item " to='/settings' v-if="!$store.state.experimentsStore.newsettings">Settings</router-link>
|
||||
<router-link to='/inventory' class="navbar-item">Inventory</router-link>
|
||||
<router-link to='/transactions' class="navbar-item">Transactions</router-link>
|
||||
<router-link class="navbar-item is-hidden-desktop" to='/notifications'>Notifications</router-link>
|
||||
<router-link to='/admin' class="navbar-item" v-if='$store.state.admin'>Admin Panel</router-link>
|
||||
<router-link class="navbar-item is-active" to='/premium'>Upgrade</router-link>
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<div
|
||||
class='item'
|
||||
>
|
||||
<div class="column">
|
||||
<div class="box">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-content">
|
||||
<p class="title is-4"><router-link :to="'/m/' + post.id">{{post.name}}</router-link></p>
|
||||
<p class="subtitle is-6">Created by <router-link :to="'/u/' + post.User.username"> @{{post.User.username}}</router-link></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content limit">
|
||||
{{post.description | truncate(70)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ThreadPost',
|
||||
props: [
|
||||
'post',
|
||||
'highlight',
|
||||
'showReply',
|
||||
'showThread',
|
||||
'showSelect',
|
||||
'clickForPost',
|
||||
'allowQuote'
|
||||
],
|
||||
data () {
|
||||
let post = this.post
|
||||
|
||||
return {
|
||||
hover: false,
|
||||
showShareModal: false,
|
||||
showReportPostModal: false,
|
||||
postURL: `${location.origin}/p/${post.id}`,
|
||||
selected: false,
|
||||
|
||||
showQuote: false,
|
||||
quoteX: 0,
|
||||
quoteY: 0,
|
||||
quoteSelection: '',
|
||||
|
||||
postContentHTML: post.content
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
username () {
|
||||
if(this.post.User) {
|
||||
return this.post.User.username
|
||||
} else {
|
||||
return '[deleted]'}
|
||||
},
|
||||
showActions () {
|
||||
return this.hover || this.showShareModal || this.showReportPostModal
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitReply () {
|
||||
this.showQuote = false;
|
||||
this.$emit('reply', this.post.id, this.username, this.quoteSelection);
|
||||
},
|
||||
setShowQuote () {
|
||||
let rootCoords = this.$el.getBoundingClientRect();
|
||||
|
||||
let selection = window.getSelection();
|
||||
let coords = selection.getRangeAt(0).getBoundingClientRect();
|
||||
let text = selection.toString();
|
||||
|
||||
if(text.length) {
|
||||
this.quoteY = coords.top - rootCoords.top - 30;
|
||||
this.quoteX = coords.left - rootCoords.left;
|
||||
this.quoteSelection = '> ' + text.replace(/\n/g, '\n> ') + '\n\n';
|
||||
this.showQuote = true;
|
||||
} else {
|
||||
this.showQuote = false;
|
||||
}
|
||||
},
|
||||
setShareModalState (val) {
|
||||
this.showShareModal = val
|
||||
},
|
||||
setShowReportPostModal (val) {
|
||||
this.showReportPostModal = val
|
||||
},
|
||||
goToThread () {
|
||||
this.$router.push(`/thread/${this.post.Thread.slug}/${this.post.Thread.id}`)
|
||||
},
|
||||
goToPost () {
|
||||
if(this.clickForPost) {
|
||||
this.$router.push(
|
||||
'/thread/' +
|
||||
this.post.Thread.slug + '/' +
|
||||
this.post.Thread.id + '/' +
|
||||
this.post.postNumber
|
||||
)
|
||||
}
|
||||
},
|
||||
toggleSelected () {
|
||||
this.selected = !this.selected
|
||||
|
||||
this.$emit('selected', this.post.id)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
showSelect () {
|
||||
if(this.selected) {
|
||||
this.$emit('selected', this.post.id)
|
||||
}
|
||||
|
||||
this.selected = false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$linkExpander(this.post.content, v => this.postContentHTML = v);
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -64,6 +64,11 @@
|
|||
title: 'Audit Logs',
|
||||
route: 'logs',
|
||||
icon: 'edit'
|
||||
},
|
||||
{
|
||||
title: 'Marketplace',
|
||||
route: 'marketplace',
|
||||
icon: 'shopping-cart'
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,249 +1,184 @@
|
|||
<style>
|
||||
.team-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.vertical-alt {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.limit{
|
||||
margin-top: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class='admin_users' ref='scrollElement'>
|
||||
<h1 class='admin_users__header'>Audit Logs</h1>
|
||||
<div class='category_widget__box'>
|
||||
<h2>Search</h2>
|
||||
<div class='admin_users__filters'>
|
||||
<b-input
|
||||
:large='false'
|
||||
v-model='search'
|
||||
placeholder="Search for a user"
|
||||
></b-input>
|
||||
|
||||
<main>
|
||||
<h1>Audit Logs</h1>
|
||||
<div class="section">
|
||||
<div class="">
|
||||
<b-table
|
||||
:data="users"
|
||||
:paginated="isPaginated"
|
||||
:per-page="perPage"
|
||||
:current-page.sync="currentPage"
|
||||
:pagination-simple="isPaginationSimple"
|
||||
:pagination-position="paginationPosition"
|
||||
:default-sort-direction="defaultSortDirection"
|
||||
:pagination-rounded="isPaginationRounded"
|
||||
:sort-icon="sortIcon"
|
||||
:opened-detailed="defaultOpenedDetails"
|
||||
:sort-icon-size="sortIconSize"
|
||||
default-sort="user.first_name"
|
||||
aria-next-label="Next page"
|
||||
aria-previous-label="Previous page"
|
||||
aria-page-label="Page"
|
||||
aria-current-label="Current page">
|
||||
|
||||
<b-table-column field="Text" label="Action" sortable v-slot="props">
|
||||
{{ props.row.action }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="date" label="Date" sortable centered v-slot="props">
|
||||
<span class="tag is-success">
|
||||
{{ new Date(props.row.createdAt).toLocaleDateString() }}
|
||||
</span>
|
||||
</b-table-column>
|
||||
</b-table>
|
||||
</div>
|
||||
</div>
|
||||
<scroll-load
|
||||
class='category_widget__box'
|
||||
@loadNext='fetchData'
|
||||
:loading='loading'
|
||||
query-selector='.admin_users'
|
||||
:padding-bottom='100'
|
||||
>
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<sort-menu v-model='tableSort' column='action' display='Action'></sort-menu>
|
||||
</th>
|
||||
<th>
|
||||
<sort-menu v-model='tableSort' column='username' display='Username'></sort-menu>
|
||||
</th>
|
||||
<th>
|
||||
<sort-menu v-model='tableSort' column='createdAt' display='Time/Date'></sort-menu>
|
||||
</th>
|
||||
</tr>
|
||||
<tr v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.hidden">
|
||||
<td class='admin_users__user_column'>
|
||||
Action
|
||||
</td>
|
||||
<td>
|
||||
<router-link :to='"/user/" + user.username'>{{user.username}}</router-link>
|
||||
</td>
|
||||
<td>{{user.createdAt | formatDate}}</td>
|
||||
<td>{{user.postCount}}</td>
|
||||
<td>{{user.threadCount}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<transition name='fade' mode='out-in'>
|
||||
<loading-message key='loading' v-if='loading'></loading-message>
|
||||
<div class='overlay_message' v-if='!loading && !users.length'>
|
||||
No users found
|
||||
</div>
|
||||
</transition>
|
||||
</scroll-load>
|
||||
</div>
|
||||
<p name='fade' mode='out-in'>
|
||||
<center><loading-message key='loading' v-if='loading'></loading-message></center>
|
||||
<center><div class='overlay_message' v-if='!loading && !users.length'>
|
||||
Something went wrong while loading the Audit Log, check your internet connection, or check the <a href="https://status.troplo.com">Service Status</a>
|
||||
</div></center></p>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SortMenu from '../SortMenu.vue';
|
||||
import LoadingMessage from '../LoadingMessage';
|
||||
import ScrollLoad from '../ScrollLoad';
|
||||
import LoadingMessage from '../LoadingMessage';
|
||||
import throttle from 'lodash.throttle';
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
||||
|
||||
import throttle from 'lodash.throttle';
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
||||
export default {
|
||||
name: 'Transactions',
|
||||
components: {
|
||||
LoadingMessage
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
users: [],
|
||||
isPaginated: true,
|
||||
isPaginationSimple: false,
|
||||
isPaginationRounded: false,
|
||||
paginationPosition: 'bottom',
|
||||
defaultSortDirection: 'asc',
|
||||
sortIcon: 'arrow-up',
|
||||
sortIconSize: 'is-small',
|
||||
currentPage: 1,
|
||||
perPage: 5,
|
||||
defaultOpenedDetails: [1],
|
||||
showDetailIcon: true,
|
||||
|
||||
export default {
|
||||
name: 'UserList',
|
||||
components: {
|
||||
SortMenu,
|
||||
LoadingMessage,
|
||||
ScrollLoad
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
users: [],
|
||||
showBadgeModal: false,
|
||||
loading: true,
|
||||
offset: 0,
|
||||
limit: 15,
|
||||
showTeamTab: 0,
|
||||
tcreateProd: {
|
||||
username: '',
|
||||
name: '',
|
||||
|
||||
loading: true,
|
||||
offset: 0,
|
||||
limit: 15,
|
||||
loading: false,
|
||||
|
||||
roleOptions: [
|
||||
{ name: 'Admin', value: 'admin' },
|
||||
{ name: 'Booster', value: 'booster' },
|
||||
{ name: 'System', value: 'system' },
|
||||
{ name: 'Bot', value: 'bot' }
|
||||
],
|
||||
roleSelected: ['admin', 'booster', 'system', 'bot'],
|
||||
|
||||
tableSort: {
|
||||
column: 'username',
|
||||
sort: 'desc'
|
||||
},
|
||||
user: {
|
||||
errors: {
|
||||
username: '',
|
||||
booster: '',
|
||||
bot: '',
|
||||
system: '',
|
||||
admin: ''
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
recaptcha() {
|
||||
this.$recaptcha('login').then((token) => {
|
||||
console.log(token) // Will print the token
|
||||
})},
|
||||
toggleBadgeModal (user) {
|
||||
this.user.username = user.username
|
||||
this.user.booster = user.booster
|
||||
this.user.bot = user.bot
|
||||
this.user.system = user.system
|
||||
this.user.admin = user.admin
|
||||
this.showBadgeModal = true
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
modifyUser (user) {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/user/modify', {
|
||||
username: user.username,
|
||||
bot: user.bot,
|
||||
system: user.system,
|
||||
booster: user.booster
|
||||
})
|
||||
.then(() => {
|
||||
this.showBadgeModal = false
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchData () {
|
||||
if(this.offset === null) return;
|
||||
|
||||
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `admin/logs?
|
||||
roleOptions: [
|
||||
{ name: 'Admins', value: 'admin' },
|
||||
{ name: 'Users', value: 'user' }
|
||||
],
|
||||
roleSelected: ['admin', 'user'],
|
||||
|
||||
tableSort: {
|
||||
column: 'username',
|
||||
sort: 'desc'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
if(this.offset === null) return;
|
||||
|
||||
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `admin/logs?
|
||||
sort=${this.tableSort.column}
|
||||
&order=${this.tableSort.sort}
|
||||
&offset=${this.offset}
|
||||
`;
|
||||
if(this.roleSelected.length === 1) {
|
||||
url += '&role=' + this.roleSelected[0];
|
||||
}
|
||||
if(this.search.length) {
|
||||
url += '&search=' + encodeURIComponent(this.search.trim());
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.axios
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.users.push(...res.data);
|
||||
this.loading = /*loading =*/ false;
|
||||
|
||||
//If returned data is less than the limit
|
||||
//then there must be no more pages to paginate
|
||||
if(res.data.length < this.limit) {
|
||||
this.offset = null;
|
||||
} else {
|
||||
this.offset+= this.limit;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e);
|
||||
this.loading = /*loading =*/ false;
|
||||
});
|
||||
},
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
if(this.roleSelected.length === 1) {
|
||||
url += '&role=' + this.roleSelected[0];
|
||||
}
|
||||
if(this.search.length) {
|
||||
url += '&search=' + encodeURIComponent(this.search.trim());
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.axios
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.users.push(...res.data);
|
||||
this.loading = /*loading =*/ false;
|
||||
|
||||
//If returned data is less than the limit
|
||||
//then there must be no more pages to paginate
|
||||
if(res.data.length < this.limit) {
|
||||
this.offset = null;
|
||||
} else {
|
||||
this.offset+= this.limit;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e);
|
||||
this.loading = /*loading =*/ false;
|
||||
});
|
||||
},
|
||||
mounted () {
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
tableSort: 'resetFetchData',
|
||||
roleSelected: 'resetFetchData',
|
||||
search: throttle(function () {
|
||||
this.resetFetchData();
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
getNewerUsers () {
|
||||
this.loadingNewer = true
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/logs' + '?limit=' + this.newUsers)
|
||||
.then(res => {
|
||||
this.loadingNewer = false
|
||||
this.newUsers = 0
|
||||
|
||||
this.threads.unshift(...res.data.Threads)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loadingNewer = false
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
tableSort: 'resetFetchData',
|
||||
roleSelected: 'resetFetchData',
|
||||
search: throttle(function () {
|
||||
this.resetFetchData();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '../../assets/scss/variables.scss';
|
||||
|
||||
.admin_users {
|
||||
padding: 1rem 2rem;
|
||||
|
||||
@at-root #{&}__header {
|
||||
margin: 0.5rem 0 1rem 0;
|
||||
}
|
||||
|
||||
@at-root #{&}__filters {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.select_filter {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
|
||||
th {
|
||||
border-bottom: 0.125rem solid $color__gray--darker;
|
||||
padding: 0.5rem 0.75rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tr {
|
||||
cursor: default;
|
||||
|
||||
&:first-child {
|
||||
background-color: #fff;
|
||||
}
|
||||
&:nth-child(odd) {
|
||||
background-color: lighten($color__gray--primary, 20%);
|
||||
}
|
||||
&:nth-child(even) {
|
||||
background-color: $color__gray--primary;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
}
|
||||
@at-root #{&}__user_column {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay_message {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
<style>
|
||||
.team-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.vertical-alt {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.limit{
|
||||
margin-top: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<main>
|
||||
<modal-window v-model='banModal' style='z-index: 100' width='25rem' :no-padding='true' :is-error="true">
|
||||
<div slot="header">
|
||||
Deleting {{selectedTeam}}
|
||||
</div>
|
||||
<div slot='main' class="card-content">
|
||||
<p style='margin: 1rem;'>You are deleting this item, are you sure you want to?</p>
|
||||
</div>
|
||||
<button
|
||||
slot='footer'
|
||||
class='button is-danger'
|
||||
style='z-index: 100; width: 50%;'
|
||||
@click='teamBan(selectedTeam)'
|
||||
ref='ajaxErrorsModalButton'
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
slot='footer'
|
||||
class='button is-info'
|
||||
style='z-index: 100; width: 20%; float: right;'
|
||||
@click='banModal = false'
|
||||
ref='ajaxErrorsModalButton'
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</modal-window>
|
||||
<section v-if='$store.state.experimentsStore.teams' class="hero is-info">
|
||||
<div class="hero-body" style="padding: 1rem 1rem !important;">
|
||||
<div class="mobile-container">
|
||||
<div class="container">
|
||||
<p style="text-align: center;">Teams are currently in development, expect missing features.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="section">
|
||||
<div class="">
|
||||
<div class="column" v-if="$store.state.username">
|
||||
<b-button @click="createTeamModal = true" class="is-primary">Create Team</b-button>
|
||||
</div>
|
||||
<scroll-load
|
||||
key='user-row'
|
||||
class='columns is-multiline'
|
||||
v-if='users.length'
|
||||
:loading='loading'
|
||||
@loadNext='fetchData'
|
||||
>
|
||||
<div class="column is-4" v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.banned"><div class="card">
|
||||
<div class="card-content">
|
||||
<router-link :to="'/t/' + user.username"><b-button style="float:right;">View</b-button></router-link>
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-64x64">
|
||||
<img class="team-img" v-if="user.picture !== 'default'" width="128px" height="128px" :src="user.picture">
|
||||
<p v-if="user.picture === 'default'">DEFAULT IMG</p>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">{{user.name}}</p>
|
||||
<p class="subtitle is-6">@{{user.username}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content limit">
|
||||
{{user.description | truncate(70)}}
|
||||
</div>
|
||||
<b-button @click="teamApprove(user.id)" class="is-info">Approve</b-button> <b-button @click="initialTeamBan(user.id)" class="is-danger">Delete item</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</scroll-load>
|
||||
</div>
|
||||
</div>
|
||||
<p name='fade' mode='out-in'>
|
||||
<center><loading-message key='loading' v-if='loading'></loading-message></center>
|
||||
<center><div class='overlay_message' v-if='!loading && !users.length'>
|
||||
Something went wrong while loading the users, check your internet connection, or check the <a href="https://status.troplo.com">Service Status</a>
|
||||
</div></center></p>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
import LoadingMessage from '../LoadingMessage';
|
||||
import ScrollLoad from '../ScrollLoad';
|
||||
import ModalWindow from '../ModalWindow'
|
||||
import throttle from 'lodash.throttle';
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
||||
|
||||
export default {
|
||||
name: 'AdminTeams',
|
||||
components: {
|
||||
LoadingMessage,
|
||||
ScrollLoad,
|
||||
ModalWindow
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
users: [],
|
||||
|
||||
loading: true,
|
||||
offset: 0,
|
||||
limit: 15,
|
||||
showTeamTab: 0,
|
||||
createTeamModal: false,
|
||||
reason: "None provided",
|
||||
banModal: false,
|
||||
selectedTeam: "Unknown Item",
|
||||
tcreateProd: {
|
||||
username: '',
|
||||
name: '',
|
||||
|
||||
loading: false,
|
||||
|
||||
errors: {
|
||||
username: '',
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
|
||||
roleOptions: [
|
||||
{ name: 'Admins', value: 'admin' },
|
||||
{ name: 'Users', value: 'user' }
|
||||
],
|
||||
roleSelected: ['admin', 'user'],
|
||||
|
||||
tableSort: {
|
||||
column: 'username',
|
||||
sort: 'desc'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
teamApprove(id) {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/marketplace/approve', {
|
||||
id: id,
|
||||
approve: true,
|
||||
delete: false
|
||||
})
|
||||
.then(() => {
|
||||
this.showBadgeModal = false
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
})
|
||||
},
|
||||
initialTeamBan(username) {
|
||||
this.selectedTeam = username
|
||||
this.banModal = true
|
||||
},
|
||||
teamBan(id) {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/marketplace/approve', {
|
||||
id: id,
|
||||
approve: false,
|
||||
delete: true
|
||||
})
|
||||
.then(() => {
|
||||
this.showBadgeModal = false
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
})
|
||||
},
|
||||
clearTeamErrors() {
|
||||
this.tcreateProd.errors.username = ''
|
||||
this.tcreateProd.errors.name = ''
|
||||
},
|
||||
closeAccountModal() {
|
||||
this.createTeamModal = false
|
||||
},
|
||||
fetchData () {
|
||||
if(this.offset === null) return;
|
||||
|
||||
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `admin/marketplace/pending?
|
||||
sort=${this.tableSort.column}
|
||||
&order=${this.tableSort.sort}
|
||||
&offset=${this.offset}
|
||||
`;
|
||||
if(this.roleSelected.length === 1) {
|
||||
url += '&role=' + this.roleSelected[0];
|
||||
}
|
||||
if(this.search.length) {
|
||||
url += '&search=' + encodeURIComponent(this.search.trim());
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.axios
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.users.push(...res.data);
|
||||
this.loading = /*loading =*/ false;
|
||||
|
||||
//If returned data is less than the limit
|
||||
//then there must be no more pages to paginate
|
||||
if(res.data.length < this.limit) {
|
||||
this.offset = null;
|
||||
} else {
|
||||
this.offset+= this.limit;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e);
|
||||
this.loading = /*loading =*/ false;
|
||||
});
|
||||
},
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
getNewerUsers () {
|
||||
this.loadingNewer = true
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/marketplace/pending' + '?limit=' + this.newUsers)
|
||||
.then(res => {
|
||||
this.loadingNewer = false
|
||||
this.newUsers = 0
|
||||
|
||||
this.threads.unshift(...res.data.Threads)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loadingNewer = false
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
tableSort: 'resetFetchData',
|
||||
roleSelected: 'resetFetchData',
|
||||
search: throttle(function () {
|
||||
this.resetFetchData();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,266 @@
|
|||
<style>
|
||||
.team-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.vertical-alt {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.limit{
|
||||
margin-top: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<main>
|
||||
<modal-window v-model='banModal' style='z-index: 100' width='25rem' :no-padding='true' :is-error="true">
|
||||
<div slot="header">
|
||||
Banning {{selectedTeam}}
|
||||
</div>
|
||||
<div slot='main' class="card-content">
|
||||
<p style='margin: 1rem;'>Please provide a ban reason in the input box below</p>
|
||||
<b-input v-model="reason"></b-input>
|
||||
</div>
|
||||
<button
|
||||
slot='footer'
|
||||
class='button is-danger'
|
||||
style='z-index: 100; width: 50%;'
|
||||
@click='teamBan(selectedTeam)'
|
||||
ref='ajaxErrorsModalButton'
|
||||
>
|
||||
Ban
|
||||
</button>
|
||||
<button
|
||||
slot='footer'
|
||||
class='button is-info'
|
||||
style='z-index: 100; width: 20%; float: right;'
|
||||
@click='banModal = false'
|
||||
ref='ajaxErrorsModalButton'
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</modal-window>
|
||||
<section v-if='$store.state.experimentsStore.teams' class="hero is-info">
|
||||
<div class="hero-body" style="padding: 1rem 1rem !important;">
|
||||
<div class="mobile-container">
|
||||
<div class="container">
|
||||
<p style="text-align: center;">Teams are currently in development, expect missing features.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="section">
|
||||
<div class="">
|
||||
<div class="column" v-if="$store.state.username">
|
||||
<b-button @click="createTeamModal = true" class="is-primary">Create Team</b-button>
|
||||
</div>
|
||||
<scroll-load
|
||||
key='user-row'
|
||||
class='columns is-multiline'
|
||||
v-if='users.length'
|
||||
:loading='loading'
|
||||
@loadNext='fetchData'
|
||||
>
|
||||
<div class="column is-4" v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.banned"><div class="card">
|
||||
<div class="card-content">
|
||||
<router-link :to="'/t/' + user.username"><b-button style="float:right;">View</b-button></router-link>
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-64x64">
|
||||
<img class="team-img" v-if="user.picture !== 'default'" width="128px" height="128px" :src="user.picture">
|
||||
<p v-if="user.picture === 'default'">DEFAULT IMG</p>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title is-4">{{user.name}}</p>
|
||||
<p class="subtitle is-6">@{{user.username}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content limit">
|
||||
{{user.description | truncate(70)}}
|
||||
</div>
|
||||
<b-button @click="teamApprove(user.username)" class="is-info">Approve</b-button> <b-button @click="initialTeamBan(user.username)" class="is-danger">Ban Team</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</scroll-load>
|
||||
</div>
|
||||
</div>
|
||||
<p name='fade' mode='out-in'>
|
||||
<center><loading-message key='loading' v-if='loading'></loading-message></center>
|
||||
<center><div class='overlay_message' v-if='!loading && !users.length'>
|
||||
Something went wrong while loading the users, check your internet connection, or check the <a href="https://status.troplo.com">Service Status</a>
|
||||
</div></center></p>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
import LoadingMessage from '../LoadingMessage';
|
||||
import ScrollLoad from '../ScrollLoad';
|
||||
import ModalWindow from '../ModalWindow'
|
||||
import throttle from 'lodash.throttle';
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
||||
|
||||
export default {
|
||||
name: 'AdminTeams',
|
||||
components: {
|
||||
LoadingMessage,
|
||||
ScrollLoad,
|
||||
ModalWindow
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
users: [],
|
||||
|
||||
loading: true,
|
||||
offset: 0,
|
||||
limit: 15,
|
||||
showTeamTab: 0,
|
||||
createTeamModal: false,
|
||||
reason: "None provided",
|
||||
banModal: false,
|
||||
selectedTeam: "Unknown Team",
|
||||
tcreateProd: {
|
||||
username: '',
|
||||
name: '',
|
||||
|
||||
loading: false,
|
||||
|
||||
errors: {
|
||||
username: '',
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
|
||||
roleOptions: [
|
||||
{ name: 'Admins', value: 'admin' },
|
||||
{ name: 'Users', value: 'user' }
|
||||
],
|
||||
roleSelected: ['admin', 'user'],
|
||||
|
||||
tableSort: {
|
||||
column: 'username',
|
||||
sort: 'desc'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
teamApprove(username) {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/teams/approve', {
|
||||
username: username,
|
||||
approve: true
|
||||
})
|
||||
.then(() => {
|
||||
this.showBadgeModal = false
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
})
|
||||
},
|
||||
initialTeamBan(username) {
|
||||
this.selectedTeam = username
|
||||
this.banModal = true
|
||||
},
|
||||
teamBan(username) {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/teams/approve', {
|
||||
username: username,
|
||||
approve: false,
|
||||
reason: this.reason
|
||||
})
|
||||
.then(() => {
|
||||
this.showBadgeModal = false
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
})
|
||||
},
|
||||
clearTeamErrors() {
|
||||
this.tcreateProd.errors.username = ''
|
||||
this.tcreateProd.errors.name = ''
|
||||
},
|
||||
closeAccountModal() {
|
||||
this.createTeamModal = false
|
||||
},
|
||||
fetchData () {
|
||||
if(this.offset === null) return;
|
||||
|
||||
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `admin/teams/pending?
|
||||
sort=${this.tableSort.column}
|
||||
&order=${this.tableSort.sort}
|
||||
&offset=${this.offset}
|
||||
`;
|
||||
if(this.roleSelected.length === 1) {
|
||||
url += '&role=' + this.roleSelected[0];
|
||||
}
|
||||
if(this.search.length) {
|
||||
url += '&search=' + encodeURIComponent(this.search.trim());
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.axios
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.users.push(...res.data);
|
||||
this.loading = /*loading =*/ false;
|
||||
|
||||
//If returned data is less than the limit
|
||||
//then there must be no more pages to paginate
|
||||
if(res.data.length < this.limit) {
|
||||
this.offset = null;
|
||||
} else {
|
||||
this.offset+= this.limit;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e);
|
||||
this.loading = /*loading =*/ false;
|
||||
});
|
||||
},
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
getNewerUsers () {
|
||||
this.loadingNewer = true
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/teams/pending' + '?limit=' + this.newUsers)
|
||||
.then(res => {
|
||||
this.loadingNewer = false
|
||||
this.newUsers = 0
|
||||
|
||||
this.threads.unshift(...res.data.Threads)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loadingNewer = false
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
tableSort: 'resetFetchData',
|
||||
roleSelected: 'resetFetchData',
|
||||
search: throttle(function () {
|
||||
this.resetFetchData();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,487 @@
|
|||
<template>
|
||||
<main>
|
||||
<section v-if="!$store.state.username" class="hero is-info is-link is-large is-fullheight-with-navbar" style="overflow: auto">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title">
|
||||
Kaverti
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
Kaverti is a new 3D sandbox gaming platform, and avatar social website.
|
||||
<br />
|
||||
Kaverti is home to hundreds of users who enjoy using it
|
||||
<br />
|
||||
So why not sign up today!
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="$store.state.username">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-3">
|
||||
<div class="box">
|
||||
<center>
|
||||
<avatar-icon
|
||||
:user='user'
|
||||
></avatar-icon>
|
||||
<h1>{{$store.state.username}}</h1>
|
||||
<tooltips style="padding-left: 5px;">
|
||||
<b-tooltip v-if='user && user.system' class="is-success" label="This user is a system user operated by administrators that mainly run API operations.">
|
||||
<b-tag rounded class="is-success"> SYSTEM <i class="fas fa-info-circle"></i></b-tag>
|
||||
</b-tooltip>
|
||||
|
||||
<b-tooltip v-if='user && user.bot' class="is-info" label="This user is a bot account that can run automated API operations.">
|
||||
<b-tag rounded class="is-info"> BOT <i class="fas fa-info-circle"></i></b-tag>
|
||||
</b-tooltip>
|
||||
|
||||
<b-tooltip v-if='user && user.admin' class="is-danger" label="User is an official Kaverti administrator.">
|
||||
<b-tag class="is-danger" rounded> ADMIN <i class="fas fa-info-circle"></i></b-tag>
|
||||
</b-tooltip>
|
||||
|
||||
<b-tooltip v-if='user && user.hidden' class="is-info" label="User is not discoverable in the user list.">
|
||||
<b-tag rounded> HIDDEN <i class="fas fa-info-circle"></i></b-tag>
|
||||
</b-tooltip>
|
||||
|
||||
<b-tooltip v-if='user && user.booster' class="is-light" label="User is boosting the Kaverti Discord server.">
|
||||
<b-tag class="is-light" rounded> BOOSTER <i class="fas fa-info-circle"></i></b-tag>
|
||||
</b-tooltip>
|
||||
|
||||
</tooltips>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<div class='column is-5' :class='{ "user_posts--no_border_bottom": posts && !posts.length }'>
|
||||
<div class="box">
|
||||
<h2>Global Wall</h2>
|
||||
<div
|
||||
class='editor'
|
||||
:class='{
|
||||
"editor--focus": focusInput,
|
||||
"editor--error": errors.content
|
||||
}'
|
||||
>
|
||||
<div class='editor__input'>
|
||||
<input-editor-core
|
||||
v-model='editor'
|
||||
@mentions='setMentions'
|
||||
@focus='setFocusInput(true)'
|
||||
@blur='setFocusInput(false)'
|
||||
></input-editor-core>
|
||||
</div>
|
||||
</div>
|
||||
<error-tooltip :error='errors.content' class='editor_error'></error-tooltip>
|
||||
<b-button class='submit' :loading='loading' @click='postThread'>Post on wall</b-button>
|
||||
<template v-if='!posts'>
|
||||
<thread-post-placeholder v-if='!posts'>
|
||||
</thread-post-placeholder>
|
||||
</template>
|
||||
|
||||
<scroll-load
|
||||
:loading='loadingPosts'
|
||||
@loadNext='loadNewPosts'
|
||||
v-else-if='posts.length'
|
||||
>
|
||||
<wall-post
|
||||
v-for='(post, index) in posts'
|
||||
:key='"thread-post-" + post.id'
|
||||
|
||||
:post='post'
|
||||
:show-thread='true'
|
||||
:click-for-post='true'
|
||||
:class='{"post--last": index === posts.length-1}'
|
||||
></wall-post>
|
||||
<template v-if='loadingPosts'>
|
||||
<thread-post-placeholder
|
||||
v-for='n in nextPostsCount'
|
||||
:key='"thread-post-placeholder-" + n'
|
||||
></thread-post-placeholder>
|
||||
</template>
|
||||
</scroll-load>
|
||||
<template v-else><br><br>There are no wall posts yet</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollLoad from '../ScrollLoad'
|
||||
import WallPost from '../WallPost'
|
||||
import ThreadPostPlaceholder from '../ThreadPostPlaceholder'
|
||||
import InputEditorCore from '../InputEditorCore'
|
||||
import ErrorTooltip from '../ErrorTooltip'
|
||||
import AvatarIcon from '../AvatarIcon'
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler'
|
||||
|
||||
export default {
|
||||
name: 'HomeAuthenticated',
|
||||
props: ['username'],
|
||||
components: {
|
||||
WallPost,
|
||||
ScrollLoad,
|
||||
ThreadPostPlaceholder,
|
||||
InputEditorCore,
|
||||
ErrorTooltip,
|
||||
AvatarIcon
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
selectedCategory: this.$store.state.category.selectedCategory,
|
||||
editor: '',
|
||||
mentions: [],
|
||||
name: '',
|
||||
loading: false,
|
||||
focusInput: false,
|
||||
threads: null,
|
||||
loadingThreads: false,
|
||||
nextURL: '',
|
||||
nextThreadsCount: 0,
|
||||
posts: null,
|
||||
user: null,
|
||||
|
||||
errors: {
|
||||
content: '',
|
||||
name: '',
|
||||
pollQuestion: '',
|
||||
pollAnswer: ''
|
||||
},
|
||||
|
||||
showPoll: false,
|
||||
pollQuestion: '',
|
||||
newPollAnswer: '',
|
||||
pollAnswers: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
categories () {
|
||||
return this.$store.getters.categoriesWithoutAll
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadNewPosts () {
|
||||
if(this.nextURL === null) return
|
||||
|
||||
this.loadingThreads = true
|
||||
},
|
||||
togglePoll (val) {
|
||||
if(val !== undefined) {
|
||||
this.showPoll = val
|
||||
} else {
|
||||
this.showPoll = !this.showPoll
|
||||
}
|
||||
},
|
||||
addPollAnswer () {
|
||||
if(!this.newPollAnswer.trim().length) return
|
||||
|
||||
this.pollAnswers.push({ answer: this.newPollAnswer })
|
||||
this.newPollAnswer = ''
|
||||
},
|
||||
removePollAnswer ($index) {
|
||||
this.pollAnswers.splice($index, 1)
|
||||
},
|
||||
removePoll () {
|
||||
this.pollQuestion = ''
|
||||
this.pollAnswers = []
|
||||
this.newPollAnswer = ''
|
||||
|
||||
this.togglePoll()
|
||||
},
|
||||
|
||||
setErrors (errors) {
|
||||
errors.forEach(error => {
|
||||
this.errors[error.name] = error.error
|
||||
})
|
||||
},
|
||||
getUserInfo () {
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'userinfo')
|
||||
},
|
||||
clearErrors () {
|
||||
this.errors.content = ''
|
||||
this.errors.name = ''
|
||||
this.errors.pollQuestion = ''
|
||||
this.errors.pollAnswer = ''
|
||||
},
|
||||
|
||||
hasDuplicates (array, cb) {
|
||||
if(cb) array = array.map(cb)
|
||||
|
||||
return array.length !== (new Set(array)).size
|
||||
},
|
||||
|
||||
postThread () {
|
||||
let errors = []
|
||||
|
||||
this.clearErrors()
|
||||
|
||||
if(!this.editor.trim().length) {
|
||||
errors.push({name: 'content', error: 'Post content cannot be blank'})
|
||||
} if(errors.length) {
|
||||
this.setErrors(errors)
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
|
||||
this.axios.post(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `wall/post`, {
|
||||
username: "GlobalWall",
|
||||
content: this.editor,
|
||||
mentions: this.mentions
|
||||
}).then(() => {
|
||||
let ajax = []
|
||||
return Promise.all(ajax)
|
||||
}).then(() => {
|
||||
this.loading = false
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `user/GlobalWall?wall=true`)
|
||||
.then(res => {
|
||||
this.posts = res.data.userWalls
|
||||
this.nextPostsCount = res.data.meta.postNumber
|
||||
})
|
||||
}).catch(e => {
|
||||
this.loading = false
|
||||
|
||||
AjaxErrorHandler(this.$store)(e, (error, errors) => {
|
||||
let path = error.path
|
||||
|
||||
if(this.errors[path] !== undefined) {
|
||||
this.errors[path] = error.message
|
||||
} else {
|
||||
errors.push(error.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
setFocusInput (val) {
|
||||
this.focusInput = val
|
||||
},
|
||||
setMentions (mentions) {
|
||||
this.mentions = mentions
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$store.state.username' (username) {
|
||||
if(!username) {
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getUserInfo();
|
||||
this.$store.dispatch('setTitle', 'Dashboard')
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `user/GlobalWall?wall=true`)
|
||||
.then(res => {
|
||||
this.loadingPosts = false
|
||||
this.posts = res.data.userWalls
|
||||
this.nextPostsCount = res.data.meta.nextPostsCount
|
||||
})
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `userinfo`)
|
||||
.then(res => {
|
||||
this.loadingPosts = false
|
||||
this.user = res.data
|
||||
this.nextPostsCount = res.data.meta.nextPostsCount
|
||||
})
|
||||
},
|
||||
beforeRouteEnter (to, from, next) {
|
||||
next(vm => {
|
||||
if(!vm.$store.state.username) {
|
||||
vm.$store.commit('setAccountModalState', true);
|
||||
next('/')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
@import '../../assets/scss/variables.scss';
|
||||
|
||||
.thread_new {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.thread_meta_info {
|
||||
background-color: #fff;
|
||||
border: thin solid $color__gray--darker;
|
||||
border-radius: 0.25rem;
|
||||
padding: 1rem;
|
||||
margin: 1rem 0;
|
||||
|
||||
@at-root #{&}__title {
|
||||
margin: 0 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@at-root #{&}__form {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
@at-root #{&}__add_poll {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
@at-root #{&}__text {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@at-root #{&}__poll {
|
||||
border-top: thin solid $color__gray--primary;
|
||||
margin-top: 1rem;
|
||||
padding-top: 0.75rem;
|
||||
position: relative;
|
||||
|
||||
@at-root #{&}__top_bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
@at-root #{&}__answer {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
& > span {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: all 0.1s;
|
||||
|
||||
font-size: 1.5rem;
|
||||
margin-left: 0.5rem;
|
||||
cursor: pointer;
|
||||
@include user-select(none);
|
||||
}
|
||||
|
||||
&:hover > span {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.editor {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-radius: 0.25rem;
|
||||
border: thin solid $color__gray--darker;
|
||||
|
||||
transition: all 0.2s;
|
||||
|
||||
@at-root #{&}--focus {
|
||||
border: thin solid $color__gray--darkest;
|
||||
}
|
||||
@at-root #{&}--error {
|
||||
border: thin solid $color__red--primary;
|
||||
}
|
||||
|
||||
@at-root #{&}__format_bar {
|
||||
height: 2.5rem;
|
||||
background-color: $color__gray--primary;
|
||||
display: flex;
|
||||
padding-right: 1rem;
|
||||
padding-bottom: 0.25rem;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
font-variant: small-caps;
|
||||
|
||||
@at-root #{&}--preview {
|
||||
border-radius: 0 0.25rem 0 0;
|
||||
}
|
||||
@at-root #{&}--editor {
|
||||
border-radius: 0.25rem 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@at-root #{&}__input {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.input_editor_core__format_bar {
|
||||
left: 0rem;
|
||||
}
|
||||
.input_editor_core textarea {
|
||||
height: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@at-root #{&}__preview {
|
||||
border-left: 1px solid $color__gray--darker;
|
||||
|
||||
div.input_editor_preview__markdownHTML {
|
||||
height: 14.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editor_error {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
margin-top: 0.5rem;
|
||||
border-radius: 0.2rem;
|
||||
border: thin solid $color__red--primary;
|
||||
|
||||
&.error_tooltip--show {
|
||||
max-height: 4rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.thread_meta_info {
|
||||
@at-root #{&}__form {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@at-root #{&}__title.fancy_input {
|
||||
margin: 0;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
@at-root #{&}__poll__question {
|
||||
width: 100%;
|
||||
|
||||
> div, input {
|
||||
width: 100% ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
|
||||
@at-root #{&}__input, #{&}__preview {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.user_posts {
|
||||
background: #fff;
|
||||
border-radius: 0.25rem;
|
||||
padding: 1rem;
|
||||
border: thin solid $color__gray--darker;
|
||||
|
||||
@at-root #{&}__title {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint--tablet) {
|
||||
.user_posts {
|
||||
margin-top: 1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<template>
|
||||
<main>
|
||||
<section class="hero is-info is-link is-large is-fullheight-with-navbar" style="overflow: auto">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title">
|
||||
Kaverti
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
Kaverti is a new 3D sandbox gaming platform, and avatar social website.
|
||||
<br />
|
||||
Kaverti is home to hundreds of users who enjoy using it
|
||||
<br />
|
||||
So why not sign up today!
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
|
@ -0,0 +1,165 @@
|
|||
<style>
|
||||
.vertical-alt {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.limit{
|
||||
margin-top: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<main>
|
||||
<div class="section">
|
||||
<div class="">
|
||||
<scroll-load
|
||||
key='user-row'
|
||||
class='columns is-multiline'
|
||||
v-if='users.length'
|
||||
:loading='loading'
|
||||
@loadNext='fetchData'
|
||||
>
|
||||
<div class="column is-4" v-for='user in users' :key='"user-row" + user.name' v-show="user && !user.hidden"><div class="card">
|
||||
<div class="card-content">
|
||||
<div class="media-left">
|
||||
<figure class="image is-128x128">
|
||||
<img v-if="user.picture !== 'default' && user.approved" width="128px" height="128px" :src="user.Item.picture">
|
||||
<img v-if="user.picture === 'default' && $store.state.theme === 'light' && user.Item.approved" width="128px" height="128px" src="https://cdn.kaverti.com/teams/unknown-light.png">
|
||||
<img v-if="user.picture === 'default' && $store.state.theme === 'dark' && user.Item.approved" width="128px" height="128px" src="https://cdn.kaverti.com/teams/unknown-dark.png">
|
||||
<img v-if="$store.state.theme === 'light' && !user.Item.approved" width="128px" height="128px" src="https://cdn.kaverti.com/teams/pending-light.png">
|
||||
<img v-if="$store.state.theme === 'dark' && !user.Item.approved" width="128px" height="128px" src="https://cdn.kaverti.com/teams/pending-dark.png">
|
||||
</figure>
|
||||
</div>
|
||||
<br>
|
||||
<div class="media">
|
||||
<div class="media-content">
|
||||
<p class="title is-4"><router-link :to="'/m/' + user.Item.id">{{user.Item.name}}</router-link></p>
|
||||
<p class="subtitle is-6">Created by <router-link :to="'/u/' + user.Item.User.username"> @{{user.Item.User.username}}</router-link></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content limit">
|
||||
{{user.Item.description | truncate(70)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</scroll-load>
|
||||
</div>
|
||||
<p name='fade' mode='out-in'>
|
||||
<center><loading-message key='loading' v-if='loading'></loading-message></center>
|
||||
<center><div class='overlay_message' v-if='!loading && !users.length'>
|
||||
Something went wrong while loading the Marketplace Items, check your internet connection, or check the <a href="https://status.troplo.com">Service Status</a> or you don't have any items.
|
||||
|
||||
</div></center></p>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
import LoadingMessage from '../LoadingMessage';
|
||||
import ScrollLoad from '../ScrollLoad';
|
||||
import throttle from 'lodash.throttle';
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
||||
|
||||
export default {
|
||||
name: 'Inventory',
|
||||
components: {
|
||||
LoadingMessage,
|
||||
ScrollLoad
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
users: [],
|
||||
|
||||
loading: true,
|
||||
offset: 0,
|
||||
limit: 15,
|
||||
|
||||
roleOptions: [
|
||||
{ name: 'Admins', value: 'admin' },
|
||||
{ name: 'Users', value: 'user' }
|
||||
],
|
||||
roleSelected: ['admin', 'user'],
|
||||
|
||||
tableSort: {
|
||||
column: 'username',
|
||||
sort: 'desc'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
if(this.offset === null) return;
|
||||
|
||||
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'inventory' + `?
|
||||
sort=${this.tableSort.column}
|
||||
&order=${this.tableSort.sort}
|
||||
&offset=${this.offset}
|
||||
`;
|
||||
if(this.roleSelected.length === 1) {
|
||||
url += '&role=' + this.roleSelected[0];
|
||||
}
|
||||
if(this.search.length) {
|
||||
url += '&search=' + encodeURIComponent(this.search.trim());
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.axios
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.users.push(...res.data);
|
||||
this.loading = /*loading =*/ false;
|
||||
|
||||
//If returned data is less than the limit
|
||||
//then there must be no more pages to paginate
|
||||
if(res.data.length < this.limit) {
|
||||
this.offset = null;
|
||||
} else {
|
||||
this.offset+= this.limit;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e);
|
||||
this.loading = /*loading =*/ false;
|
||||
});
|
||||
},
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
getNewerUsers () {
|
||||
this.loadingNewer = true
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'inventory' + '?limit=' + this.newUsers)
|
||||
.then(res => {
|
||||
this.loadingNewer = false
|
||||
this.newUsers = 0
|
||||
|
||||
this.threads.unshift(...res.data)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loadingNewer = false
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
tableSort: 'resetFetchData',
|
||||
roleSelected: 'resetFetchData',
|
||||
search: throttle(function () {
|
||||
this.resetFetchData();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -58,7 +58,7 @@
|
|||
</div>
|
||||
<div class="column is-7">
|
||||
<p>
|
||||
<span class="title is-bold limit">{{user.name}}</span><br>
|
||||
<span class="title is-bold limit">{{user.name}} <b-tooltip v-if="user.banned" class="is-danger" label="Team is banned and cannot be interacted with."><b-tag v-if="user.banned" class="is-danger">BANNED</b-tag></b-tooltip></span><br>
|
||||
<span class="subtitle limit">@{{user.username}}</span>
|
||||
<br>
|
||||
</p>
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
:loading='loading'
|
||||
@loadNext='fetchData'
|
||||
>
|
||||
<div class="column is-4" v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.hidden"><div class="card">
|
||||
<div class="column is-4" v-for='user in users' :key='"user-row" + user.username' v-show="user && !user.banned"><div class="card">
|
||||
<div class="card-content">
|
||||
<router-link :to="'/t/' + user.username"><b-button style="float:right;">View</b-button></router-link>
|
||||
<div class="media">
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<style>
|
||||
.team-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.vertical-alt {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.limit{
|
||||
margin-top: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<main>
|
||||
<div class="section column">
|
||||
<div class="box">
|
||||
<h1>Transaction Log</h1>
|
||||
<b-table
|
||||
:data="users"
|
||||
:paginated="isPaginated"
|
||||
:per-page="perPage"
|
||||
:current-page.sync="currentPage"
|
||||
:pagination-simple="isPaginationSimple"
|
||||
:pagination-position="paginationPosition"
|
||||
:default-sort-direction="defaultSortDirection"
|
||||
:pagination-rounded="isPaginationRounded"
|
||||
:sort-icon="sortIcon"
|
||||
:opened-detailed="defaultOpenedDetails"
|
||||
:sort-icon-size="sortIconSize"
|
||||
default-sort="user.first_name"
|
||||
aria-next-label="Next page"
|
||||
aria-previous-label="Previous page"
|
||||
aria-page-label="Page"
|
||||
aria-current-label="Current page">
|
||||
|
||||
<b-table-column field="Text" label="Message" sortable v-slot="props">
|
||||
{{ props.row.text }}
|
||||
</b-table-column>
|
||||
|
||||
|
||||
<b-table-column field="user.last_name" label="Limited Edition" sortable v-slot="props">
|
||||
{{ props.row.limited }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="date" label="Date" sortable centered v-slot="props">
|
||||
<span class="tag is-success">
|
||||
{{ new Date(props.row.createdAt).toLocaleDateString() }}
|
||||
</span>
|
||||
</b-table-column>
|
||||
</b-table>
|
||||
</div>
|
||||
</div>
|
||||
<p name='fade' mode='out-in'>
|
||||
<center><loading-message key='loading' v-if='loading'></loading-message></center>
|
||||
<center><div class='overlay_message' v-if='!loading && !users.length'>
|
||||
Something went wrong while loading the Transaction Log, check your internet connection, or check the <a href="https://status.troplo.com">Service Status</a>
|
||||
</div></center></p>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
import LoadingMessage from '../LoadingMessage';
|
||||
import throttle from 'lodash.throttle';
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler';
|
||||
|
||||
export default {
|
||||
name: 'Transactions',
|
||||
components: {
|
||||
LoadingMessage
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
users: [],
|
||||
isPaginated: true,
|
||||
isPaginationSimple: false,
|
||||
isPaginationRounded: false,
|
||||
paginationPosition: 'bottom',
|
||||
defaultSortDirection: 'asc',
|
||||
sortIcon: 'arrow-up',
|
||||
sortIconSize: 'is-small',
|
||||
currentPage: 1,
|
||||
perPage: 5,
|
||||
defaultOpenedDetails: [1],
|
||||
showDetailIcon: true,
|
||||
|
||||
loading: true,
|
||||
offset: 0,
|
||||
limit: 15,
|
||||
showTeamTab: 0,
|
||||
tcreateProd: {
|
||||
username: '',
|
||||
name: '',
|
||||
|
||||
loading: false,
|
||||
|
||||
errors: {
|
||||
username: '',
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
|
||||
roleOptions: [
|
||||
{ name: 'Admins', value: 'admin' },
|
||||
{ name: 'Users', value: 'user' }
|
||||
],
|
||||
roleSelected: ['admin', 'user'],
|
||||
|
||||
tableSort: {
|
||||
column: 'username',
|
||||
sort: 'desc'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
if(this.offset === null) return;
|
||||
|
||||
let url = process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `transactions?
|
||||
sort=${this.tableSort.column}
|
||||
&order=${this.tableSort.sort}
|
||||
&offset=${this.offset}
|
||||
`;
|
||||
if(this.roleSelected.length === 1) {
|
||||
url += '&role=' + this.roleSelected[0];
|
||||
}
|
||||
if(this.search.length) {
|
||||
url += '&search=' + encodeURIComponent(this.search.trim());
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.axios
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.users.push(...res.data);
|
||||
this.loading = /*loading =*/ false;
|
||||
|
||||
//If returned data is less than the limit
|
||||
//then there must be no more pages to paginate
|
||||
if(res.data.length < this.limit) {
|
||||
this.offset = null;
|
||||
} else {
|
||||
this.offset+= this.limit;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
AjaxErrorHandler(this.$store)(e);
|
||||
this.loading = /*loading =*/ false;
|
||||
});
|
||||
},
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
getNewerUsers () {
|
||||
this.loadingNewer = true
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'transactions' + '?limit=' + this.newUsers)
|
||||
.then(res => {
|
||||
this.loadingNewer = false
|
||||
this.newUsers = 0
|
||||
|
||||
this.threads.unshift(...res.data.Threads)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loadingNewer = false
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
tableSort: 'resetFetchData',
|
||||
roleSelected: 'resetFetchData',
|
||||
search: throttle(function () {
|
||||
this.resetFetchData();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -79,6 +79,9 @@
|
|||
<b-button class="menu_button" :key='"user-menu-item-threads"' @click='$router.push(`/user/${username}/threads`)'>
|
||||
Threads
|
||||
</b-button>
|
||||
<b-button class="menu_button" :key='"user-menu-item-threads"' @click='$router.push(`/user/${username}/items`)'>
|
||||
Items
|
||||
</b-button>
|
||||
<br/> <br/>
|
||||
<div class="column box">
|
||||
<router-view :username='username'></router-view>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<main>
|
||||
|
||||
</main>
|
||||
</template>
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<scroll-load
|
||||
:loading='loadingMarketplace'
|
||||
@loadNext='loadNewmarketplace'
|
||||
@loadNext='loadNewMarketplace'
|
||||
v-else-if='marketplace.length'
|
||||
>
|
||||
<thread-post
|
||||
|
@ -28,13 +28,13 @@
|
|||
></thread-post-placeholder>
|
||||
</template>
|
||||
</scroll-load>
|
||||
<template v-else>This user hasn't posted anything yet</template>
|
||||
<template v-else>This user doesn't have any Marketplace Items yet</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollLoad from '../ScrollLoad'
|
||||
import ThreadPost from '../ThreadPost'
|
||||
import ThreadPost from '../MarketplaceItemView'
|
||||
import ThreadPostPlaceholder from '../ThreadPostPlaceholder'
|
||||
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler'
|
||||
|
@ -57,7 +57,7 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
loadNewmarketplace () {
|
||||
loadNewMarketplace () {
|
||||
if(this.nextURL === null) return
|
||||
|
||||
this.loadingmarketplace = true
|
||||
|
@ -88,9 +88,9 @@
|
|||
this.$store.dispatch('setTitle', this.$route.params.username + ' - Marketplace')
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `marketplace/user/${this.$route.params.username}`)
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `user/${this.$route.params.username}?marketplace=true`)
|
||||
.then(res => {
|
||||
this.marketplace = res.data.Marketplace
|
||||
this.marketplace = res.data.Items
|
||||
this.nextURL = res.data.meta.nextURL
|
||||
this.nextMarketplaceCount = res.data.meta.nextMarketplaceCount
|
||||
})
|
||||
|
|
|
@ -57,7 +57,7 @@ Vue.component('font-awesome-icon', FontAwesomeIcon);
|
|||
|
||||
const Index = () => import('./components/routes/Index')
|
||||
const CategorySelect = () => import('./components/routes/CategorySelect')
|
||||
const HomeUnauthenticated = () => import('./components/routes/HomeUnauthenticated')
|
||||
const Home = () => import('./components/routes/Home')
|
||||
const HomeAuthenticated = () => import('./components/routes/HomeAuthenticated')
|
||||
|
||||
const P = () => import('./components/routes/P')
|
||||
|
@ -84,6 +84,7 @@ const UserPosts = () => import('./components/routes/UserPosts')
|
|||
const UserThreads = () => import('./components/routes/UserThreads')
|
||||
const UserMarketplace = () => import('./components/routes/UserMarketplace')
|
||||
const UserWall = () => import('./components/routes/UserWall')
|
||||
const UserInventory = () => import('./components/routes/UserInventory')
|
||||
const UsersList = () => import('./components/routes/UsersList')
|
||||
const Notifications = () => import('./components/routes/Notifications')
|
||||
const Banned = () => import('./components/routes/Banned')
|
||||
|
@ -127,14 +128,19 @@ const AdminOther = () => import('./components/routes/AdminOther')
|
|||
const AdminBadges = () => import('./components/routes/AdminAudit')
|
||||
const AdminApplications = () => import('./components/routes/AdminApplications')
|
||||
const AdminPrivileges = () => import('./components/routes/AdminPrivileges')
|
||||
const AdminTeams = () => import('./components/routes/AdminTeams')
|
||||
const AdminMarketplace = () => import('./components/routes/AdminMarketplace')
|
||||
|
||||
const Licenses = () => import('./components/routes/LICENSES')
|
||||
const ConnectionProblems = () => import('./components/routes/ConnectionProblems')
|
||||
const Contributors = () => import('./components/routes/Contributors')
|
||||
|
||||
const Inventory = () => import('./components/routes/Inventory')
|
||||
|
||||
const Marketplace = () => import('./components/routes/Marketplace')
|
||||
const MarketplaceItem = () => import('./components/routes/MarketplaceItem')
|
||||
const MarketplaceItemAuction = () => import('./components/routes/MarketplaceItemAuction')
|
||||
const TransactionLog = () => import('./components/routes/TransactionLog')
|
||||
|
||||
import NotFound from './components/routes/NotFound'
|
||||
import Reserved from './components/routes/Reserved'
|
||||
|
@ -165,8 +171,8 @@ Vue.use({
|
|||
|
||||
const router = new VueRouter({
|
||||
routes: [
|
||||
{ path: '/', component: HomeUnauthenticated },
|
||||
{ path: '/dashboard', component: HomeAuthenticated },
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/dashboard', redirect: '/', component: HomeAuthenticated },
|
||||
{ path: '/category/select', component: CategorySelect },
|
||||
{ path: '/category/:category', component: Index },
|
||||
{ path: '/p/:id', component: P },
|
||||
|
@ -184,6 +190,7 @@ const router = new VueRouter({
|
|||
{ path: '/thread/:slug/:id/:post_number', name: 'thread-post', component: Thread },
|
||||
{ path: '/thread/new', component: ThreadNew },
|
||||
{ path: '/marketplace', component: Marketplace },
|
||||
{ path: '/transactions', component: TransactionLog },
|
||||
{ path: '/m/:id', component: MarketplaceItem, children: [
|
||||
{ path: 'auction', component: MarketplaceItemAuction }
|
||||
] },
|
||||
|
@ -238,6 +245,7 @@ const router = new VueRouter({
|
|||
{ path: '/user/:username/items', component: User, redirect: '/u/:username/items' },
|
||||
{ path: '/user/:username/wall', component: User, redirect: '/u/:username/wall' },
|
||||
{ path: '/notifications', component: Notifications },
|
||||
{ path: '/inventory', component: Inventory },
|
||||
{
|
||||
path: '/chat',
|
||||
component: Chat,
|
||||
|
@ -251,7 +259,8 @@ const router = new VueRouter({
|
|||
{ path: 'posts', component: UserPosts },
|
||||
{ path: 'threads', component: UserThreads },
|
||||
{ path: 'items', component: UserMarketplace },
|
||||
{ path: 'wall', component: UserWall }
|
||||
{ path: 'wall', component: UserWall },
|
||||
{ path: 'inventory', component: UserInventory }
|
||||
] },
|
||||
{ path: '/settings', redirect: '/settings/general', component: Settings, children: [
|
||||
{ path: 'general', component: SettingsGeneral },
|
||||
|
@ -273,6 +282,8 @@ const router = new VueRouter({
|
|||
{ path: 'logs', component: AdminBadges },
|
||||
{ path: 'applications', component: AdminApplications },
|
||||
{ path: 'privileges', component: AdminPrivileges },
|
||||
{ path: 'teams', component: AdminTeams },
|
||||
{ path: 'marketplace', component: AdminMarketplace },
|
||||
] },
|
||||
{ path: '*', component: NotFound }
|
||||
],
|
||||
|
|
|
@ -15,6 +15,14 @@ let Errors = {
|
|||
'You may not perform this action while in Read-Only mode.',
|
||||
400
|
||||
],
|
||||
teamBanned: [
|
||||
'This team is banned and cannot be interacted or viewed.',
|
||||
400
|
||||
],
|
||||
itemUnavailable: [
|
||||
'This item is currently unavailable at this time.',
|
||||
400
|
||||
],
|
||||
LoginRequired: [
|
||||
'Due to the nature of this action, a login is required.',
|
||||
401
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
'use strict';
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('transactions', {
|
||||
id: {
|
||||
type: Sequelize.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
itemCategoryId: {
|
||||
type: Sequelize.BIGINT,
|
||||
defaultValue: 1,
|
||||
default: 1,
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
text: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: false,
|
||||
},
|
||||
UserId: {
|
||||
type: Sequelize.BIGINT,
|
||||
},
|
||||
boughtFromId: {
|
||||
type: Sequelize.TEXT,
|
||||
},
|
||||
limited: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
defaultValue: 0,
|
||||
default: 0
|
||||
},
|
||||
priceOfPurchase: {
|
||||
type: Sequelize.BIGINT
|
||||
},
|
||||
team: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
default: false
|
||||
},
|
||||
teamId: {
|
||||
type: Sequelize.BIGINT,
|
||||
default: 0
|
||||
},
|
||||
itemId: {
|
||||
type: Sequelize.BIGINT
|
||||
}
|
||||
});
|
||||
},
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('transactions');
|
||||
}
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
up(queryInterface, Sequelize) {
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'transactions',
|
||||
'ipId',
|
||||
{
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
up(queryInterface, Sequelize) {
|
||||
return Promise.all([
|
||||
queryInterface.addColumn(
|
||||
'items',
|
||||
'deleted',
|
||||
{
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
default: false,
|
||||
defaultValue: false
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
let bcrypt = require('bcryptjs')
|
||||
let randomColor = require('randomcolor')
|
||||
var passportLocalSequelize = require('passport-local-sequelize');
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const Errors = require('../lib/errors.js')
|
||||
var crypto = require("crypto");
|
||||
var cryptoRandomString = require("crypto-random-string");
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
let AuditLog = sequelize.define('AuditLog', {
|
||||
updatedAt: DataTypes.DATE,
|
||||
createdAt: DataTypes.DATE,
|
||||
action: DataTypes.TEXT
|
||||
})
|
||||
|
||||
return AuditLog
|
||||
}
|
|
@ -58,9 +58,11 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
}
|
||||
}, {
|
||||
associate (models) {
|
||||
Inventory.belongsTo(models.User)
|
||||
Inventory.belongsTo(models.Item, { as: 'User'})
|
||||
classMethods: {
|
||||
associate(models) {
|
||||
Inventory.belongsTo(models.User, {through: 'User'})
|
||||
Inventory.belongsTo(models.Item)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -122,6 +122,11 @@ module.exports = (sequelize, DataTypes) => {
|
|||
}
|
||||
}
|
||||
},
|
||||
deleted: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
default: false,
|
||||
defaultValue: false
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
default: "No Marketplace item description provided"
|
||||
|
@ -134,28 +139,6 @@ module.exports = (sequelize, DataTypes) => {
|
|||
Item.belongsTo(models.ItemCategory)
|
||||
Item.belongsTo(models.User, {through: 'User'})
|
||||
},
|
||||
includeOptions (from, limit) {
|
||||
let models = sequelize.models
|
||||
let options = models.Post.includeOptions()
|
||||
|
||||
return [{
|
||||
model: models.Post,
|
||||
include: options,
|
||||
limit,
|
||||
where: { postNumber: { $gte: from } },
|
||||
order: [['id', 'ASC']]
|
||||
}]
|
||||
},
|
||||
includeWallOptions (from, limit) {
|
||||
let models = sequelize.models
|
||||
|
||||
return [{
|
||||
model: models.userWall,
|
||||
limit,
|
||||
where: { postNumber: { $gte: from } },
|
||||
order: [['id', 'ASC']]
|
||||
}]
|
||||
},
|
||||
async canBeUser (passkey) {
|
||||
let { User, PassKey } = sequelize.models
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
let randomColor = require('randomcolor')
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
let Transaction = sequelize.define('Transaction', {
|
||||
id: {
|
||||
type: DataTypes.BIGINT,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
itemCategoryId: {
|
||||
type: DataTypes.BIGINT,
|
||||
defaultValue: 1,
|
||||
default: 1,
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: DataTypes.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: DataTypes.DATE
|
||||
},
|
||||
text: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
},
|
||||
UserId: {
|
||||
type: DataTypes.BIGINT,
|
||||
},
|
||||
boughtFromId: {
|
||||
type: DataTypes.TEXT,
|
||||
},
|
||||
limited: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: 0,
|
||||
default: 0
|
||||
},
|
||||
priceOfPurchase: {
|
||||
type: DataTypes.BIGINT
|
||||
},
|
||||
team: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
default: false
|
||||
},
|
||||
teamId: {
|
||||
type: DataTypes.BIGINT,
|
||||
default: 0
|
||||
},
|
||||
itemId: {
|
||||
type: DataTypes.BIGINT
|
||||
}
|
||||
})
|
||||
|
||||
return Transaction
|
||||
}
|
|
@ -429,7 +429,9 @@ module.exports = (sequelize, DataTypes) => {
|
|||
User.hasMany(models.Thread)
|
||||
User.hasMany(models.userWall)
|
||||
User.hasMany(models.Inventory)
|
||||
User.belongsToMany(models.Item, { through: 'UserId'})
|
||||
User.hasMany(models.Transaction)
|
||||
User.hasMany(models.AuditLog)
|
||||
User.hasMany(models.Item)
|
||||
User.belongsToMany(models.Conversation, { through: models.UserConversation })
|
||||
User.belongsToMany(models.Ip, { through: 'UserIp' })
|
||||
},
|
||||
|
@ -445,6 +447,16 @@ module.exports = (sequelize, DataTypes) => {
|
|||
order: [['id', 'ASC']]
|
||||
}]
|
||||
},
|
||||
includeMarketplaceOptions (from, limit) {
|
||||
let models = sequelize.models
|
||||
|
||||
return [{
|
||||
model: models.Item,
|
||||
limit,
|
||||
where: { postNumber: { $gte: from } },
|
||||
order: [['id', 'ASC']]
|
||||
}]
|
||||
},
|
||||
includeWallOptions (from, limit) {
|
||||
let models = sequelize.models
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
|
|||
const Errors = require('../lib/errors.js')
|
||||
var format = require('date-format');
|
||||
let {
|
||||
User, Post, ProfilePicture, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
User, AuditLog, Team, Item, Post, ProfilePicture, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
} = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
|
||||
|
@ -40,21 +40,25 @@ router.put('/user/scrub', auth, async(req, res, next) => {
|
|||
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.autosan.body.username === "usernamescram") {
|
||||
} 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
|
||||
|
@ -62,6 +66,7 @@ router.put('/user/scrub', auth, async(req, res, next) => {
|
|||
})
|
||||
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) }
|
||||
|
@ -83,13 +88,17 @@ router.put('/user/modify', auth, async(req, res, next) => {
|
|||
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,
|
||||
|
@ -104,6 +113,7 @@ router.put('/user/modify', auth, async(req, res, next) => {
|
|||
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,
|
||||
|
@ -118,6 +128,7 @@ router.put('/user/modify', auth, async(req, res, next) => {
|
|||
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
|
||||
}
|
||||
|
@ -136,11 +147,90 @@ router.get('/privileges', auth, async(req, res, next) => {
|
|||
} 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}})
|
||||
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});
|
||||
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)
|
||||
|
||||
throw Errors.featureDisabled
|
||||
let logs = await AuditLog.findAll()
|
||||
if(!logs) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
res.json(logs)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
module.exports = router;
|
||||
|
|
|
@ -4,9 +4,11 @@ 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 {
|
||||
|
|
|
@ -2,7 +2,7 @@ let express = require('express')
|
|||
let router = express.Router()
|
||||
const auth = require('../lib/auth')
|
||||
|
||||
let { User, Ban, Sequelize } = require('../models')
|
||||
let { User, Ban, AuditLog, Sequelize } = require('../models')
|
||||
const Errors = require('../lib/errors')
|
||||
|
||||
router.all('*', auth, async(req, res, next) => {
|
||||
|
@ -28,6 +28,8 @@ router.post('/', auth, async(req, res, next) => {
|
|||
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,
|
||||
|
@ -64,6 +66,7 @@ router.delete('/:ban_id', auth, async(req, res, next) => {
|
|||
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 })
|
||||
|
|
|
@ -8,62 +8,16 @@ let { Ban, Item, Inventory, ItemCategory, User, sequelize, Sequelize } = require
|
|||
|
||||
router.get('/', auth, 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 }})
|
||||
let queryObj = {
|
||||
where: {UserId: req.userData.UserId},
|
||||
include: { model: Item, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } }
|
||||
}
|
||||
|
||||
function threadInclude(order) {
|
||||
let options = {
|
||||
model: Inventory,
|
||||
order: [['id', 'DESC']],
|
||||
limit,
|
||||
where: {},
|
||||
include: [
|
||||
User,
|
||||
{ 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]
|
||||
let transaction = await Inventory.findAll(queryObj)
|
||||
if(!transaction) {
|
||||
res.status(200)
|
||||
res.json({success: false})
|
||||
}
|
||||
|
||||
if(req.userData.id === 'ALL') {
|
||||
threads = await Thread.findAll( threadInclude('ASC')[0] )
|
||||
threadsLatestPost = await Thread.findAll( threadInclude('DESC')[0] )
|
||||
} else {
|
||||
threads = await User.findOne({
|
||||
where: { username: req.userData.username },
|
||||
include: threadInclude('ASC')
|
||||
})
|
||||
}
|
||||
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 = {}
|
||||
}
|
||||
|
||||
res.json(resThreads)
|
||||
|
||||
res.json(transaction)
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
module.exports = router
|
||||
|
|
|
@ -4,7 +4,7 @@ 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')
|
||||
let { Ban, Item, Transaction, Inventory, ItemCategory, User, Ip, sequelize, Sequelize } = require('../models')
|
||||
|
||||
|
||||
router.get('/', async(req, res) => {
|
||||
|
@ -95,8 +95,18 @@ router.get('/view/:id', async(req, res, next) => {
|
|||
}
|
||||
let marketplace = await Item.findOne(queryObj)
|
||||
if (!marketplace) throw Errors.invalidItem
|
||||
|
||||
res.json(marketplace.toJSON())
|
||||
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(marketplace.toJSON())
|
||||
}
|
||||
if(!marketplace.deleted) {
|
||||
res.json(marketplace.toJSON())
|
||||
}
|
||||
|
||||
|
||||
} catch (err) { next(err) }
|
||||
|
@ -108,12 +118,12 @@ router.get('/purchase/:id', auth, async(req, res, next) => {
|
|||
let queryObj = {
|
||||
where: {id: req.params.id},
|
||||
}
|
||||
let queryObj3 = {
|
||||
where: {UserId: req.userData.UserId, ItemId: 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)
|
||||
|
@ -127,6 +137,12 @@ router.get('/purchase/:id', auth, async(req, res, next) => {
|
|||
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
|
||||
}
|
||||
|
@ -136,18 +152,16 @@ router.get('/purchase/:id', auth, async(req, res, next) => {
|
|||
if(marketplace.saleEnabled) {
|
||||
const UserId = user.id
|
||||
const priceTotal = marketplace.salePrice
|
||||
console.log(priceTotal)
|
||||
await user.removeKoins(priceTotal)
|
||||
console.log(user.id)
|
||||
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 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
|
||||
console.log(priceTotal)
|
||||
await user.removeKoins(priceTotal)
|
||||
console.log(user.id)
|
||||
await Transaction.create({UserId: UserId, priceOfPurchase: priceTotal, text: user.username + ' purchased ' + marketplace.name + ' (' + marketplace.id + ') from the Marketplace for ' + priceTotal, itemId: marketplace.id, limited: marketplace.limited, ipId: Ip.createIfNotExists(req.ip, user)})
|
||||
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})
|
||||
|
|
114
routes/team.js
114
routes/team.js
|
@ -41,7 +41,7 @@ var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
|
|||
const Errors = require('../lib/errors.js')
|
||||
var format = require('date-format');
|
||||
let {
|
||||
User, Post, teamWall, teamPicture, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize, Team, TeamMembers, TeamRoles
|
||||
User, Post, teamWall, 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');
|
||||
|
@ -68,51 +68,62 @@ function setUserSession(req, res, username, UserId, admin) {
|
|||
router.post('/create', emailLimiter, auth, async(req, res, next) => {
|
||||
try {
|
||||
await Ban.isIpBanned(req.ip)
|
||||
|
||||
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.id
|
||||
let user = await User.findOne({where: {
|
||||
username: req.userData.username
|
||||
}})
|
||||
if(user.koins >= 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)})
|
||||
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.id
|
||||
}
|
||||
let team = await Team.create(userParams)
|
||||
let teamInfo = team.toJSON()
|
||||
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 teamMemberOwner = {
|
||||
userId: req.userData.id,
|
||||
teamId: teamInfo.id,
|
||||
roles: memberRoleInfo.id + ", " + adminRoleInfo.id
|
||||
}
|
||||
await TeamMembers.create(teamMemberOwner)
|
||||
res.json(team.toJSON())
|
||||
} else {
|
||||
throw Errors.insufficientKoins
|
||||
}
|
||||
let team = await Team.create(userParams)
|
||||
let teamInfo = team.toJSON()
|
||||
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 teamMemberOwner = {
|
||||
userId: req.userData.id,
|
||||
teamId: teamInfo.id,
|
||||
roles: memberRoleInfo.id + ", " + adminRoleInfo.id
|
||||
}
|
||||
await TeamMembers.create(teamMemberOwner)
|
||||
res.json(team.toJSON())
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
router.get('/view/:username', async(req, res, next) => {
|
||||
try {
|
||||
let queryObj = {
|
||||
attributes: {exclude: ['banReason']},
|
||||
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) {
|
||||
|
@ -130,17 +141,23 @@ router.get('/view/:username', async(req, res, next) => {
|
|||
|
||||
let user = await Team.findOne(queryObj)
|
||||
if (!user) throw Errors.accountDoesNotExist
|
||||
if(user.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
if (user.userWallOptOut) {
|
||||
throw Errors.userWallOptOut
|
||||
}
|
||||
|
||||
let meta = await user.getMeta(limit)
|
||||
res.json(Object.assign(user.toJSON(limit), {meta}))
|
||||
} else {
|
||||
let team = await Team.findOne(queryObj)
|
||||
if (!team) throw Errors.accountDoesNotExist
|
||||
|
||||
res.json(team.toJSON())
|
||||
if(!team.banned) {
|
||||
res.json(team.toJSON())
|
||||
} else {
|
||||
let team = await Team.findOne(queryObjBanned)
|
||||
res.json(team.toJSON())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -165,13 +182,16 @@ router.get('/view/:username/picture', async (req, res, next) => {
|
|||
if(!picture) {
|
||||
res.status(404)
|
||||
res.json({picture: "https://cdn.kaverti.com/teams/unknown-light.png"})
|
||||
} else {
|
||||
} 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) }
|
||||
})
|
||||
|
@ -183,6 +203,9 @@ router.get('/view/:username/members', async(req, res, next) => {
|
|||
username: req.params.username
|
||||
}
|
||||
})
|
||||
if(user.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
if(user) {
|
||||
let team = await TeamMembers.findAll({
|
||||
order: [['id', 'DESC']],
|
||||
|
@ -211,7 +234,7 @@ router.get('/', async(req, res, next) => {
|
|||
username: 'X.username',
|
||||
};
|
||||
let offset = Number.isInteger(+req.query.offset) ? +req.query.offset : 0;
|
||||
let havingClause = '';
|
||||
let havingClause = 'Having Teams.banned = false';
|
||||
|
||||
if(req.query.search) {
|
||||
//I.e. if there is not already a HAVING clause
|
||||
|
@ -251,6 +274,9 @@ router.put('/join/:username', auth, async(req, res, next) => {
|
|||
})
|
||||
console.log(team.id)
|
||||
if(team) {
|
||||
if(team.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
let role = await TeamRoles.findOne({
|
||||
where: {teamId: team.id, name: "Members"}
|
||||
})
|
||||
|
|
|
@ -61,6 +61,10 @@ router.post('/post', postLimiter, auth, async(req, res, next) => {
|
|||
//Will throw an error if banned
|
||||
await Ban.ReadOnlyMode(req.userData.username)
|
||||
|
||||
if(getWallUser.banned) {
|
||||
throw Errors.teamBanned
|
||||
}
|
||||
|
||||
if (req.body.mentions) {
|
||||
uniqueMentions = Notification.filterMentions(req.body.mentions)
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -41,7 +41,7 @@ var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
|
|||
const Errors = require('../lib/errors.js')
|
||||
var format = require('date-format');
|
||||
let {
|
||||
User, Post, ProfilePicture, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
User, Post, ProfilePicture, Item, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
} = require('../models')
|
||||
let pagination = require('../lib/pagination.js')
|
||||
const sgMail = require('@sendgrid/mail');
|
||||
|
@ -147,9 +147,9 @@ router.get('/:username', async(req, res, next) => {
|
|||
where: {username: req.params.username}
|
||||
}
|
||||
|
||||
if(req.query.posts) {
|
||||
if (req.query.posts) {
|
||||
|
||||
let { from, limit } = pagination.getPaginationProps(req.query, true)
|
||||
let {from, limit} = pagination.getPaginationProps(req.query, true)
|
||||
|
||||
let postInclude = {
|
||||
model: Post,
|
||||
|
@ -157,40 +157,40 @@ router.get('/:username', async(req, res, next) => {
|
|||
limit,
|
||||
order: [['id', 'DESC']]
|
||||
}
|
||||
if(from !== null) {
|
||||
postInclude.where = { id: { $lte: from } }
|
||||
if (from !== null) {
|
||||
postInclude.where = {id: {$lte: from}}
|
||||
}
|
||||
queryObj.include = [postInclude]
|
||||
|
||||
let user = await User.findOne(queryObj)
|
||||
if(!user) throw Errors.accountDoesNotExist
|
||||
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)
|
||||
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 } }
|
||||
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) {
|
||||
if (!user) throw Errors.accountDoesNotExist
|
||||
if (user.userWallOptOut) {
|
||||
throw Errors.userWallOptOut
|
||||
}
|
||||
|
||||
let meta = await user.getMeta(limit)
|
||||
|
||||
res.json(Object.assign( user.toJSON(limit), { meta } ))
|
||||
} else if(req.query.threads) {
|
||||
res.json(Object.assign(user.toJSON(limit), {meta}))
|
||||
} else if (req.query.threads) {
|
||||
let queryString = ''
|
||||
|
||||
Object.keys(req.query).forEach(query => {
|
||||
|
@ -198,6 +198,32 @@ router.get('/:username', async(req, res, next) => {
|
|||
})
|
||||
|
||||
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 {
|
||||
let user = await User.findOne(queryObj)
|
||||
if(!user) throw Errors.accountDoesNotExist
|
||||
|
@ -295,7 +321,7 @@ router.get('/', async(req, res, next) => {
|
|||
} else if(req.query.role === 'user') {
|
||||
havingClause = 'HAVING Users.admin = false';
|
||||
} else {
|
||||
havingClause = '';
|
||||
havingClause = 'Having Users.hidden = false';
|
||||
}
|
||||
if(req.query.search) {
|
||||
//I.e. if there is not already a HAVING clause
|
||||
|
|
|
@ -12,7 +12,7 @@ var format = require('date-format');
|
|||
var speakeasy = require('speakeasy');
|
||||
var secret = speakeasy.generateSecret();
|
||||
let {
|
||||
User, Post, ProfilePicture, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
|
||||
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");
|
||||
|
@ -667,6 +667,7 @@ router.put('/preferences', auth, async(req, res, next) => {
|
|||
username: req.userData.username
|
||||
}})
|
||||
if(user.koins >= 200) {
|
||||
await Transaction.create({UserId: user.id, priceOfPurchase: 200, text: user.username + ' purchased a Kaverti username change for 200 koins', limited: false, ipId: Ip.createIfNotExists(req.ip, user)})
|
||||
await user.invalidateJWT()
|
||||
await user.updateUsername(req.body.username, req.body.password)
|
||||
} else {
|
||||
|
|
|
@ -115,6 +115,7 @@ 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(require('./lib/errorHandler'))
|
||||
app.use(profanity.init);
|
||||
app.set('trust proxy', true)
|
||||
|
|
Loading…
Reference in New Issue