This repository has been archived on 2022-05-17. You can view files and clone it, but cannot push or open issues or pull requests.
brainlet/app/resources/js/app.js

452 lines
16 KiB
JavaScript
Raw Normal View History

2020-10-05 20:36:03 +03:00
Vue.use(VueMaterial.default);
const getCreatePostError = (json) => {
switch (json.message) {
case 'ERROR_REQUEST_INVALID_DATA': {
switch (json.errors[0].param) {
case 'title': {
return 'Invalid title. Must be between 3 and 32 characters.';
}
case 'body': {
return 'Invalid content. Must be between 3 and 1000 characters';
}
case 'category': {
return 'Invalid category. Something went wrong.';
}
default: {
return 'Invalid value sent to server. Something went wrong.';
}
}
}
case 'ERROR_CATEGORY_NOT_FOUND': {
return 'The category you tried to post to no longer exists.';
}
case 'ERROR_ACCESS_DENIED': {
return 'You are not allowed to perform this action.'
}
default: {
return 'Unknown error. Something went wrong.';
}
}
}
const getCreateCategoryError = (json) => {
switch (json.message) {
case 'ERROR_REQUEST_INVALID_DATA': {
switch (json.errors[0].param) {
case 'title': {
return 'Invalid title. Title must be between 3 and 32 characters.';
}
}
}
case 'ERROR_ACCESS_DENIED': {
return 'You are not allowed to perform this action.'
}
default: {
return 'Unknown error. Something went wrong.';
}
}
}
class GatewayConnection {
constructor() {
this.isConnected = false;
this.socket = null;
// TODO: set up proper event listening and such, not this dumb crap
this.onDisconnect = () => {}
this.onConnect = () => {}
}
}
GatewayConnection.prototype.connect = function(token) {
console.log('[*] [gateway] [handshake] Trying to connect to gateway');
this.socket = io('/gateway', {
query: {
token
},
transports: ['websocket']
});
this.socket.on('hello', () => {
console.log('[*] [gateway] [handshake] Got hello from server, sending yoo...');
this.socket.emit('yoo');
2020-11-16 22:08:23 +02:00
this.isConnected = true;
this.onConnect('CONNECT_RECEIVED_HELLO');
});
this.socket.on('error', (e) => {
console.log('[E] [gateway] Gateway error', e);
this.isConnected = false;
this.onDisconnect('DISCONNECT_ERR', e);
});
this.socket.on('disconnectNotification', (e) => {
console.log('[E] [gateway] Received disconnect notfication', e);
this.isConnected = false;
this.onDisconnect('DISCONNECT_NOTIF', e);
});
this.socket.on('disconnect', (e) => {
console.log('[E] [gateway] Disconnected from gateway: ', e);
this.isConnected = false;
this.onDisconnect('DISCONNECT', e);
});
};
2020-11-16 22:08:23 +02:00
GatewayConnection.prototype.sendMessage = function(categoryId, content) {
if (!this.isConnected) return;
this.socket.emit('message', { categoryId, content });
};
GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) {
if (!this.isConnected) return;
this.socket.emit('subscribe', [categoryId]);
};
2020-10-05 20:36:03 +03:00
const app = new Vue({
el: '#app',
data: {
showSnackbarNotification: false,
snackbarNotification: '',
snackbarNotificationDuration: 999999,
snackbarButtonText: 'Ok',
loggedInUser: {},
showApp: false,
menuVisible: false,
selection: {
category: {
title: '',
browsing: false,
_id: undefined,
isCategory: false
},
posts: []
},
cardButtons: [],
dialog: {
show: {
createPost: false,
createCategory: false
},
text: {
createPost: {
title: '',
body: ''
},
createCategory: {
title: ''
}
}
},
viewingProfile: {
show: false,
_id: '',
username: '',
role: ''
},
2020-11-16 22:18:39 +02:00
gateway: new GatewayConnection(),
viewingChat: {
show: false,
categoryId: 'X'
},
messages: {
'X': [ { username: 'NONEXISTENT_TEST_ACCOUNT', content: 'TEST MSG' } ]
}
2020-10-05 20:36:03 +03:00
},
mounted: async function() {
const res = await fetch(`${window.location.origin}/api/v1/users/current/info`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
if (json.user.permissionLevel >= 1) {
this.loggedInUser = json.user;
this.showApp = true;
this.performGatewayConnection();
2020-10-05 20:36:03 +03:00
this.browseCategories();
} else {
this.showApp = false;
this.snackbarEditButton('Manage', () => {
window.location.href = `${window.location.origin}/auth.html`;
this.resetSnackbarButton();
this.showSnackbarNotification = false;
});
this.notification('Your account does not have the required permissions to enter this page');
}
} else {
this.showApp = false;
this.snackbarEditButton('Manage', () => {
window.location.href = `${window.location.origin}/auth.html`;
this.resetSnackbarButton();
this.showSnackbarNotification = false;
});
this.notification('You are not logged in or your session is invalid');
}
},
methods: {
navigateToAccountManager() {
window.location.href = `${window.location.origin}/auth.html`;
},
snackbarButtonAction: function() {
this.showSnackbarNotification = false;
},
snackbarButtonClick: function() {
this.snackbarButtonAction();
},
snackbarEditButton: function(buttonText="Ok", action) {
this.snackbarButtonText = buttonText;
this.snackbarButtonAction = action;
},
resetSnackbarButton: function() {
this.snackbarButtonText = 'Ok';
this.snackbarButtonAction = () => {
this.showSnackbarNotification = false;
};
},
notification: function(text) {
this.snackbarNotification = text;
this.showSnackbarNotification = true;
},
performGatewayConnection: function() {
// TODO: again, the thing im doing with the token is not very secure, since its being sent by the current user info endpoint and is also being send through query parameters
this.gateway.onDisconnect = (e) => {
this.resetSnackbarButton();
this.notification('ERROR: You have been disconnected from the gateway. Realtime features such as chat will not work and unexpected errors may occur.');
};
2020-11-16 22:08:23 +02:00
this.gateway.onConnect = () => {
this.gateway.socket.on('message', ({ username, categoryId, content }) => {
2020-11-16 22:18:39 +02:00
this.showMessageNotification(categoryId, username, content);
2020-11-16 22:08:23 +02:00
})
};
this.gateway.connect(this.loggedInUser.token);
},
2020-11-16 22:18:39 +02:00
showMessageNotification: async function(categoryId, username, content) {
2020-11-16 22:08:23 +02:00
this.resetSnackbarButton();
this.notification(`${username}: ${content}`);
2020-11-16 22:18:39 +02:00
if (!this.messages[categoryId]) this.messages[categoryId] = []
this.messages[categoryId].push({ username, content });
2020-11-16 22:08:23 +02:00
},
2020-10-05 20:36:03 +03:00
button: function(text, click) {
this.cardButtons.push({ text, click });
},
refresh: function() {
if (this.selection.category.title === 'categories' && this.selection.category.isCategory === false) {
this.browseCategories();
} else {
this.browse(this.selection.category);
}
},
viewProfile: async function(id) {
// TODO: this just returns the username for now
const res = await fetch(`${window.location.origin}/api/v1/users/user/${id}/info`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.viewingProfile.username = json.user.username;
this.viewingProfile._id = json.user._id;
this.viewingProfile.role = json.user.role;
this.viewingProfile.show = true;
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch user data');
}
},
stopBrowsing: function() {
this.selection.category = {
title: '',
browsing: false,
_id: undefined,
isCategory: false
};
this.selection.posts = [];
this.cardButtons = [];
},
showCreatePostDialog: function() {
if (!this.selection.category.isCategory) {
this.resetSnackbarButton();
this.notification('You are not in a category');
return;
}
this.dialog.show.createPost = true;
},
createPost: async function() {
if (!this.selection.category.isCategory) {
this.resetSnackbarButton();
this.notification('You are not in a category');
return;
}
const category = this.selection.category;
const input = this.dialog.text.createPost;
const res = await fetch(`${window.location.origin}/api/v1/content/post/create`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({
category: category._id,
title: input.title,
body: input.body
})
});
if (res.status !== 200) {
if (res.status === 401 || res.status === 403) {
this.resetSnackbarButton();
this.notification('You are not allowed to do that');
return;
}
if (res.status === 429) {
this.resetSnackbarButton();
this.notification('Chill! You are posting too much!');
return;
}
const json = await res.json();
this.resetSnackbarButton();
this.notification(getCreatePostError(json));
return;
} else {
this.resetSnackbarButton();
this.notification('Successfully created post');
this.dialog.show.createPost = false;
this.browse(this.selection.category);
return;
}
},
createCategory: async function() {
const input = this.dialog.text.createCategory;
const res = await fetch(`${window.location.origin}/api/v1/content/category/create`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({
title: input.title
})
});
if (res.status !== 200) {
if (res.status === 401 || res.status === 403) {
this.resetSnackbarButton();
this.notification('You are not allowed to do that');
return;
}
if (res.status === 429) {
this.resetSnackbarButton();
this.notification('Chill! You are posting too much!');
return;
}
const json = await res.json();
this.resetSnackbarButton();
this.notification(getCreateCategoryError(json));
return;
} else {
this.resetSnackbarButton();
this.notification('Successfully created category');
this.dialog.show.createCategory = false;
this.browseCategories();
return;
}
},
browse: async function(category) {
const { _id, title } = category;
const res = await fetch(`${window.location.origin}/api/v1/content/category/${_id}/info`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.selection.category.title = title;
this.selection.category._id = _id;
this.selection.category.browsing = true;
this.selection.category.isCategory = true;
this.selection.posts = [];
this.cardButtons = [];
for (let i = 0; i < json.category.posts.length; i++) {
const v = json.category.posts[i];
this.selection.posts.push({ title: v.title, body: v.body, _id: v._id, creator: v.creator });
}
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch category');
}
},
openChatForCategory: async function(categoryId) {
2020-10-05 20:36:03 +03:00
},
browseCategories: async function() {
const res = await fetch(`${window.location.origin}/api/v1/content/category/list?count=50`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include'
});
if (res.status === 200) {
const json = await res.json();
this.selection.category.title = 'categories';
this.selection.category.browsing = true;
this.selection.category.isCategory = false;
this.selection.posts = [];
this.cardButtons = [];
this.button('Chat', (post) => {
2020-11-16 22:08:23 +02:00
if (post._id) {
this.gateway.subscribeToCategoryChat(post._id);
this.gateway.sendMessage(post._id, 'yoooo');
}
});
2020-10-05 20:36:03 +03:00
this.button('View', (post) => {
this.browse(post);
});
for (let i = 0; i < json.categories.length; i++) {
const v = json.categories[i];
this.selection.posts.push({ title: v.title, body: '', _id: v._id, creator: v.creator });
}
} else {
this.resetSnackbarButton();
this.notification('Failed to fetch category list');
}
}
}
});