infoalloggi-monorepo/apps/infoalloggi/src/server/api/routers/chat_sse.ts
Marco Pedone ba4debdd1d Refactor: Remove unused procedures and clean up router files
- Deleted contact router and its associated mail functionality.
- Removed addEvent mutation from event_queue router.
- Cleaned up fatture router by removing createClient and getListClienti procedures.
- Commented out deletePrezziario mutation in prezziario router.
- Removed unused procedures in servizio router, including getServizioOrdini and getPreOnboardData.
- Cleaned up stats router by removing commented-out code.
- Updated stripe router to restrict health check to admin users and changed several public procedures to protected.
- Commented out sync router procedures and related types.
- Removed unused client-related functions in fic controller.
- Cleaned up servizio controller by removing getServizioOrdini and commented-out code.
- Refactored auth service to inline pwResetTokenFromMailHandler logic.
- Commented out deletePrezziario function in prezziario service.
- Cleaned up zod types by commenting out unused types.
- Commented out getMessagesArray function in kysely helper.
2026-03-05 12:32:03 +01:00

167 lines
4.2 KiB
TypeScript

import { TRPCError, tracked } from "@trpc/server";
import { z } from "zod/v4";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import {
addTyping,
getCurrentlyTyping,
removeStaleTyping,
removeTyping,
} from "~/server/controllers/chat.controller";
import { db } from "~/server/db";
import { add } from "~/server/services/chat.service";
import { ee, type OnlineStatus } from "~/server/sse";
import { zChatId, zUserId } from "~/server/utils/zod_types";
import { getKeydbClient } from "~/utils/keydb";
//IDEA forse cambiare in semplice interval polling invece di sse
export const chatSSERouter = createTRPCRouter({
create: protectedProcedure
.input(z.object({ userId: zUserId }))
.mutation(async ({ input }) => {
const newChat = await add({
db,
userId: input.userId,
});
return newChat.chatid;
}),
enterChat: protectedProcedure
.input(z.object({ chatId: zChatId }))
.mutation(async ({ input, ctx }) => {
const chatKey = `chat:${input.chatId}:onlineUsers`;
const keydb = getKeydbClient();
if (!keydb) {
console.warn(
"KeyDB client is not initialized. Skipping online status.",
);
return [];
}
// Add the user to the Redis set
await keydb.sadd(
chatKey,
JSON.stringify({
isAdmin: ctx.session.isAdmin,
nome: ctx.session.nome,
userId: ctx.session.id,
}),
);
const onlineUsers = await keydb.smembers(chatKey);
const parsedUsers = onlineUsers.map(
(user) => JSON.parse(user) as OnlineStatus,
);
ee.emit("chatUpdates", input.chatId, {
data: parsedUsers,
eventType: "online",
});
return parsedUsers;
}),
leaveChat: protectedProcedure
.input(z.object({ chatId: zChatId }))
.mutation(async ({ input, ctx }) => {
const chatKey = `chat:${input.chatId}:onlineUsers`;
const keydb = getKeydbClient();
if (!keydb) {
console.warn(
"KeyDB client is not initialized. Skipping online status.",
);
return [];
}
// Remove the user from the Redis set
await keydb.srem(
chatKey,
JSON.stringify({
isAdmin: ctx.session.isAdmin,
nome: ctx.session.nome,
userId: ctx.session.id,
}),
);
const onlineUsers = await keydb.smembers(chatKey);
const parsedUsers = onlineUsers.map(
(user) => JSON.parse(user) as OnlineStatus,
);
ee.emit("chatUpdates", input.chatId, {
data: parsedUsers,
eventType: "online",
});
return parsedUsers;
}),
// list: publicProcedure.query(async () => {
// return await db
// .selectFrom("chats")
// .select([
// "chats.chatid",
// "chats.created_at",
// "chats.user_ref",
// getMessagesArray(),
// ])
// .innerJoin("users", "users.id", "chats.user_ref")
// .select(["users.username", "users.email", "users.nome"])
// .orderBy("chats.created_at", "desc")
// .execute();
// }),
onChatEvent: protectedProcedure
.input(
z.object({
chatId: zChatId,
}),
)
.subscription(async function* ({ input, signal }) {
try {
const iterable = ee.toIterable("chatUpdates", {
signal,
});
for await (const [chatId, data] of iterable) {
if (chatId === input.chatId) {
yield tracked(chatId, data);
}
}
} catch (e) {
if (e instanceof Error && e.name === "AbortError") return;
console.error("Error in onChatEvent subscription:", e);
}
}),
type: protectedProcedure
.input(
z.object({ chatId: zChatId, typing: z.boolean(), username: z.string() }),
)
.mutation(async ({ ctx, input }) => {
try {
await removeStaleTyping(input.chatId);
if (input.typing) {
// Add the user to the set
await addTyping({
chatId: input.chatId,
userId: ctx.session.id,
username: input.username,
});
} else {
await removeTyping({
chatId: input.chatId,
userId: ctx.session.id,
username: input.username,
});
}
ee.emit("chatUpdates", input.chatId, {
data: await getCurrentlyTyping(input.chatId),
eventType: "typing",
});
return true;
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message:
"An error occurred while processing the typing event: " +
(e as Error).message,
});
}
}),
});