chatapi / src /handlers /friendHandler.ts
wilooper's picture
initial commit
87cb242
Raw History Blame Contribute Delete
3.78 kB
import WebSocket from 'ws';
import { ClientMessage, safeSend } from '../utils/protocol';
import {
sendFriendRequest,
acceptFriendRequest,
rejectFriendRequest,
removeFriend,
getFriendList,
areFriends,
} from '../services/friendService';
import { isOnline } from '../redis/presenceClient';
import { registry } from '../services/connectionRegistry';
import { logger } from '../utils/logger';
/**
* Routes friend-related WS actions.
* Called from connectionHandler's routeMessage for type: 'friend'
*
* Payload shape: { action, targetUid, ...extras }
* Actions: send-request | accept | reject | remove | list | status
*/
export async function handleFriend(
ws: WebSocket,
uid: string,
msg: ClientMessage
): Promise<void> {
const { action, targetUid } = msg.payload as { action?: string; targetUid?: string };
if (!action) {
safeSend(ws, 'error', { message: 'action is required' }, msg.requestId);
return;
}
switch (action) {
case 'send-request': {
if (!targetUid) { safeSend(ws, 'error', { message: 'targetUid required' }, msg.requestId); return; }
if (targetUid === uid) { safeSend(ws, 'error', { message: 'Cannot friend yourself' }, msg.requestId); return; }
await sendFriendRequest(uid, targetUid);
safeSend(ws, 'message', { ack: true, action: 'send-request', targetUid }, msg.requestId);
// Notify the target if online
const targetMeta = registry.getByUid(targetUid);
if (targetMeta) {
safeSend(targetMeta.ws, 'message', { type: 'friend-request', fromUid: uid });
}
break;
}
case 'accept': {
if (!targetUid) { safeSend(ws, 'error', { message: 'targetUid required' }, msg.requestId); return; }
const ok = await acceptFriendRequest(targetUid, uid); // targetUid sent the request
safeSend(ws, 'message', { ack: true, action: 'accept', success: ok }, msg.requestId);
if (ok) {
const targetMeta = registry.getByUid(targetUid);
if (targetMeta) {
safeSend(targetMeta.ws, 'message', { type: 'friend-accepted', fromUid: uid });
}
}
break;
}
case 'reject': {
if (!targetUid) { safeSend(ws, 'error', { message: 'targetUid required' }, msg.requestId); return; }
await rejectFriendRequest(targetUid, uid);
safeSend(ws, 'message', { ack: true, action: 'reject' }, msg.requestId);
break;
}
case 'remove': {
if (!targetUid) { safeSend(ws, 'error', { message: 'targetUid required' }, msg.requestId); return; }
await removeFriend(uid, targetUid);
safeSend(ws, 'message', { ack: true, action: 'remove' }, msg.requestId);
break;
}
case 'list': {
const friends = await getFriendList(uid);
// Bulk check online status from Redis
const statusChecks = await Promise.allSettled(friends.map(f => isOnline(f)));
const withStatus = friends.map((f, i) => ({
uid: f,
online: statusChecks[i].status === 'fulfilled' && statusChecks[i].value === true,
}));
safeSend(ws, 'message', { action: 'list', friends: withStatus }, msg.requestId);
break;
}
case 'status': {
if (!targetUid) { safeSend(ws, 'error', { message: 'targetUid required' }, msg.requestId); return; }
// Only allow checking status of friends
const isFriend = await areFriends(uid, targetUid);
if (!isFriend) {
safeSend(ws, 'error', { message: 'Not friends with this user' }, msg.requestId);
return;
}
const online = await isOnline(targetUid);
safeSend(ws, 'message', { action: 'status', uid: targetUid, online }, msg.requestId);
break;
}
default:
safeSend(ws, 'error', { message: `Unknown friend action: ${action}` }, msg.requestId);
}
}