WIP on redux refactor: last commit before client_server.js removal

This commit is contained in:
Claudio Maggioni 2020-04-07 14:49:49 +02:00
parent 636ade0273
commit c36d298f10
2 changed files with 122 additions and 32 deletions

View file

@ -3,6 +3,7 @@ import actions from "./storeActions";
import axios from "axios"; import axios from "axios";
class Endpoint { class Endpoint {
socket = null;
axiosInstance = axios.create({ axiosInstance = axios.create({
baseURL: this.URL, baseURL: this.URL,
validateStatus: (status) => status >= 200 && status < 300, validateStatus: (status) => status >= 200 && status < 300,
@ -55,6 +56,31 @@ class Endpoint {
} }
}); });
} }
/**
* Performs login
* @param {String} usernameOrEmail
* @param {String} password
* @returns {Promise<Undefined, String[]>} promise that resolves to void and rejects
* with user-fiendly errors as a String array
*/
static login(dispatch, usernameOrEmail, password) {
return Endpoint.axiosInstance
.post(`${Endpoint.URL}/auth/login`, {
usernameOrEmail,
password,
})
.then((res) => {
localStorage.setItem("token", res.token);
localStorage.setItem("exp", new Date().getTime() + 5 * 60 * 60 * 1000);
this.socket = new ServiceSocket(res.data.token);
return res.data.token;
});
}
static logout() {
this.socket.close();
this.socket = null;
}
/** /**
* Performs an authenticated GET request * Performs an authenticated GET request
@ -126,19 +152,8 @@ export class RemoteService {
*/ */
static login(usernameOrEmail, password) { static login(usernameOrEmail, password) {
return (dispatch) => { return (dispatch) => {
return Endpoint.axiosInstance return Endpoint.login(dispatch, usernameOrEmail, password)
.post(`${Endpoint.URL}/auth/login`, { .then((token) => dispatch(actions.loginSuccess(token)))
usernameOrEmail,
password,
})
.then((res) => {
localStorage.setItem("token", res.token);
localStorage.setItem(
"exp",
new Date().getTime() + 5 * 60 * 60 * 1000
);
dispatch(actions.loginSuccess(res.token));
})
.catch((err) => { .catch((err) => {
console.warn("login error", err); console.warn("login error", err);
return [ return [
@ -156,7 +171,7 @@ export class RemoteService {
* @param {String} password * @param {String} password
*/ */
static logout() { static logout() {
return (dispatch) => void dispatch(actions.logout()); return (dispatch) => Endpoint.logout.then(void dispatch(actions.logout()));
} }
/** /**
@ -386,6 +401,71 @@ export class RemoteService {
} }
} }
/** Class to handle connection with the sensor socket */
class ServiceSocket {
token;
authenticated = false;
retries = 0;
connection;
static get URL() {
const httpURL = new URL(Endpoint.URL);
const isSecure = httpURL.protocol === "https:";
const protocol = isSecure ? "wss:" : "ws:";
const port = httpURL.port || (isSecure ? 443 : 80);
return `${protocol}//${httpURL.host}:${port}/sensor-socket`;
}
/**
* Create a new sensor socket connection
* @param {string} token - The JWT token (needed for authentication)
*/
constructor(token) {
this.token = token;
this.authenticated = false;
}
_init() {
this.connection = new WebSocket(ServiceSocket.URL);
this.connection.onopen = (_) => {
this.connection.send(JSON.stringify({ token: this.token }));
};
this.connection.onmessage = (evt) => {
let data = JSON.parse(evt.data);
if (!this.authenticated) {
if (data.authenticated) {
this.authenticated = true;
this.retries = 0;
} else {
console.error("socket authentication failed", data);
}
} else {
this.invokeCallbacks(data);
}
};
this.connection.onerror = (evt) => {
console.warn("socket error", evt);
if (this.retries >= 5) {
console.error("too many socket connection retries");
return;
}
this.retries++;
this._init();
};
}
/**
* Closes the underlying websocket connection
*/
close() {
this.connection.close();
}
}
export class Forms { export class Forms {
/** /**
* Attempts to create a new user from the given data. * Attempts to create a new user from the given data.

View file

@ -1,22 +1,7 @@
import { createStore } from "redux"; import { createStore, applyMiddleware } from "redux";
import { RemoteService } from "./remote"; import thunk from "redux-thunk";
import { createDispatchHook } from "react-redux";
import actions from "./storeActions"; import actions from "./storeActions";
const initialToken = localStorage.getItem("token");
const initialState = {
login: {
loggedIn: false,
token: initialToken ? initialToken : null,
},
userInfo: null,
/** @type {[integer]Room} */
rooms: {},
/** @type {[integer]Device} */
devices: {},
};
function reducer(previousState, action) { function reducer(previousState, action) {
let newState = Object.assign({}, previousState); let newState = Object.assign({}, previousState);
const createOrUpdateRoom = (room) => { const createOrUpdateRoom = (room) => {
@ -129,9 +114,34 @@ function reducer(previousState, action) {
console.warn(`Action type ${action.type} unknown`, action); console.warn(`Action type ${action.type} unknown`, action);
} }
console.log("new state: ", newState);
return newState; return newState;
} }
const smartHutStore = createStore(reducer, initialState); function createSmartHutStore() {
const token = localStorage.getItem("token");
const exp = localStorage.getItem("exp");
const initialState = {
login: {
token: token,
},
userInfo: null,
/** @type {[integer]Room} */
rooms: {},
/** @type {[integer]Device} */
devices: {},
};
initialState.login.loggedIn = token && exp > new Date().getTime();
if (!initialState.login.loggedIn) {
localStorage.removeItem("token");
localStorage.removeItem("exp");
initialState.login.token = null;
}
return createStore(reducer, initialState, applyMiddleware(thunk));
}
const smartHutStore = createSmartHutStore();
export default smartHutStore; export default smartHutStore;