refactor: packet handling
This commit is contained in:
parent
d085339567
commit
72f3d9ec9b
14
src/server/packet/add-favorite.js
Normal file
14
src/server/packet/add-favorite.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import AddFavorite from '~/common/packets/add-favorite.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export default {
|
||||
Packet: AddFavorite,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
await req.user.createFavorite({channel: packet.data});
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
fn();
|
||||
},
|
||||
};
|
38
src/server/packet/add-friend.js
Normal file
38
src/server/packet/add-friend.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import AddFriend from '~/common/packets/add-friend.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export default {
|
||||
Packet: AddFriend,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const {
|
||||
Friendship,
|
||||
User,
|
||||
} = allModels();
|
||||
const adderId = req.user.id;
|
||||
const user = await User.findOne({where: {redditUsername: packet.data.nameOrStatus}});
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
const addeeId = user.id;
|
||||
let friendship = await Friendship.findOne({where: {adderId: addeeId, addeeId: adderId}});
|
||||
const friendshipIsActive = !!friendship;
|
||||
if (friendship) {
|
||||
friendship.status = 'active';
|
||||
await friendship.save();
|
||||
}
|
||||
else {
|
||||
friendship = await Friendship.create({adderId, addeeId});
|
||||
}
|
||||
[addeeId, adderId].forEach((id) => {
|
||||
socket.to(`/user/${id}`, new AddFriend({
|
||||
addeeId: friendshipIsActive ? adderId : addeeId,
|
||||
adderId: friendshipIsActive ? addeeId : adderId,
|
||||
nameOrStatus: friendship.status,
|
||||
}));
|
||||
});
|
||||
fn({id: addeeId});
|
||||
},
|
||||
};
|
34
src/server/packet/block.js
Normal file
34
src/server/packet/block.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {Op} from 'sequelize';
|
||||
|
||||
import Block from '~/common/packets/block.packet';
|
||||
import RemoveFriend from '~/common/packets/remove-friend.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
import {removeFavoritedUser} from './remove-favorite';
|
||||
|
||||
export default {
|
||||
Packet: Block,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const id = packet.data;
|
||||
const {Friendship, User} = allModels();
|
||||
await req.user.createBlock({blocked: id});
|
||||
await Friendship.destroy({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{[Op.and]: [{addeeId: req.userId}, {adderId: id}]},
|
||||
{[Op.and]: [{addeeId: id}, {adderId: req.userId}]},
|
||||
],
|
||||
},
|
||||
});
|
||||
const user = await User.findByPk(id);
|
||||
removeFavoritedUser(socket, user, req.user);
|
||||
removeFavoritedUser(socket, req.user, user);
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
socket.to(`/user/${id}`, new RemoveFriend(req.userId));
|
||||
socket.to(`/user/${req.userId}`, new RemoveFriend(id));
|
||||
fn();
|
||||
},
|
||||
};
|
10
src/server/packet/index.js
Normal file
10
src/server/packet/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export {default as AddFavorite} from './add-favorite';
|
||||
export {default as AddFriend} from './add-friend';
|
||||
export {default as Block} from './block';
|
||||
export {default as Join} from './join';
|
||||
export {default as Leave} from './leave';
|
||||
export {default as Message} from './message';
|
||||
export {default as RemoveFavorite} from './remove-favorite';
|
||||
export {default as RemoveFriend} from './remove-friend';
|
||||
export {default as Unblock} from './unblock';
|
||||
export {default as Usernames} from './usernames';
|
31
src/server/packet/join.js
Normal file
31
src/server/packet/join.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import {promisify} from 'util';
|
||||
|
||||
import {ServerSocket} from '@avocado/net/server/socket';
|
||||
|
||||
import Join from '~/common/packets/join.packet';
|
||||
|
||||
import {
|
||||
channelState,
|
||||
channelUsers,
|
||||
} from '~/server/entry';
|
||||
|
||||
export const userJoin = async (channel, socket) => {
|
||||
const userId = '/r/anonymous' === channel ? 0 : socket.handshake.userId;
|
||||
const users = await channelUsers(socket.handshake, channel);
|
||||
if (-1 === users.indexOf(userId)) {
|
||||
ServerSocket.send(socket.to(channel), new Join({channel, id: userId}));
|
||||
}
|
||||
await promisify(socket.join.bind(socket))(channel);
|
||||
};
|
||||
|
||||
export default {
|
||||
Packet: Join,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const {channel} = packet.data;
|
||||
await userJoin(channel, socket.socket);
|
||||
fn(await channelState(req, channel));
|
||||
},
|
||||
};
|
25
src/server/packet/leave.js
Normal file
25
src/server/packet/leave.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import {promisify} from 'util';
|
||||
|
||||
import Leave from '~/common/packets/leave.packet';
|
||||
|
||||
import {channelUserCounts} from '~/server/entry';
|
||||
|
||||
export const userLeave = async (channel, socket) => {
|
||||
const userId = '/r/anonymous' === channel ? 0 : socket.req.userId;
|
||||
await promisify(socket.leave.bind(socket))(channel);
|
||||
const userCounts = await channelUserCounts(socket.req, channel);
|
||||
if (!userCounts[userId]) {
|
||||
socket.to(channel, new Leave({channel, id: userId}));
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
Packet: Leave,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {channel} = packet.data;
|
||||
await userLeave(channel, socket);
|
||||
fn();
|
||||
},
|
||||
};
|
48
src/server/packet/message.js
Normal file
48
src/server/packet/message.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
import {parseChannel} from '~/common/channel';
|
||||
import Message from '~/common/packets/message.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export default {
|
||||
Packet: Message,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const {pubClient} = req.adapter;
|
||||
const {User} = allModels();
|
||||
const {channel, message} = packet.data;
|
||||
const {name, type} = parseChannel(`/chat${channel}`);
|
||||
const other = await User.findOne({where: {redditUsername: name}});
|
||||
const owner = '/r/anonymous' === channel ? 0 : req.userId;
|
||||
const serverChannel = 'r' === type
|
||||
? channel
|
||||
: `/u/${[name, req.user.redditUsername].sort().join('$')}`;
|
||||
const timestamp = Date.now();
|
||||
const username = '/r/anonymous' === channel ? 'anonymous' : req.user.redditUsername;
|
||||
const uuid = uuidv4();
|
||||
const key = `${serverChannel}:messages:${uuid}`;
|
||||
('u' === type ? [`/user/${other.id}`, `/user/${req.userId}`] : [channel]).forEach((room) => (
|
||||
socket.to(room, new Message({
|
||||
...packet.data,
|
||||
channel: 'r' === type
|
||||
? channel
|
||||
: `/u/${username === room.substr(3) ? name : username}`,
|
||||
owner,
|
||||
timestamp,
|
||||
uuid,
|
||||
}))
|
||||
));
|
||||
pubClient
|
||||
.multi()
|
||||
.set(key, JSON.stringify({
|
||||
message,
|
||||
owner,
|
||||
socket: socket.id,
|
||||
timestamp,
|
||||
}))
|
||||
.expire(key, '/r/anonymous' === channel ? 60 : 600)
|
||||
.exec(() => fn([timestamp, uuid]));
|
||||
},
|
||||
};
|
21
src/server/packet/remove-favorite.js
Normal file
21
src/server/packet/remove-favorite.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import RemoveFavorite from '~/common/packets/remove-favorite.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export default {
|
||||
Packet: RemoveFavorite,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const {Favorite} = allModels();
|
||||
const favorites = await req.user.getFavorites();
|
||||
const toRemove = favorites.find(({channel}) => channel === packet.data);
|
||||
await Favorite.destroy({
|
||||
where: {
|
||||
id: toRemove.id,
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
fn();
|
||||
},
|
||||
};
|
44
src/server/packet/remove-friend.js
Normal file
44
src/server/packet/remove-friend.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import {Op} from 'sequelize';
|
||||
|
||||
import RemoveFavorite from '~/common/packets/remove-favorite.packet';
|
||||
import RemoveFriend from '~/common/packets/remove-friend.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export const removeFavoritedUser = async (socket, user, other) => {
|
||||
const {Favorite} = allModels();
|
||||
const favorites = await user.getFavorites();
|
||||
const toRemove = favorites.find(({channel}) => channel === `/u/${other.redditUsername}`);
|
||||
if (toRemove) {
|
||||
await Favorite.destroy({
|
||||
where: {
|
||||
id: toRemove.id,
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${user.id}`, new RemoveFavorite(`/u/${other.redditUsername}`));
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
Packet: RemoveFriend,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const id = packet.data;
|
||||
const {Friendship, User} = allModels();
|
||||
await Friendship.destroy({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{[Op.and]: [{addeeId: req.userId}, {adderId: id}]},
|
||||
{[Op.and]: [{addeeId: id}, {adderId: req.userId}]},
|
||||
],
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${id}`, new RemoveFriend(req.userId));
|
||||
socket.to(`/user/${req.userId}`, new RemoveFriend(id));
|
||||
const user = await User.findByPk(id);
|
||||
removeFavoritedUser(socket, user, req.user);
|
||||
removeFavoritedUser(socket, req.user, user);
|
||||
fn();
|
||||
},
|
||||
};
|
20
src/server/packet/unblock.js
Normal file
20
src/server/packet/unblock.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Unblock from '~/common/packets/unblock.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export default {
|
||||
Packet: Unblock,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {req} = socket;
|
||||
const {Block: BlockModel} = allModels();
|
||||
await BlockModel.destroy({
|
||||
where: {
|
||||
blocked: packet.data,
|
||||
user_id: req.userId,
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
fn();
|
||||
},
|
||||
};
|
14
src/server/packet/usernames.js
Normal file
14
src/server/packet/usernames.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Usernames from '~/common/packets/usernames.packet';
|
||||
|
||||
import {allModels} from '~/server/models/registrar';
|
||||
|
||||
export default {
|
||||
Packet: Usernames,
|
||||
validator: () => true,
|
||||
responder: async (packet, socket, fn) => {
|
||||
const {User} = allModels();
|
||||
fn(await Promise.all(packet.data.map(
|
||||
async (id) => (await User.findByPk(id)).redditUsername,
|
||||
)));
|
||||
},
|
||||
};
|
|
@ -1,32 +1,14 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import {promisify} from 'util';
|
||||
|
||||
import {ServerSocket, SocketServer} from '@avocado/net/server/socket';
|
||||
import {SocketServer} from '@avocado/net/server/socket';
|
||||
import socketSession from 'express-socket.io-session';
|
||||
import {Op} from 'sequelize';
|
||||
import redisAdapter from 'socket.io-redis';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
import {joinChannel, parseChannel} from '~/common/channel';
|
||||
import AddFavorite from '~/common/packets/add-favorite.packet';
|
||||
import AddFriend from '~/common/packets/add-friend.packet';
|
||||
import Block from '~/common/packets/block.packet';
|
||||
import Join from '~/common/packets/join.packet';
|
||||
import Leave from '~/common/packets/leave.packet';
|
||||
import Message from '~/common/packets/message.packet';
|
||||
import RemoveFavorite from '~/common/packets/remove-favorite.packet';
|
||||
import RemoveFriend from '~/common/packets/remove-friend.packet';
|
||||
import Usernames from '~/common/packets/usernames.packet';
|
||||
import Unblock from '~/common/packets/unblock.packet';
|
||||
|
||||
import {
|
||||
channelsToHydrate,
|
||||
channelState,
|
||||
channelUserCounts,
|
||||
channelUsers,
|
||||
} from '~/server/entry';
|
||||
import {channelsToHydrate} from '~/server/entry';
|
||||
|
||||
import {allModels} from './models/registrar';
|
||||
import * as PacketHandlers from './packet';
|
||||
import {userJoin} from './packet/join';
|
||||
import {userLeave} from './packet/leave';
|
||||
import passport from './passport';
|
||||
import createRedisClient from './redis';
|
||||
import session from './session';
|
||||
|
@ -34,31 +16,14 @@ import session from './session';
|
|||
const pubClient = createRedisClient();
|
||||
const subClient = createRedisClient();
|
||||
const adapter = redisAdapter({pubClient, subClient});
|
||||
|
||||
const removeFavoritedUser = async (socket, user, other) => {
|
||||
const {Favorite} = allModels();
|
||||
const favorites = await user.getFavorites();
|
||||
const toRemove = favorites.find(({channel}) => channel === `/u/${other.redditUsername}`);
|
||||
if (toRemove) {
|
||||
await Favorite.destroy({
|
||||
where: {
|
||||
id: toRemove.id,
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${user.id}`, new RemoveFavorite(`/u/${other.redditUsername}`));
|
||||
}
|
||||
};
|
||||
const packetHandlers = new Map();
|
||||
|
||||
export function createSocketServer(httpServer) {
|
||||
const {
|
||||
Block: BlockModel,
|
||||
Favorite,
|
||||
Friendship,
|
||||
User,
|
||||
} = allModels();
|
||||
const socketServer = new SocketServer(httpServer, {
|
||||
adapter,
|
||||
Object.keys(PacketHandlers).forEach((key) => {
|
||||
const {Packet, responder, validator} = PacketHandlers[key];
|
||||
packetHandlers.set(Packet, {responder, validator});
|
||||
});
|
||||
const socketServer = new SocketServer(httpServer, {adapter});
|
||||
socketServer.io.use(socketSession(session()));
|
||||
socketServer.io.use((socket, next) => {
|
||||
passport.initialize()(socket.handshake, undefined, next);
|
||||
|
@ -81,22 +46,6 @@ export function createSocketServer(httpServer) {
|
|||
}
|
||||
next();
|
||||
});
|
||||
const userJoin = async (channel, socket) => {
|
||||
const userId = '/r/anonymous' === channel ? 0 : socket.handshake.userId;
|
||||
const users = await channelUsers(socket.handshake, channel);
|
||||
if (-1 === users.indexOf(userId)) {
|
||||
ServerSocket.send(socket.to(channel), new Join({channel, id: userId}));
|
||||
}
|
||||
await promisify(socket.join.bind(socket))(channel);
|
||||
};
|
||||
const userLeave = async (channel, socket) => {
|
||||
const userId = '/r/anonymous' === channel ? 0 : socket.req.userId;
|
||||
await promisify(socket.leave.bind(socket))(channel);
|
||||
const userCounts = await channelUserCounts(socket.req, channel);
|
||||
if (!userCounts[userId]) {
|
||||
socket.to(channel, new Leave({channel, id: userId}));
|
||||
}
|
||||
};
|
||||
socketServer.io.use(async (socket, next) => {
|
||||
await Promise.all(
|
||||
(await channelsToHydrate(socket.handshake))
|
||||
|
@ -106,143 +55,9 @@ export function createSocketServer(httpServer) {
|
|||
next();
|
||||
});
|
||||
socketServer.on('connect', (socket) => {
|
||||
const {req} = socket;
|
||||
socket.on('packet', async (packet, fn) => {
|
||||
if (packet instanceof AddFavorite) {
|
||||
await req.user.createFavorite({channel: packet.data});
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
}
|
||||
if (packet instanceof AddFriend) {
|
||||
const adderId = req.user.id;
|
||||
const user = await User.findOne({where: {redditUsername: packet.data.nameOrStatus}});
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
const addeeId = user.id;
|
||||
let friendship = await Friendship.findOne({where: {adderId: addeeId, addeeId: adderId}});
|
||||
const friendshipIsActive = !!friendship;
|
||||
if (friendship) {
|
||||
friendship.status = 'active';
|
||||
await friendship.save();
|
||||
}
|
||||
else {
|
||||
friendship = await Friendship.create({adderId, addeeId});
|
||||
}
|
||||
[addeeId, adderId].forEach((id) => {
|
||||
socket.to(`/user/${id}`, new AddFriend({
|
||||
addeeId: friendshipIsActive ? adderId : addeeId,
|
||||
adderId: friendshipIsActive ? addeeId : adderId,
|
||||
nameOrStatus: friendship.status,
|
||||
}));
|
||||
});
|
||||
fn({id: addeeId});
|
||||
}
|
||||
if (packet instanceof Block) {
|
||||
const id = packet.data;
|
||||
await req.user.createBlock({blocked: id});
|
||||
await Friendship.destroy({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{[Op.and]: [{addeeId: req.userId}, {adderId: id}]},
|
||||
{[Op.and]: [{addeeId: id}, {adderId: req.userId}]},
|
||||
],
|
||||
},
|
||||
});
|
||||
const user = await User.findByPk(id);
|
||||
removeFavoritedUser(socket, user, req.user);
|
||||
removeFavoritedUser(socket, req.user, user);
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
socket.to(`/user/${id}`, new RemoveFriend(req.userId));
|
||||
socket.to(`/user/${req.userId}`, new RemoveFriend(id));
|
||||
fn();
|
||||
}
|
||||
if (packet instanceof RemoveFavorite) {
|
||||
const favorites = await req.user.getFavorites();
|
||||
const toRemove = favorites.find(({channel}) => channel === packet.data);
|
||||
await Favorite.destroy({
|
||||
where: {
|
||||
id: toRemove.id,
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
fn();
|
||||
}
|
||||
if (packet instanceof RemoveFriend) {
|
||||
const id = packet.data;
|
||||
await Friendship.destroy({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{[Op.and]: [{addeeId: req.userId}, {adderId: id}]},
|
||||
{[Op.and]: [{addeeId: id}, {adderId: req.userId}]},
|
||||
],
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${id}`, new RemoveFriend(req.userId));
|
||||
socket.to(`/user/${req.userId}`, new RemoveFriend(id));
|
||||
const user = await User.findByPk(id);
|
||||
removeFavoritedUser(socket, user, req.user);
|
||||
removeFavoritedUser(socket, req.user, user);
|
||||
fn();
|
||||
}
|
||||
if (packet instanceof Join) {
|
||||
const {channel} = packet.data;
|
||||
await userJoin(channel, socket.socket);
|
||||
fn(await channelState(req, channel));
|
||||
}
|
||||
if (packet instanceof Leave) {
|
||||
const {channel} = packet.data;
|
||||
await userLeave(channel, socket);
|
||||
fn();
|
||||
}
|
||||
if (packet instanceof Message) {
|
||||
const {channel, message} = packet.data;
|
||||
const {name, type} = parseChannel(`/chat${channel}`);
|
||||
const other = await User.findOne({where: {redditUsername: name}});
|
||||
const owner = '/r/anonymous' === channel ? 0 : req.userId;
|
||||
const serverChannel = 'r' === type
|
||||
? channel
|
||||
: `/u/${[name, req.user.redditUsername].sort().join('$')}`;
|
||||
const timestamp = Date.now();
|
||||
const username = '/r/anonymous' === channel ? 'anonymous' : req.user.redditUsername;
|
||||
const uuid = uuidv4();
|
||||
const key = `${serverChannel}:messages:${uuid}`;
|
||||
('u' === type ? [`/user/${other.id}`, `/user/${req.userId}`] : [channel]).forEach((room) => (
|
||||
socket.to(room, new Message({
|
||||
...packet.data,
|
||||
channel: 'r' === type
|
||||
? channel
|
||||
: `/u/${username === room.substr(3) ? name : username}`,
|
||||
owner,
|
||||
timestamp,
|
||||
uuid,
|
||||
}))
|
||||
));
|
||||
pubClient
|
||||
.multi()
|
||||
.set(key, JSON.stringify({
|
||||
message,
|
||||
owner,
|
||||
socket: socket.id,
|
||||
timestamp,
|
||||
}))
|
||||
.expire(key, '/r/anonymous' === channel ? 60 : 600)
|
||||
.exec(() => fn([timestamp, uuid]));
|
||||
}
|
||||
if (packet instanceof Unblock) {
|
||||
await BlockModel.destroy({
|
||||
where: {
|
||||
blocked: packet.data,
|
||||
user_id: req.userId,
|
||||
},
|
||||
});
|
||||
socket.to(`/user/${req.userId}`, packet);
|
||||
fn();
|
||||
}
|
||||
if (packet instanceof Usernames) {
|
||||
fn(await Promise.all(packet.data.map(
|
||||
async (id) => (await User.findByPk(id)).redditUsername,
|
||||
)));
|
||||
}
|
||||
const {responder} = packetHandlers.get(packet.constructor);
|
||||
responder(packet, socket, fn);
|
||||
});
|
||||
socket.on('disconnecting', async () => {
|
||||
Object.keys(socket.socket.rooms).forEach((room) => {
|
||||
|
|
Loading…
Reference in New Issue
Block a user