integrate the new gateway, more or less
This commit is contained in:
parent
e3bb4ce125
commit
6111d1dfa3
13 changed files with 370 additions and 679 deletions
763
bfrontend/package-lock.json
generated
763
bfrontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -3,21 +3,20 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@testing-library/jest-dom": "^5.11.6",
|
"@testing-library/jest-dom": "^5.11.10",
|
||||||
"@testing-library/react": "^11.2.2",
|
"@testing-library/react": "^11.2.5",
|
||||||
"@testing-library/user-event": "^12.6.0",
|
"@testing-library/user-event": "^13.0.16",
|
||||||
"nord": "^0.2.1",
|
"nord": "^0.2.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.2",
|
||||||
"react-content-loader": "^5.1.4",
|
"react-content-loader": "^6.0.2",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.2",
|
||||||
"react-media-hook": "^0.4.9",
|
"react-media-hook": "^0.4.9",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.3",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "4.0.1",
|
"react-scripts": "4.0.3",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"sass": "^1.32.0",
|
"sass": "^1.32.8",
|
||||||
"socket.io-client": "^3.0.5",
|
"web-vitals": "^1.1.1"
|
||||||
"web-vitals": "^0.2.4"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import config from '../Config';
|
import config from '../Config';
|
||||||
|
import token from "./TokenManager";
|
||||||
|
|
||||||
async function APIRequest(endpoint, options) {
|
async function APIRequest(endpoint, options) {
|
||||||
let res;
|
let res;
|
||||||
|
@ -9,11 +10,6 @@ async function APIRequest(endpoint, options) {
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
if (!options.headers) options.headers = {};
|
if (!options.headers) options.headers = {};
|
||||||
|
|
||||||
options = {
|
|
||||||
credentials: 'include',
|
|
||||||
...options
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res = await fetch(`${config.apiUrl}${endpoint}`, options);
|
res = await fetch(`${config.apiUrl}${endpoint}`, options);
|
||||||
json = await res.json();
|
json = await res.json();
|
||||||
|
@ -41,12 +37,15 @@ APIRequest.authenticated = async function(endpoint, options) {
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
if (!options.headers) options.headers = {};
|
if (!options.headers) options.headers = {};
|
||||||
|
|
||||||
options = {
|
console.log(options);
|
||||||
credentials: 'include',
|
|
||||||
...options
|
options.headers = {
|
||||||
|
"Authorization": token.getToken(),
|
||||||
|
...options.headers
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(options);
|
||||||
res = await fetch(`${config.apiUrl}${endpoint}`, options);
|
res = await fetch(`${config.apiUrl}${endpoint}`, options);
|
||||||
json = await res.json();
|
json = await res.json();
|
||||||
isOK = true;
|
isOK = true;
|
||||||
|
|
|
@ -1,34 +1,15 @@
|
||||||
import APIRequest from './APIRequest';
|
|
||||||
import gatewayConnection from '../API/Gateway/globalGatewayConnection';
|
|
||||||
import Logger from '../Util/Logger';
|
import Logger from '../Util/Logger';
|
||||||
|
import gateway from "./Gateway/globalGatewayConnection";
|
||||||
|
|
||||||
const { log: authLog, error: authError } = Logger([ 'Authenticator' ]);
|
const { log: authLog } = Logger([ 'Authenticator' ]);
|
||||||
|
|
||||||
const Authenticator = {
|
const Authenticator = {
|
||||||
getToken: function() {
|
getToken: function() {
|
||||||
return localStorage.getItem("token");
|
return localStorage.getItem("token");
|
||||||
},
|
},
|
||||||
getLoggedInUserFromCookie: async function() {
|
login: async function() {
|
||||||
authLog('Fetching current logged in user status...');
|
authLog('Logging in through gateway...');
|
||||||
const { json, isOK, err } = await APIRequest.authenticated('/api/v1/users/current/info');
|
return gateway.connect();
|
||||||
if (!isOK && err) {
|
|
||||||
authLog('Exception while fetching current logged in user status', err);
|
|
||||||
throw new Error(err);
|
|
||||||
}
|
|
||||||
if (!isOK && !err) {
|
|
||||||
authError('User is not authenticated');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (!json || !json.user) {
|
|
||||||
authError('User is not authenticated');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
authLog(`Logged in as "${json.user.username || '[undefined username]'}"`);
|
|
||||||
|
|
||||||
// NOTE(hippoz): this function has a stupid side-effect but this will have to do for now...
|
|
||||||
gatewayConnection.connect(json.user.token);
|
|
||||||
return json.user;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logger from "../../Util/Logger";
|
import logger from "../../Util/Logger";
|
||||||
|
|
||||||
const logGateway = logger([ "Gateway" ]);
|
const { log: logGateway } = logger([ "Gateway" ]);
|
||||||
|
|
||||||
const opcodes = {
|
const opcodes = {
|
||||||
0: { name: "HELLO", data: "JSON" },
|
0: { name: "HELLO", data: "JSON" },
|
||||||
|
@ -46,7 +46,13 @@ const getOpcodeByName = (name) => {
|
||||||
|
|
||||||
class GatewayConnection {
|
class GatewayConnection {
|
||||||
constructor(token, gatewayUrl) {
|
constructor(token, gatewayUrl) {
|
||||||
this.ws = new WebSocket(gatewayUrl);
|
this.token = token;
|
||||||
|
this.gatewayUrl = gatewayUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GatewayConnection.prototype.connect = function() {
|
||||||
|
this.ws = new WebSocket(this.gatewayUrl);
|
||||||
|
|
||||||
this.handshakeCompleted = false;
|
this.handshakeCompleted = false;
|
||||||
this.sessionInformation = null;
|
this.sessionInformation = null;
|
||||||
|
@ -67,7 +73,7 @@ class GatewayConnection {
|
||||||
// Got HELLO from server, send YOO as soon as possible
|
// Got HELLO from server, send YOO as soon as possible
|
||||||
logGateway("Got HELLO", packet.data);
|
logGateway("Got HELLO", packet.data);
|
||||||
logGateway("Sending YOO");
|
logGateway("Sending YOO");
|
||||||
this.ws.send(this.packet("YOO", { token }));
|
this.ws.send(this.packet("YOO", { token: this.token }));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "YOO_ACK": {
|
case "YOO_ACK": {
|
||||||
|
@ -94,8 +100,7 @@ class GatewayConnection {
|
||||||
return console.error("err: gateway:", e);
|
return console.error("err: gateway:", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
GatewayConnection.prototype.sendMessage = function(content, channelId) {
|
GatewayConnection.prototype.sendMessage = function(content, channelId) {
|
||||||
if (!this.sessionInformation) throw new Error("gateway: tried to send message before handshake completion");
|
if (!this.sessionInformation) throw new Error("gateway: tried to send message before handshake completion");
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import GatewayConnection from './GatewayConnection';
|
import GatewayConnection from './GatewayConnection';
|
||||||
import config from '../../Config';
|
import config from '../../Config';
|
||||||
import Authenticator from '../Authenticator';
|
import token from '../TokenManager';
|
||||||
import store from '../../Global/store';
|
import store from '../../Global/store';
|
||||||
console.log(Authenticator);
|
|
||||||
|
|
||||||
const globalGatewayConnection = new GatewayConnection(Authenticator.getToken(), config.gatewayUrl);
|
const globalGatewayConnection = new GatewayConnection(token.getToken(), config.gatewayUrl);
|
||||||
|
|
||||||
globalGatewayConnection.onopen = (sessionData) => {
|
globalGatewayConnection.onopen = (sessionData) => {
|
||||||
store.dispatch({ type: 'gateway/connectionstatus', gateway: { isConnected: true } });
|
store.dispatch({ type: 'gateway/connectionstatus', gateway: { isConnected: true } });
|
||||||
store.dispatch({ type: 'authenticator/updatelocaluserobject', user: sessionData.user });
|
store.dispatch({ type: 'authenticator/updatelocaluserobject', user: sessionData.user });
|
||||||
store.dispatch({ type: 'channels/updatechannellist', user: sessionData.channels })
|
store.dispatch({ type: 'channels/updatechannellist', channels: sessionData.channels })
|
||||||
};
|
};
|
||||||
|
|
||||||
globalGatewayConnection.onmessage = (message) => {
|
globalGatewayConnection.onmessage = (message) => {
|
||||||
|
@ -17,6 +16,7 @@ globalGatewayConnection.onmessage = (message) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
globalGatewayConnection.onclose = function() {
|
globalGatewayConnection.onclose = function() {
|
||||||
|
store.dispatch({ type: 'authenticator/updatelocaluserobject', user: undefined });
|
||||||
store.dispatch({ type: 'gateway/connectionstatus', gateway: { isConnected: false } });
|
store.dispatch({ type: 'gateway/connectionstatus', gateway: { isConnected: false } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
6
bfrontend/src/API/TokenManager.js
Normal file
6
bfrontend/src/API/TokenManager.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const Auth = {
|
||||||
|
getToken: () => localStorage.getItem("token"),
|
||||||
|
setToken: (token) => localStorage.setItem("token", token)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Auth;
|
|
@ -1,15 +1,14 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
|
|
||||||
import Notification from '../UI/Notification';
|
import Notification from '../UI/Notification';
|
||||||
import APIRequest from '../../API/APIRequest';
|
import APIRequest from '../../API/APIRequest';
|
||||||
import Authenticator from '../../API/Authenticator';
|
import Authenticator from '../../API/Authenticator';
|
||||||
|
import token from "../../API/TokenManager";
|
||||||
import { getLoginMessageFromError } from '../../Util/Errors'
|
import { getLoginMessageFromError } from '../../Util/Errors'
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const [ usernameInput, setUsernameInput ] = useState();
|
const [ usernameInput, setUsernameInput ] = useState();
|
||||||
const [ passwordInput, setPasswordInput ] = useState();
|
const [ passwordInput, setPasswordInput ] = useState();
|
||||||
|
@ -30,8 +29,7 @@ export default function Login() {
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: usernameInput,
|
username: usernameInput,
|
||||||
password: passwordInput,
|
password: passwordInput
|
||||||
alsoSetCookie: true
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,8 +42,8 @@ export default function Login() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await Authenticator.getLoggedInUserFromCookie();
|
token.setToken(json.token);
|
||||||
dispatch({ type: 'authenticator/updatelocaluserobject', user: res });
|
await Authenticator.login();
|
||||||
|
|
||||||
history.push('/');
|
history.push('/');
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default function ChannelButton({ channel, selected }) {
|
||||||
if (selected) buttonClasses += ' pressed';
|
if (selected) buttonClasses += ' pressed';
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (gatewayConnection.isConnected) {
|
if (gatewayConnection.handshakeCompleted) {
|
||||||
history.push(`/channels/${channel._id}`);
|
history.push(`/channels/${channel._id}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,65 +1,19 @@
|
||||||
import ChannelListLoader from './ChannelListLoader';
|
import ChannelListLoader from './ChannelListLoader';
|
||||||
import ChannelButton from './ChannelButton';
|
import ChannelButton from './ChannelButton';
|
||||||
import APIRequest from '../../API/APIRequest';
|
|
||||||
import { couldNotReach } from '../../Util/Errors';
|
|
||||||
|
|
||||||
import { connect, useDispatch } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import Logger from '../../Util/Logger';
|
|
||||||
import gatewayConnection from '../../API/Gateway/globalGatewayConnection';
|
|
||||||
|
|
||||||
const { log: loaderLog } = Logger([ 'ChannelList', 'Loader' ]);
|
function ChannelList({ selectedChannelId, channels }) {
|
||||||
|
if (!channels) {
|
||||||
function ChannelList({ selectedChannelId, gatewayStatus }) {
|
|
||||||
const [ channelList, setChannelList ] = useState();
|
|
||||||
const [ error, setError ] = useState();
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loaderLog('Loading ChannelList...');
|
|
||||||
APIRequest.authenticated('/api/v1/content/channel/list?count=50')
|
|
||||||
.then(({ isOK, json }) => {
|
|
||||||
if (!isOK) return setError(true);
|
|
||||||
|
|
||||||
loaderLog('Got channel list from server, dispatching...');
|
|
||||||
setChannelList(json.channels || []);
|
|
||||||
dispatch({ type: 'channels/updatechannellist', channels: json.channels });
|
|
||||||
loaderLog('Subscribing to all channels...');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setError(true);
|
|
||||||
});
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!channelList) return;
|
|
||||||
if (!gatewayStatus.isConnected) return;
|
|
||||||
|
|
||||||
// TODO: IMPORTANT: Subscribing to a lot of channels puts strain on the server
|
|
||||||
gatewayConnection.subscribeToChannelChats(channelList.map(channel => channel._id));
|
|
||||||
}, [gatewayStatus, channelList]);
|
|
||||||
|
|
||||||
if (!channelList) {
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
{ couldNotReach }
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ChannelListLoader />
|
<ChannelListLoader />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className="channel-list">
|
<div className="channel-list">
|
||||||
{ channelList.map((channel) => ( <ChannelButton key={ channel._id } channel={ channel } selected={ (channel._id === selectedChannelId) } /> )) }
|
{ channels.map((channel) => ( <ChannelButton key={ channel._id } channel={ channel } selected={ (channel._id === selectedChannelId) } /> )) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +22,7 @@ function ChannelList({ selectedChannelId, gatewayStatus }) {
|
||||||
const stateToProps = (state) => {
|
const stateToProps = (state) => {
|
||||||
return {
|
return {
|
||||||
selectedChannelId: state?.selectedChannelId,
|
selectedChannelId: state?.selectedChannelId,
|
||||||
gatewayStatus: state?.gateway,
|
channels: state?.channels
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ function ChannelView({ channels, messages }) {
|
||||||
|
|
||||||
const handleTextboxKeydown = (e) => {
|
const handleTextboxKeydown = (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
gatewayConnection.sendMessage(id, textInput);
|
gatewayConnection.sendMessage(textInput, id);
|
||||||
textInputRef.current.value = '';
|
textInputRef.current.value = '';
|
||||||
setTextInput('');
|
setTextInput('');
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import Root from '../Home/Root';
|
||||||
import Authenticator from '../../API/Authenticator';
|
import Authenticator from '../../API/Authenticator';
|
||||||
import Notification from '../UI/Notification';
|
import Notification from '../UI/Notification';
|
||||||
import './../../Styles/App.scss';
|
import './../../Styles/App.scss';
|
||||||
import { couldNotReach } from '../../Util/Errors';
|
|
||||||
import ChannelView from '../Channels/ChannelView';
|
import ChannelView from '../Channels/ChannelView';
|
||||||
import Sidebar from '../UI/Sidebar';
|
import Sidebar from '../UI/Sidebar';
|
||||||
|
|
||||||
|
@ -12,20 +11,13 @@ import { useDispatch, connect } from 'react-redux'
|
||||||
import { BrowserRouter, Switch, Route } from 'react-router-dom';
|
import { BrowserRouter, Switch, Route } from 'react-router-dom';
|
||||||
|
|
||||||
function App({ user }) {
|
function App({ user }) {
|
||||||
const [ notificationText, setNotificationText ] = useState('');
|
const [ notificationText ] = useState('');
|
||||||
const [ hasError, setHasError ] = useState(false);
|
const [ hasError ] = useState(false);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Authenticator.getLoggedInUserFromCookie()
|
Authenticator.login();
|
||||||
.then((res) => {
|
|
||||||
dispatch({ type: 'authenticator/updatelocaluserobject', user: res });
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setNotificationText(couldNotReach);
|
|
||||||
setHasError(true);
|
|
||||||
});
|
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
if (user === null && !hasError) {
|
if (user === null && !hasError) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const config = {
|
const config = {
|
||||||
apiUrl: 'http://localhost:3000',
|
apiUrl: 'http://localhost:3000',
|
||||||
gatewayUrl: '', // Leave blank for it to look for the gateway on the current page, if that makes sense
|
gatewayUrl: 'ws://localhost:3005/gateway'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
Loading…
Reference in a new issue