chore: initial commit
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import type {Namespace, Socket} from 'socket.io';
|
||||
import type {RelaySessionStore} from './session_store.ts';
|
||||
import {RelayError, error_ack, success} from '../utils/relay_validation.ts';
|
||||
|
||||
export function install_relay_routes(namespace: Namespace, store: RelaySessionStore): () => void {
|
||||
const sockets = new Map<Socket, () => void>();
|
||||
const connect = (socket: Socket) => {
|
||||
if (!store.attach(socket)) return;
|
||||
const handlers = new Map<string, (...args: unknown[]) => void>();
|
||||
const route = (name: string, operation: (value: unknown) => unknown | Promise<unknown>, wrapped = true, ack_required = true) => {
|
||||
const handler = (...args: unknown[]) => {
|
||||
const callback = args[args.length - 1];
|
||||
if (ack_required && typeof callback !== 'function') return;
|
||||
let replied = false;
|
||||
const ack = (value: unknown) => {
|
||||
if (replied || typeof callback !== 'function' || !store.can_reply(socket)) return;
|
||||
replied = true;
|
||||
try {callback(value);} catch { /* A peer callback cannot disrupt route cleanup. */ }
|
||||
};
|
||||
if (args.length !== (typeof callback === 'function' ? 2 : 1)) {
|
||||
ack(error_ack(new RelayError('INVALID_REQUEST', 'Expected one request payload')));
|
||||
return;
|
||||
}
|
||||
void Promise.resolve().then(() => operation(args[0])).then(
|
||||
value => ack(wrapped ? success(value) : value), error => ack(error_ack(error)));
|
||||
};
|
||||
handlers.set(name, handler);
|
||||
socket.on(name, handler);
|
||||
};
|
||||
route('join_room', value => store.join(socket, value));
|
||||
route('reauth', value => store.join(socket, value, true));
|
||||
route('forward_node_ready', value => store.node_ready(socket, value));
|
||||
route('relay_request', value => store.request(socket, value), false);
|
||||
route('bot_log', value => store.log(socket, value), true, false);
|
||||
route('info_update', value => store.info(socket, value), true, false);
|
||||
const unknown = (name: string, ...args: unknown[]) => {
|
||||
if (handlers.has(name)) return;
|
||||
const callback = args[args.length - 1];
|
||||
try {
|
||||
store.unknown_event(socket);
|
||||
if (typeof callback === 'function' && store.can_reply(socket))
|
||||
callback(error_ack(new RelayError('FORBIDDEN', 'Event is not allowed on the relay data channel')));
|
||||
} catch { /* The store disconnects unknown-event floods. */ }
|
||||
};
|
||||
const cleanup = () => {
|
||||
socket.offAny(unknown);
|
||||
socket.off('disconnect', cleanup);
|
||||
for (const [name, handler] of handlers) socket.off(name, handler);
|
||||
sockets.delete(socket);
|
||||
};
|
||||
socket.onAny(unknown);
|
||||
socket.on('disconnect', cleanup);
|
||||
sockets.set(socket, cleanup);
|
||||
};
|
||||
namespace.on('connection', connect);
|
||||
let disposed = false;
|
||||
return () => {
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
namespace.off('connection', connect);
|
||||
for (const [socket, cleanup] of [...sockets]) {
|
||||
cleanup();
|
||||
store.disconnect(socket);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user