diff --git a/README.md b/README.md index c440c36..ae8d0a2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A scrum poker tool that helps agile teams plan their sprints in real-time. ## Stack - Front-end framework: Nextjs -- Front-end library: Preact +- Front-end library: React - Rendering method: SSR SPA - Hosting: Vercel - Real-time pub/sub: Ably diff --git a/next.config.mjs b/next.config.mjs index da54605..ea8dac8 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -12,17 +12,6 @@ const config = { images: { domains: ["avatars.githubusercontent.com", "lh3.googleusercontent.com"], }, - webpack: (config, { dev, isServer }) => { - if (!dev && !isServer) { - Object.assign(config.resolve.alias, { - "react/jsx-runtime.js": "preact/compat/jsx-runtime", - react: "preact/compat", - "react-dom/test-utils": "preact/test-utils", - "react-dom": "preact/compat", - }); - } - return config; - }, }; export default config; diff --git a/package.json b/package.json index d1afb61..c91fe55 100644 --- a/package.json +++ b/package.json @@ -23,14 +23,14 @@ "@trpc/server": "10.35.0", "@upstash/ratelimit": "^0.4.3", "@upstash/redis": "^1.22.0", - "ably": "^1.2.41", + "ably": "^1.2.42", "autoprefixer": "^10.4.14", "json2csv": "6.0.0-alpha.2", "next": "^13.4.12", "next-auth": "^4.22.3", "postcss": "^8.4.27", - "preact": "^10.16.0", "react": "18.2.0", + "react-dom": "18.2.0", "react-email": "^1.9.4", "react-icons": "^4.10.1", "resend": "^0.17.1", @@ -39,7 +39,7 @@ "zod": "^3.21.4" }, "devDependencies": { - "@types/eslint": "^8.44.0", + "@types/eslint": "^8.44.1", "@types/json2csv": "^5.0.3", "@types/node": "^20.4.4", "@types/react": "^18.2.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04123fd..b587c97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,8 +39,8 @@ dependencies: specifier: ^1.22.0 version: 1.22.0 ably: - specifier: ^1.2.41 - version: 1.2.41 + specifier: ^1.2.42 + version: 1.2.42 autoprefixer: specifier: ^10.4.14 version: 10.4.14(postcss@8.4.27) @@ -56,12 +56,12 @@ dependencies: postcss: specifier: ^8.4.27 version: 8.4.27 - preact: - specifier: ^10.16.0 - version: 10.16.0 react: specifier: 18.2.0 version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) react-email: specifier: ^1.9.4 version: 1.9.4 @@ -83,8 +83,8 @@ dependencies: devDependencies: '@types/eslint': - specifier: ^8.44.0 - version: 8.44.0 + specifier: ^8.44.1 + version: 8.44.1 '@types/json2csv': specifier: ^5.0.3 version: 5.0.3 @@ -132,7 +132,7 @@ packages: react: '>=18.1.0' react-dom: '>=18.1.0' dependencies: - ably: 1.2.41 + ably: 1.2.42 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -1087,8 +1087,8 @@ packages: '@types/responselike': 1.0.0 dev: false - /@types/eslint@8.44.0: - resolution: {integrity: sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw==} + /@types/eslint@8.44.1: + resolution: {integrity: sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==} dependencies: '@types/estree': 1.0.1 '@types/json-schema': 7.0.12 @@ -1382,8 +1382,8 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: false - /ably@1.2.41: - resolution: {integrity: sha512-TE3PjtrclXFjfIALQhuTd1bEL7EAlS28AGFLaILeXmecXnBQE37WoC2EKZJIX7jRX31bwTHxUouje3WBEnhNKQ==} + /ably@1.2.42: + resolution: {integrity: sha512-dUnza7cERLWaDa/2pLVXtU2PJoU5k/t6g9sQZI1dgWC5Vok39nE6tf/xH2Rat7PJs7pXl9hLpkg1AhS4Xwd2/w==} engines: {node: '>=5.10.x'} dependencies: '@ably/msgpack-js': 0.4.0 @@ -1686,7 +1686,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001517 - electron-to-chromium: 1.4.469 + electron-to-chromium: 1.4.470 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: false @@ -2169,8 +2169,8 @@ packages: semver: 7.5.4 dev: false - /electron-to-chromium@1.4.469: - resolution: {integrity: sha512-HRN9XQjElxJBrdDky5iiUUr3eDwXGTg6Cp4IV8MuNc8VqMkYSneSnIe6poFKx9PsNzkudCgaWCBVxwDqirwQWQ==} + /electron-to-chromium@1.4.470: + resolution: {integrity: sha512-zZM48Lmy2FKWgqyvsX9XK+J6FfP7aCDUFLmgooLJzA7v1agCs/sxSoBpTIwDLhmbhpx9yJIxj2INig/ncjJRqg==} dev: false /emoji-regex@9.2.2: diff --git a/src/server/ably.ts b/src/server/ably.ts index f83c500..986581e 100644 --- a/src/server/ably.ts +++ b/src/server/ably.ts @@ -2,12 +2,16 @@ import Ably from "ably"; import { env } from "~/env.mjs"; import type { EventType } from "../utils/types"; -export const publishToChannel = async ( - ablyInstance: Ably.Types.RealtimePromise, +const ablyRest = new Ably.Rest(env.ABLY_PRIVATE_KEY); + +export const publishToChannel = ( channel: string, event: EventType, message: string ) => { - const channelName = ablyInstance.channels.get(`${env.APP_ENV}-${channel}`); - await channelName.publish(event, message); + try { + ablyRest.channels.get(`${env.APP_ENV}-${channel}`).publish(event, message); + } catch (error) { + console.log(`❌❌❌ Failed to send message!`); + } }; diff --git a/src/server/api/createTokenRequest.ts b/src/server/api/createTokenRequest.ts new file mode 100644 index 0000000..0de7824 --- /dev/null +++ b/src/server/api/createTokenRequest.ts @@ -0,0 +1,25 @@ +import Ably from "ably/promises"; +import { NextApiRequest, NextApiResponse } from "next"; +import { env } from "~/env.mjs"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../auth"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const session = await getServerSession(req, res, authOptions); + + if (session) { + // Signed in + const client = new Ably.Realtime(env.ABLY_PRIVATE_KEY); + + const tokenRequestData = await client.auth.createTokenRequest({ + clientId: session.user.id, + }); + res.status(200).json(tokenRequestData); + } else { + // Not Signed in + res.status(401); + } +} diff --git a/src/server/api/routers/room.ts b/src/server/api/routers/room.ts index 33017dc..d978278 100644 --- a/src/server/api/routers/room.ts +++ b/src/server/api/routers/room.ts @@ -27,8 +27,7 @@ export const roomRouter = createTRPCRouter({ await invalidateCache(`kv_roomcount_admin`); await invalidateCache(`kv_roomlist_${ctx.session.user.id}`); - await publishToChannel( - ctx.ably, + publishToChannel( `${ctx.session.user.id}`, "ROOM_LIST_UPDATE", "CREATE" @@ -202,12 +201,7 @@ export const roomRouter = createTRPCRouter({ }); if (newRoom) { - await publishToChannel( - ctx.ably, - `${newRoom.id}`, - "ROOM_UPDATE", - "UPDATE" - ); + publishToChannel(`${newRoom.id}`, "ROOM_UPDATE", "UPDATE"); } return !!newRoom; @@ -228,19 +222,13 @@ export const roomRouter = createTRPCRouter({ await invalidateCache(`kv_votecount_admin`); await invalidateCache(`kv_roomlist_${ctx.session.user.id}`); - await publishToChannel( - ctx.ably, + publishToChannel( `${ctx.session.user.id}`, "ROOM_LIST_UPDATE", "DELETE" ); - await publishToChannel( - ctx.ably, - `${deletedRoom.id}`, - "ROOM_UPDATE", - "DELETE" - ); + publishToChannel(`${deletedRoom.id}`, "ROOM_UPDATE", "DELETE"); } return !!deletedRoom; diff --git a/src/server/api/routers/vote.ts b/src/server/api/routers/vote.ts index 7fd3df4..6f48387 100644 --- a/src/server/api/routers/vote.ts +++ b/src/server/api/routers/vote.ts @@ -100,12 +100,7 @@ export const voteRouter = createTRPCRouter({ await invalidateCache(`kv_votecount_admin`); await invalidateCache(`kv_votes_${input.roomId}`); - await publishToChannel( - ctx.ably, - `${vote.roomId}`, - "VOTE_UPDATE", - "UPDATE" - ); + publishToChannel(`${vote.roomId}`, "VOTE_UPDATE", "UPDATE"); } return !!vote; diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts index a727b91..48e063b 100644 --- a/src/server/api/trpc.ts +++ b/src/server/api/trpc.ts @@ -19,7 +19,6 @@ import { type Session } from "next-auth"; import { getServerAuthSession } from "~/server/auth"; import { prisma } from "~/server/db"; -import Ably from "ably"; type CreateContextOptions = { session: Session | null; @@ -38,7 +37,6 @@ type CreateContextOptions = { const createInnerTRPCContext = (opts: CreateContextOptions) => { return { session: opts.session, - ably: new Ably.Realtime.Promise(env.ABLY_PRIVATE_KEY), prisma, }; }; @@ -108,33 +106,26 @@ const enforceRouteProtection = t.middleware(async ({ ctx, next }) => { throw new TRPCError({ code: "UNAUTHORIZED" }); } - try { - const rateLimit = new Ratelimit({ - redis: Redis.fromEnv(), - limiter: Ratelimit.slidingWindow( - Number(env.UPSTASH_RATELIMIT_REQUESTS), - `${Number(env.UPSTASH_RATELIMIT_SECONDS)}s` - ), - analytics: true, - }); + const rateLimit = new Ratelimit({ + redis: Redis.fromEnv(), + limiter: Ratelimit.slidingWindow( + Number(env.UPSTASH_RATELIMIT_REQUESTS), + `${Number(env.UPSTASH_RATELIMIT_SECONDS)}s` + ), + analytics: true, + }); - const { success } = await rateLimit.limit( - `${env.APP_ENV}_${ctx.session.user.id}` - ); - if (!success) throw new TRPCError({ code: "TOO_MANY_REQUESTS" }); + const { success } = await rateLimit.limit( + `${env.APP_ENV}_${ctx.session.user.id}` + ); + console.log(success); + if (!success) throw new TRPCError({ code: "TOO_MANY_REQUESTS" }); - return next({ - ctx: { - session: { ...ctx.session, user: ctx.session.user }, - }, - }); - } catch { - return next({ - ctx: { - session: { ...ctx.session, user: ctx.session.user }, - }, - }); - } + return next({ + ctx: { + session: { ...ctx.session, user: ctx.session.user }, + }, + }); }); /**