Socket connection now works
- Pairing a new device works (I did a lot since the last commit)
This commit is contained in:
@@ -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.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user