From 1f06ded59354d884d4f9e5de26af39d2141055de Mon Sep 17 00:00:00 2001 From: wackbyte Date: Wed, 4 Oct 2023 19:05:00 -0400 Subject: [PATCH 1/8] reorganize trpc code --- astro.config.ts | 2 +- package.json | 2 +- src/backend/wsRouter.ts | 5 ++++ src/components/Chat.svelte | 2 +- src/trpc/client.ts | 20 ++++--------- src/{backend => trpc}/context.ts | 4 --- src/trpc/router.ts | 10 +++++++ .../router.ts => trpc/routes/chat.ts} | 29 ++++++------------- src/trpc/t.ts | 7 +++++ ws.js | 2 +- 10 files changed, 40 insertions(+), 43 deletions(-) create mode 100644 src/backend/wsRouter.ts rename src/{backend => trpc}/context.ts (86%) create mode 100644 src/trpc/router.ts rename src/{backend/router.ts => trpc/routes/chat.ts} (93%) create mode 100644 src/trpc/t.ts diff --git a/astro.config.ts b/astro.config.ts index 94770fba..9376ecb0 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ port: 8443, routerFilePath: path.resolve( path.dirname(fileURLToPath(import.meta.url)), - "src/backend/router.ts" + "src/backend/wsRouter.ts" ), routerName: "socketRouter", }), diff --git a/package.json b/package.json index 7aaa6b9f..bec5c21d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "astro dev", "start": "astro dev", - "build": "astro build && vite build --outDir dist-router --ssr src/backend/router.ts", + "build": "astro build && vite build --outDir dist-router --ssr src/backend/wsRouter.ts", "preview": "astro preview", "astro": "astro", "check": "pnpm check:astro ; pnpm check:svelte", diff --git a/src/backend/wsRouter.ts b/src/backend/wsRouter.ts new file mode 100644 index 00000000..ee2e8001 --- /dev/null +++ b/src/backend/wsRouter.ts @@ -0,0 +1,5 @@ +import { createContext } from "../trpc/context"; +import { router } from "../trpc/router"; + +export const socketRouter = router; +export const socketContext = createContext; diff --git a/src/components/Chat.svelte b/src/components/Chat.svelte index a698cf0d..3bdbc12b 100644 --- a/src/components/Chat.svelte +++ b/src/components/Chat.svelte @@ -16,7 +16,7 @@ import Icon from "@iconify/svelte"; import type { Unsubscribable } from "@trpc/server/observable"; import UserChip from "./UserChip.svelte"; - import { client } from "../trpc/client"; + import { chat as client } from "../trpc/client"; import { chat as md } from "../helper/markdown"; export let channel = "limbo"; diff --git a/src/trpc/client.ts b/src/trpc/client.ts index 3a5242ef..9b3f106a 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -1,15 +1,9 @@ import { createTRPCProxyClient, createWSClient, wsLink } from "@trpc/client"; -import type { SocketRouter } from "../backend/router"; -import superjson from "superjson"; +import type { Router } from "./router"; +import SuperJSON from "superjson"; -// // @ts-ignore -// export const client = generateRPCClient({ -// socketURL: "", -// transformer: superjson, -// }); - -export const client = createTRPCProxyClient({ - transformer: superjson, +export const client = createTRPCProxyClient({ + transformer: SuperJSON, links: [ wsLink({ client: createWSClient({ @@ -21,8 +15,4 @@ export const client = createTRPCProxyClient({ ], }); -// export const useQuery = client.useQuery; -// export const useMutation = client.useMutation; -// export const runQuery = client.runQueryAndThrow; -// export const runMutation = client.runMutationAndThrow; -// export const subscribe = client.subscribe; +export const chat = client.chat; diff --git a/src/backend/context.ts b/src/trpc/context.ts similarity index 86% rename from src/backend/context.ts rename to src/trpc/context.ts index c17c141f..dc4e2d8b 100644 --- a/src/backend/context.ts +++ b/src/trpc/context.ts @@ -31,10 +31,6 @@ export async function createContext({ req: IncomingMessage; res: WebSocket; }) { - // Create your context based on the request object - // Will be available as `ctx` in all your resolvers - // This is just an example of something you might want to do in your ctx fn - async function getUserFromHeader() { if (req.headers.cookie !== undefined) { const cookie = parseCookie(req.headers.cookie); diff --git a/src/trpc/router.ts b/src/trpc/router.ts new file mode 100644 index 00000000..676da63b --- /dev/null +++ b/src/trpc/router.ts @@ -0,0 +1,10 @@ +import { chatRouter } from "./routes/chat"; +import { t } from "./t"; + +export const router = t.router({ + chat: chatRouter, +}); + +export type Router = typeof router; + +// console.log(router._def._config.transformer); diff --git a/src/backend/router.ts b/src/trpc/routes/chat.ts similarity index 93% rename from src/backend/router.ts rename to src/trpc/routes/chat.ts index 608a74e1..8982426b 100644 --- a/src/backend/router.ts +++ b/src/trpc/routes/chat.ts @@ -1,16 +1,13 @@ -import type { IChannel, IChannelSafe, SocketMessage } from "../types"; -import { Channel } from "../models/channel"; -import { ChatMessage } from "../models/chatmessage"; -import { ChatMute } from "../models/chatmute"; -import type { Context } from "./context"; +import type { IChannel, IChannelSafe, SocketMessage } from "../../types"; +import { Channel } from "../../models/channel"; +import { ChatMessage } from "../../models/chatmessage"; +import { ChatMute } from "../../models/chatmute"; import type { HydratedDocument } from "mongoose"; -import SuperJSON from "superjson"; -import { convertFormat } from "../libgalaxy"; -import { createContext } from "./context"; -import { findUsers } from "../helper/users"; -import { initTRPC } from "@trpc/server"; +import { convertFormat } from "../../libgalaxy"; +import { findUsers } from "../../helper/users"; import { observable } from "@trpc/server/observable"; -import sendWebhook from "../helper/sendWebhook"; +import sendWebhook from "../../helper/sendWebhook"; +import { t } from "../t"; import { z } from "zod"; type UserData = { @@ -35,10 +32,6 @@ function channelSanityFilter(channel: ChannelData, user: string) { channel.users = channel.users.filter(u => u !== user); } -const t = initTRPC.context().create({ - transformer: SuperJSON, -}); - const channelNotFound = observable(e => { e.next({ type: "info", channel: false }); }); @@ -53,7 +46,7 @@ function userIsStaff(info: IChannel, id: number): boolean { } // TODO eughhhhhhhhhhhhh. -export const socketRouter = t.router({ +export const chatRouter = t.router({ onSend: t.procedure .input( z.object({ @@ -434,10 +427,6 @@ function safeInfo(c: IChannel): IChannelSafe { }; } -export type SocketRouter = typeof socketRouter; -export const socketContext = createContext; -// console.log(router._def._config.transformer); - async function sendOldMessages( input: { channel: string }, onAdd: (data: SocketMessage) => void diff --git a/src/trpc/t.ts b/src/trpc/t.ts new file mode 100644 index 00000000..d9938c16 --- /dev/null +++ b/src/trpc/t.ts @@ -0,0 +1,7 @@ +import type { Context } from "./context"; +import SuperJSON from "superjson"; +import { initTRPC } from "@trpc/server"; + +export const t = initTRPC.context().create({ + transformer: SuperJSON, +}); diff --git a/ws.js b/ws.js index 8b083b54..c8300e63 100644 --- a/ws.js +++ b/ws.js @@ -1,4 +1,4 @@ -import { socketContext, socketRouter } from "./prod-ws/router.js"; +import { socketContext, socketRouter } from "./prod-ws/wsRouter.js"; import { WebSocketServer } from "ws"; import { applyWSSHandler } from "@trpc/server/adapters/ws"; -- GitLab From df7c53e247b159b47bc6ce4f749d58961a60c434 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Thu, 5 Oct 2023 21:34:28 -0400 Subject: [PATCH 2/8] add notif count update api via trpc --- src/components/Header.astro | 30 ++---------------- src/components/NotifCount.svelte | 37 +++++++++++++++++++++++ src/pages/api/messages/send.ts | 8 +++++ src/trpc/client.ts | 25 +++++++++------ src/trpc/router.ts | 2 ++ src/trpc/routes/notif.ts | 52 ++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 src/components/NotifCount.svelte create mode 100644 src/trpc/routes/notif.ts diff --git a/src/components/Header.astro b/src/components/Header.astro index c1aba4db..53400e51 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -11,6 +11,7 @@ import { getLevel, getXP, getXPToNextLevel } from "../helper/leveling"; import type { IUser } from "../types"; import Icon from "@iconify/svelte"; import { Message } from "../models/message"; +import NotifCount from "./NotifCount.svelte"; import Signature from "./Signature.svelte"; import { convertFormat } from "../libgalaxy"; @@ -226,11 +227,7 @@ const xpToNext = getXPToNextLevel(level);
{/* prettier-ignore */} - {mcount !== "0" && ( - - {mcount} - - )}{username}{username}me view profile {/* prettier-ignore */} - mailbox{ - mcount !== "0" && ( - {mcount} - )} - + mailbox

