Socket connection now works

- Pairing a new device works

(I did a lot since the last commit)
This commit is contained in:
2021-11-14 01:42:21 +01:00
parent 28e85ea730
commit 19b7c05d75
72 changed files with 5861 additions and 23719 deletions

View File

@@ -1,23 +1,165 @@
import * as socketio from "socket.io";
import { Server } from 'http';
import { JWT_SECRET, logger } from "../app";
import { randomString, verifyJWT } from "./crypto";
import { decode, sign } from "jsonwebtoken";
import { User } from "../models/user/user.model";
import { IPhone } from "../models/phone/phone.interface";
import { IUser } from "../models/user/user.interface";
import { Phone } from "../models/phone/phone.model";
/**
* This class handles all SocketIO connections.
*
* *SocketIO is another layer ontop of WebSockets*
*/
class SocketManager {
export class SocketManager {
io: socketio.Server;
express: Express.Application;
constructor (express: Express.Application) {
this.io = new socketio.Server(express);
this.express = express;
/**
* Frontends have limited access to socket.io features. They just sit in connection and wait for any events.
*/
frontends: Array<string>;
/**
* A phone has some more privileges. They activly send new data and thus have write access.
*/
phones: Array<string>;
constructor(httpServer: Server) {
logger.debug("Preparing real-time communication ...");
this.frontends = [];
this.phones = [];
this.io = new socketio.Server();
this.io.listen(httpServer);
this.init();
}
getUserRoom(user: IUser) {
return `user-${user.id}`;
}
getUserFrontendRoom(user: IUser) {
return `user-${user.id}-frontend`;
}
getUserPhoneRoom(user: IUser) {
return `user-${user.id}-phone`;
}
init() {
this.io.on('connect', data => {
console.log('New connection')
this.io.on('connection', socket => {
socket.on('requestAccess', async data => {
data = JSON.parse(data);
let token: string = data.token;
let phone: IPhone = data.phone;
// If request is faulty or token invalid -> return.
if (data === undefined || phone === undefined) return;
if (await !verifyJWT(token)) return;
const id = decode(token, { json: true })!.user;
const user = await User.findById(id);
// If user doesn't exist -> return.
if (user === null) return;
const approvalCode = randomString(6, '0123456789');
// Create phone
const newPhone = await Phone.create({
...phone,
user,
approval: {
code: approvalCode
}
});
this.io.to(this.getUserRoom(user)).emit('approvePhone', newPhone);
// Respond with id so device can later submit correct code.
socket.emit('requestAccess', { phoneId: newPhone.id });
logger.info(`User ${user?.name} requests to connect new phone ${phone.displayName}`);
});
socket.on('submitPairCode', async data => {
const { phoneId, code } = JSON.parse(data);
console.log("Entry:", data, phoneId, code);
if (phoneId === undefined || code === undefined) return;
const phone = await Phone.findById(phoneId);
if (phone === null) return;
console.log(data, phoneId, code);
// If provided code isn't equal with actual code -> Emit event again.
if (phone.approval.code !== code) {
console.log(data, phoneId, code);
socket.emit('submitPairCode', '');
return;
}
phone.approval.approvedOn = new Date();
await phone.save();
// We're good. Create JWT token.
const token = sign({ user: phone.user._id, type: 'phone' }, JWT_SECRET, { expiresIn: '30d' });
socket.emit('submitPairCode', token);
});
socket.on('loginFrontend', async (token: string) => {
if (await verifyJWT(token)) {
const tokenDecoded = decode(token, { json: true });
const id = tokenDecoded!.user;
const type = tokenDecoded!.type;
const user = await User.findById(id);
if (user == null) return;
if (type != 'frontend') return;
if (this.frontends.indexOf(socket.id) != -1)
this.frontends.push(socket.id);
socket.join(this.getUserRoom(user));
socket.join(this.getUserFrontendRoom(user));
logger.info(`Socket ${socket.id} became a frontend socket.`);
}
});
socket.on('loginPhone', async (token: string) => {
if (await verifyJWT(token)) {
const tokenDecoded = decode(token, { json: true });
const id = tokenDecoded!.user;
const type = tokenDecoded!.type;
const user = await User.findById(id);
if (user == null) return;
if (type != 'phone') return;
if (this.frontends.indexOf(socket.id) != -1)
this.frontends.push(socket.id);
socket.join(this.getUserRoom(user));
socket.join(this.getUserPhoneRoom(user));
logger.info(`Socket ${socket.id} became a phone socket.`);
}
});
logger.info(`New socket connection from ${socket.handshake.address} with id ${socket.id} (total connections: ${this.io.sockets.sockets.size})`);
socket.emit('test', 'Yay, it works.');
});
}