114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import { Status } from '$lib';
|
|
import { kvStore } from '$lib/server/db/index.js';
|
|
import { json } from '@sveltejs/kit';
|
|
|
|
interface SubscribedTo {
|
|
subscribed: string[]; // any ID (including channels, dms, et cetera can be added in here, to send SSE data to any group of users)
|
|
userId: string;
|
|
controller: ReadableStreamDefaultController;
|
|
}
|
|
|
|
export const _clients = new Map<string, SubscribedTo>();
|
|
|
|
export function _sendToSubscribers(id: string, payload: unknown) {
|
|
for (const [key, client] of _clients) {
|
|
if (client.subscribed.includes(id)) {
|
|
try {
|
|
client.controller.enqueue(`data: ${JSON.stringify(payload)}\n\n`);
|
|
} catch {
|
|
_clients.delete(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
export function _sendToUser(userId: string, payload: unknown) {
|
|
for (const [, client] of _clients) {
|
|
if (client.userId == userId) {
|
|
client.controller.enqueue(`data: ${JSON.stringify(payload)}\n\n`);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function _isUserConnected(userId: string): boolean {
|
|
for (const client of _clients.values()) {
|
|
if (client.userId === userId) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function GET({ locals, request }) {
|
|
if (!locals.user) {
|
|
return json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const userId = locals.user.id;
|
|
//@TODO add more to subscribed eventually, server members, et cetera
|
|
const subscribed = locals.user.friends.map((f) => f.id);
|
|
subscribed.push(userId); // shit such as friend requests
|
|
|
|
const overwrite = locals.user.statusOverwrite;
|
|
|
|
const sessionId = crypto.randomUUID();
|
|
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
_clients.set(sessionId, { subscribed, userId, controller });
|
|
console.log(`SSE Client opened. total: ${_clients.size}`);
|
|
|
|
controller.enqueue(`data: ${JSON.stringify({ type: 'connected', sessionId })}\n\n`);
|
|
|
|
if (overwrite === Status.DND) {
|
|
kvStore.set(`user-${userId}-state`, Status.DND);
|
|
_sendToSubscribers(userId, {
|
|
type: 'status',
|
|
id: userId,
|
|
status: Status.DND,
|
|
statusMessage:
|
|
kvStore.get('user-' + userId + '-state') != Status.OFFLINE
|
|
? kvStore.get('user-' + userId + '-message')
|
|
: ''
|
|
});
|
|
}
|
|
|
|
request.signal.addEventListener('abort', () => {
|
|
_clients.delete(sessionId);
|
|
console.log(`SSE Client aborted. total: ${_clients.size}`);
|
|
|
|
if (_isUserConnected(userId)) return;
|
|
|
|
if (overwrite === Status.OFFLINE) return;
|
|
|
|
kvStore.set(`user-${userId}-state`, Status.OFFLINE);
|
|
_sendToSubscribers(userId, {
|
|
type: 'status',
|
|
id: userId,
|
|
status: Status.OFFLINE,
|
|
statusMessage: ''
|
|
});
|
|
});
|
|
},
|
|
cancel() {
|
|
console.log(`SSE Client cancelled. total: ${_clients.size}`);
|
|
|
|
if (_isUserConnected(userId)) return;
|
|
|
|
if (overwrite === Status.OFFLINE) return;
|
|
|
|
kvStore.set(`user-${userId}-state`, Status.OFFLINE);
|
|
_sendToSubscribers(userId, {
|
|
type: 'status',
|
|
id: userId,
|
|
status: Status.OFFLINE,
|
|
statusMessage: ''
|
|
});
|
|
}
|
|
});
|
|
|
|
return new Response(stream, {
|
|
headers: {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
Connection: 'keep-alive'
|
|
}
|
|
});
|
|
}
|