preferences

my account @@ -917,23 +910,6 @@ const xpToNext = getXPToNextLevel(level); border-radius: 0 3px 0 0; } - .notif { - height: 24px; - min-width: 24px; - background: var(--background-red-1); - display: inline-block; - text-align: center; - border-radius: 25px; - line-height: 24px; - color: #eee; - } - .notif:not(:first-child) { - margin-left: 8px; - } - .notif:not(:last-child) { - margin-right: 8px; - } - #mobile-menu-button { display: none; position: relative; diff --git a/src/components/NotifCount.svelte b/src/components/NotifCount.svelte new file mode 100644 index 00000000..3a509864 --- /dev/null +++ b/src/components/NotifCount.svelte @@ -0,0 +1,37 @@ + + +{#if count !== 0} + {count} +{/if} + + diff --git a/src/pages/api/messages/send.ts b/src/pages/api/messages/send.ts index a9e8a391..a9a75c67 100644 --- a/src/pages/api/messages/send.ts +++ b/src/pages/api/messages/send.ts @@ -1,6 +1,7 @@ import { byLockdown, route } from "../../../helper/route"; import { Message } from "../../../models/message"; import { User } from "../../../models/user"; +import { postNotifCount } from "../../../trpc/routes/notif"; import { rmsg } from "../../../helper/res"; import { z } from "zod"; @@ -35,6 +36,13 @@ export const post = route( }); await msg.save(); + const notifCount = await Message.countDocuments({ + to: to.id, + read: false, + deleted: false, + }); + postNotifCount(to.id, notifCount); + return rmsg("success"); } ); diff --git a/src/trpc/client.ts b/src/trpc/client.ts index 9b3f106a..17e01701 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -4,15 +4,22 @@ import SuperJSON from "superjson"; export const client = createTRPCProxyClient({ transformer: SuperJSON, - links: [ - wsLink({ - client: createWSClient({ - url: `${ - window.location.protocol === "https:" ? "wss" : "ws" - }://${location.hostname}:8443/api/trpc`, - }), - }), - ], + // Only create a websocket link in the browser. + links: + typeof window !== "undefined" + ? [ + wsLink({ + client: createWSClient({ + url: `${ + window.location.protocol === "https:" + ? "wss" + : "ws" + }://${location.hostname}:8443/api/trpc`, + }), + }), + ] + : [], }); export const chat = client.chat; +export const notif = client.notif; diff --git a/src/trpc/router.ts b/src/trpc/router.ts index 676da63b..2e93f971 100644 --- a/src/trpc/router.ts +++ b/src/trpc/router.ts @@ -1,8 +1,10 @@ import { chatRouter } from "./routes/chat"; +import { notifRouter } from "./routes/notif"; import { t } from "./t"; export const router = t.router({ chat: chatRouter, + notif: notifRouter, }); export type Router = typeof router; diff --git a/src/trpc/routes/notif.ts b/src/trpc/routes/notif.ts new file mode 100644 index 00000000..96d8581d --- /dev/null +++ b/src/trpc/routes/notif.ts @@ -0,0 +1,52 @@ +import { TRPCError } from "@trpc/server"; +import { observable } from "@trpc/server/observable"; +import { t } from "../t"; + +type Sender = (notifCount: number) => void; + +// user id -> list of connections +const users: Map = new Map(); + +export const notifRouter = t.router({ + onUpdate: t.procedure.subscription(async ({ ctx }) => { + const user = ctx.user; + if (!user) { + throw new TRPCError({ + code: "UNAUTHORIZED", + }); + } + + return observable(emit => { + const onUpdate = (notifCount: number) => { + emit.next(notifCount); + }; + + const userEntry = users.get(user.id); + if (userEntry) { + userEntry.push(onUpdate); + } else { + users.set(user.id, [onUpdate]); + } + + return () => { + const userEntry = users.get(user.id); + if (userEntry) { + if (userEntry.length) { + users.set( + user.id, + userEntry.filter(send => send !== onUpdate) + ); + } else { + users.delete(user.id); + } + } + }; + }); + }), +}); + +// broadcasts a new notification count to each connection associated with the given user +export function postNotifCount(userId: number, notifCount: number) { + const userEntry = users.get(userId); + userEntry?.forEach(send => send(notifCount)); +} -- GitLab From 46cc825f09705f08a99499e943565bab84610ed2 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Thu, 5 Oct 2023 21:37:32 -0400 Subject: [PATCH 3/8] fix astroshuttle oversight (hardcoded socketRouter/socketContext names) --- src/backend/wsDevServer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/wsDevServer.ts b/src/backend/wsDevServer.ts index 3013e150..dd98ae0e 100644 --- a/src/backend/wsDevServer.ts +++ b/src/backend/wsDevServer.ts @@ -110,8 +110,8 @@ const config: (cfg: WSConfig) => AstroIntegration = cfg => ({ errorLog("Error on RPC-Router WebSocket."); }, wss, - router: router.socketRouter as AnyRouter, - createContext: router.socketContext, + router: router[cfg.routerName] as AnyRouter, + createContext: router[cfg.createContextName], }); infoLog("RPC-Router module loaded."); -- GitLab From c3a5ba116f7618f07063cafee81a634e4612358a Mon Sep 17 00:00:00 2001 From: wackbyte Date: Thu, 5 Oct 2023 21:47:15 -0400 Subject: [PATCH 4/8] helper for counting notifications; broadcast notif count on message read --- src/components/Header.astro | 8 ++------ src/helper/notif.ts | 9 +++++++++ src/pages/api/messages/read.ts | 5 +++++ src/pages/api/messages/readAll.ts | 5 +++++ src/pages/api/messages/send.ts | 7 ++----- 5 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 src/helper/notif.ts diff --git a/src/components/Header.astro b/src/components/Header.astro index 53400e51..66e3109f 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -10,10 +10,10 @@ export interface Props { import { getLevel, getXP, getXPToNextLevel } from "../helper/leveling"; import type { IUser } from "../types"; import Icon from "@iconify/svelte"; -import { Message } from "../models/message"; import NotifCount from "./NotifCount.svelte"; import Signature from "./Signature.svelte"; import { convertFormat } from "../libgalaxy"; +import { getNotifCount } from "../helper/notif"; let loggedIn = false; let username = "Anonymous"; @@ -55,11 +55,7 @@ if (diffUser.loggedIn || diffUser.needsVerification) { let mcount: string | number = "0"; if (diffUser.loggedIn) { - mcount = await Message.countDocuments({ - to: userId, - read: false, - deleted: false, - }); + mcount = await getNotifCount(userId); mcount = mcount.toLocaleString(); } diff --git a/src/helper/notif.ts b/src/helper/notif.ts new file mode 100644 index 00000000..472d9587 --- /dev/null +++ b/src/helper/notif.ts @@ -0,0 +1,9 @@ +import { Message } from "../models/message"; + +export async function getNotifCount(userId: number) { + return await Message.countDocuments({ + to: userId, + read: false, + deleted: false, + }); +} diff --git a/src/pages/api/messages/read.ts b/src/pages/api/messages/read.ts index 5b6390a8..6830e7a7 100644 --- a/src/pages/api/messages/read.ts +++ b/src/pages/api/messages/read.ts @@ -1,5 +1,7 @@ import { rdata, rmsg } from "../../../helper/res"; import { Message } from "../../../models/message"; +import { getNotifCount } from "../../../helper/notif"; +import { postNotifCount } from "../../../trpc/routes/notif"; import { route } from "../../../helper/route"; export const get = route( @@ -20,6 +22,9 @@ export const get = route( msg.read = true; await msg.save(); + const notifCount = await getNotifCount(user.id); + postNotifCount(user.id, notifCount); + return rdata({ msg }); } ); diff --git a/src/pages/api/messages/readAll.ts b/src/pages/api/messages/readAll.ts index c1c23b0a..f6c1a2fc 100644 --- a/src/pages/api/messages/readAll.ts +++ b/src/pages/api/messages/readAll.ts @@ -1,4 +1,6 @@ import { Message } from "../../../models/message"; +import { getNotifCount } from "../../../helper/notif"; +import { postNotifCount } from "../../../trpc/routes/notif"; import { rmsg } from "../../../helper/res"; import { route } from "../../../helper/route"; @@ -14,6 +16,9 @@ export const get = route( { read: true } ); + const notifCount = await getNotifCount(user.id); + postNotifCount(user.id, notifCount); + return rmsg("done"); } ); diff --git a/src/pages/api/messages/send.ts b/src/pages/api/messages/send.ts index a9a75c67..d67f6a4d 100644 --- a/src/pages/api/messages/send.ts +++ b/src/pages/api/messages/send.ts @@ -1,6 +1,7 @@ import { byLockdown, route } from "../../../helper/route"; import { Message } from "../../../models/message"; import { User } from "../../../models/user"; +import { getNotifCount } from "../../../helper/notif"; import { postNotifCount } from "../../../trpc/routes/notif"; import { rmsg } from "../../../helper/res"; import { z } from "zod"; @@ -36,11 +37,7 @@ export const post = route( }); await msg.save(); - const notifCount = await Message.countDocuments({ - to: to.id, - read: false, - deleted: false, - }); + const notifCount = await getNotifCount(to.id); postNotifCount(to.id, notifCount); return rmsg("success"); -- GitLab From 7a84f6edfc6ddada652119083d257d72bbcb1ee1 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Thu, 5 Oct 2023 21:54:23 -0400 Subject: [PATCH 5/8] fix rendering of 0 notifs --- src/components/Header.astro | 3 +-- src/components/NotifCount.svelte | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/Header.astro b/src/components/Header.astro index 66e3109f..ab58f850 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -53,10 +53,9 @@ if (diffUser.loggedIn || diffUser.needsVerification) { pfpTimestamp = diffUser.user.pfpTimestamp; } -let mcount: string | number = "0"; +let mcount = 0; if (diffUser.loggedIn) { mcount = await getNotifCount(userId); - mcount = mcount.toLocaleString(); } const xp = user?.user ? getXP(user.user) : 0; diff --git a/src/components/NotifCount.svelte b/src/components/NotifCount.svelte index 3a509864..8b8da628 100644 --- a/src/components/NotifCount.svelte +++ b/src/components/NotifCount.svelte @@ -14,7 +14,7 @@ {#if count !== 0} - {count} + {count.toLocaleString()} {/if} diff --git a/src/components/NotifCount.svelte b/src/components/NotifCount.svelte deleted file mode 100644 index 8b8da628..00000000 --- a/src/components/NotifCount.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - -{#if count !== 0} - {count.toLocaleString()} -{/if} - - -- GitLab From 8516f3e0abd0cbd12addae71c1a805671e1f1d34 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Thu, 5 Oct 2023 22:25:27 -0400 Subject: [PATCH 7/8] don't update notif counts from the Inbox component --- src/components/Inbox.svelte | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/Inbox.svelte b/src/components/Inbox.svelte index b6bc0ab9..f35410ba 100644 --- a/src/components/Inbox.svelte +++ b/src/components/Inbox.svelte @@ -37,15 +37,6 @@ } }); } - - const mcount = messages.reduce((n, m) => (n += !m.read), 0); - if (mcount === 0) { - [...document.querySelectorAll(".notif")].forEach(e => e.remove()); - } else { - [...document.querySelectorAll(".notif")].forEach( - e => (e.textContent = mcount.toLocaleString()) - ); - } } async function send() { -- GitLab From 63aed6e43c5b39baef41f3cb9d82dd9fe08a3090 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Fri, 6 Oct 2023 19:38:28 -0400 Subject: [PATCH 8/8] revert trpc client ssr fix (unnecessary due to svelte -> astro) --- src/trpc/client.ts | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/trpc/client.ts b/src/trpc/client.ts index 17e01701..c6d15093 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -4,21 +4,15 @@ import SuperJSON from "superjson"; export const client = createTRPCProxyClient({ transformer: SuperJSON, - // Only create a websocket link in the browser. - links: - typeof window !== "undefined" - ? [ - wsLink({ - client: createWSClient({ - url: `${ - window.location.protocol === "https:" - ? "wss" - : "ws" - }://${location.hostname}:8443/api/trpc`, - }), - }), - ] - : [], + links: [ + wsLink({ + client: createWSClient({ + url: `${ + window.location.protocol === "https:" ? "wss" : "ws" + }://${location.hostname}:8443/api/trpc`, + }), + }), + ], }); export const chat = client.chat; -- GitLab