Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
43239e9
added recaptcha detection text
Dec 6, 2023
732ea84
added home button to menu dialog for ease of navigation
Dec 6, 2023
c7e3646
added recaptcha provider, hooks and utils
Dec 6, 2023
86b76c0
added types, service hooks and recaptcha server functions
Dec 6, 2023
f0a459d
pnp files
Dec 6, 2023
ac0d485
Merge remote-tracking branch 'origin/main' into feature/recaptcha
Dec 6, 2023
c9b93e2
pnp files
Dec 6, 2023
6013661
fix: minor yarn build issue
Dec 7, 2023
b59bc29
updated env file
Dec 7, 2023
9cf860f
added yarn cache and hide recaptcha logo
Dec 7, 2023
e8c8b15
Revert "pnp files"
Dec 7, 2023
1273af6
added zod based types for recaptcha response
Dec 7, 2023
4e7929d
moved and renamed files
Dec 7, 2023
e8b9d8e
code refactor based on review comments
Dec 7, 2023
ff83345
added recaptcha secret to deployment staging
Dec 7, 2023
d3d9ce4
code refactor based on comment reviews
Dec 7, 2023
b63e4a0
refactor: removed recaptcha provider
Dec 11, 2023
738a8bf
refactor: updated recaptcha types and removed eslint ignores
Dec 11, 2023
d404c53
refactor: updated recaptcha to use a hook instead of a context
Dec 11, 2023
575e954
refactor: removed grecaptcha types
Dec 11, 2023
5b58500
refactor: yarn.lock
Dec 11, 2023
58b4746
Merge remote-tracking branch 'origin/main' into feature/recaptcha
Dec 15, 2023
7c642c2
updated based on review
Dec 15, 2023
0a35010
updated based on review comments
Dec 15, 2023
29ff6e0
updated based on review comments
Dec 15, 2023
e9d1605
updated based on review comments
Dec 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ APP_URL="http://localhost:3000"
NEXT_PUBLIC_WS_URL="ws://localhost:3001"
NEXT_PUBLIC_MOCK_AUTH="false"
PLAYERS_PER_MATCH="2"
NEXT_PUBLIC_RECAPTCHA_SITE_KEY="add-recaptcha-site-key-here"
RECAPTCHA_SECRET_KEY="add-recaptcha-secret-key-here"

HUGGING_FACE_TOKEN="<your_token>"
511 changes: 15 additions & 496 deletions .pnp.cjs

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion deployment/staging/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,11 @@ spec:
- name: NEXT_PUBLIC_WS_URL
value: wss://bot-busters.staging.kryha.dev
- name: PLAYERS_PER_MATCH
# TODO: set to correct value
value: "3"
- name: NEXT_PUBLIC_RECAPTCHA_SITE_KEY
Comment thread
privilegemendes marked this conversation as resolved.
value: "6LdpdiEpAAAAAJ6HKbT9Dmax-Li-SipyWdTcV0qW"
- name: RECAPTCHA_SECRET_KEY
valueFrom:
secretKeyRef:
name: backend-secrets
key: recaptcha_secret_key
1 change: 1 addition & 0 deletions deployment/staging/secrets.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ data:
db_password: $DB_PASSWORD
nextauth_secret: $NEXTAUTH_SECRET
hugging_face_token: $HUGGING_FACE_TOKEN
recaptcha_secret_key: $RECAPTCHA_SECRET_KEY
1 change: 1 addition & 0 deletions src/assets/text/homepage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const homepage = {
"The daily top 100 gets Aleo credits transferred to their wallets. Start playing now!",
topRanked: (position: number, username: string, score: number) =>
`${position + 1}. ${username} ${score} points`,
botDetected: "Recaptcha detected a bot. Please try again.",
};
15 changes: 13 additions & 2 deletions src/components/menu-dialog/menu-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import { Button, Dialog, Slide, Stack } from "@mui/material";
import { type TransitionProps } from "@mui/material/transitions";

import { text } from "~/assets/text/index.js";

import { styles } from "./styles.js";
import { Footer } from "./footer.jsx";
import { MenuOptions } from "./menu-options.jsx";
import { useRouter } from "next/router";
import { pages } from "~/router.js";

const Transition = forwardRef(function Transition(
props: TransitionProps & {
children: ReactElement;
},
ref: Ref<unknown>
ref: Ref<unknown>,
) {
return <Slide direction="left" ref={ref} {...props} />;
});
Expand All @@ -24,6 +25,13 @@ interface Props {
}

export const MenuDialog: FC<Props> = ({ open, setOpen }) => {
const { push } = useRouter();

const goHome = () => {
setOpen(false);
void push(pages.home);
};
Comment thread
privilegemendes marked this conversation as resolved.

const handleClose = () => {
setOpen(false);
};
Expand All @@ -37,6 +45,9 @@ export const MenuDialog: FC<Props> = ({ open, setOpen }) => {
sx={styles.dialog}
>
<Stack sx={styles.buttonWrapper}>
<Button variant="contained" sx={styles.button} onClick={goHome}>
Comment thread
privilegemendes marked this conversation as resolved.
{text.general.home}
</Button>
<Button variant="contained" sx={styles.button} onClick={handleClose}>
{text.general.close}
</Button>
Expand Down
3 changes: 3 additions & 0 deletions src/components/menu-dialog/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ export const styles = {
},
buttonWrapper: {
alignItems: "flex-end",
flexDirection: "row",
justifyContent: "space-between",
pt: 3,
pr: 3,
pl: 3,
width: "100vw",
},
button: {
Expand Down
39 changes: 33 additions & 6 deletions src/containers/home/index.tsx → src/containers/home/home.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import { Button, Stack, Typography } from "@mui/material";
import React from "react";
import React, { useState } from "react";
import { signIn, useSession } from "next-auth/react";
import { useRouter } from "next/router.js";

import { text } from "~/assets/text/index.js";
import { useRecaptcha } from "~/service/index.js";
import { TopRanked } from "~/components/index.js";
import { isValidSession } from "~/utils/session.js";
import Script from "next/script";
import { api } from "~/utils/api.js";
import { text } from "~/assets/text/index.js";
import { pages } from "~/router.js";
import { TOP_RANKED_PLAYERS } from "~/constants/index.js";
import { isValidSession } from "~/utils/session.js";

import { styles } from "./styles.js";

export const Homepage = () => {
const { push } = useRouter();
const [isRecaptchaFailed, setIsRecaptchaFailed] = useState<boolean>(false);
const verifyCaptcha = api.recaptcha.verify.useMutation();
const join = api.lobby.join.useMutation();
const { data: sessionData } = useSession();
const { scriptProps, executeRecaptcha, ...props } = useRecaptcha();

const handleGameStart = async () => {
const captchaToken = await executeRecaptcha("start_game");
Comment thread
privilegemendes marked this conversation as resolved.

try {
if (!isValidSession(sessionData)) {
await signIn("credentials", { callbackUrl: pages.lobby });
const result = await verifyCaptcha.mutateAsync({ captchaToken });
if (!result) {
await signIn("credentials", {
callbackUrl: pages.lobby,
});
} else {
setIsRecaptchaFailed(true);
return;
}
} else {
await push(pages.lobby);
void push(pages.lobby);
}
} catch (error) {
console.error(error);
Expand All @@ -38,6 +52,11 @@ export const Homepage = () => {
<Stack sx={styles.description}>
<Typography variant="h5">{text.homepage.descriptionPart1}</Typography>
<Typography variant="h5">{text.homepage.descriptionPart2}</Typography>
{isRecaptchaFailed && (
<Typography variant="h6" color="error">
{text.homepage.botDetected}
</Typography>
)}
</Stack>
<Stack sx={styles.actions}>
<Button
Expand All @@ -61,6 +80,14 @@ export const Homepage = () => {
</Button>
</Stack>
<TopRanked players={TOP_RANKED_PLAYERS} />
<Script
id={scriptProps.id}
src={scriptProps.src}
strategy={scriptProps.strategy}
onLoad={scriptProps.onLoad}
onError={scriptProps.onError}
{...props}
/>
</Stack>
);
};
1 change: 1 addition & 0 deletions src/containers/home/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./home.jsx";
4 changes: 4 additions & 0 deletions src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const env = createEnv({
.default("5")
.transform((val) => Number(val)),
HUGGING_FACE_TOKEN: z.string(),
RECAPTCHA_SECRET_KEY: z.string().min(1),
Comment thread
privilegemendes marked this conversation as resolved.
},

/**
Expand All @@ -39,6 +40,7 @@ export const env = createEnv({
.enum(["true", "false"])
.default("false")
.transform((val) => (val === "true" ? true : false)),
NEXT_PUBLIC_RECAPTCHA_SITE_KEY: z.string().min(1),
Comment thread
privilegemendes marked this conversation as resolved.
},

/**
Expand All @@ -57,6 +59,8 @@ export const env = createEnv({
NEXT_PUBLIC_MOCK_AUTH: process.env.NEXT_PUBLIC_MOCK_AUTH,
PLAYERS_PER_MATCH: process.env.PLAYERS_PER_MATCH,
HUGGING_FACE_TOKEN: process.env.HUGGING_FACE_TOKEN,
NEXT_PUBLIC_RECAPTCHA_SITE_KEY: process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY,
RECAPTCHA_SECRET_KEY: process.env.RECAPTCHA_SECRET_KEY,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
Expand Down
2 changes: 1 addition & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Homepage } from "~/containers/home/index.jsx";
import { Homepage } from "~/containers/home/index.js";

export default function Home() {
return <Homepage />;
Expand Down
8 changes: 7 additions & 1 deletion src/server/api/root.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { userRouter, matchRouter, lobbyRouter } from "./routers/index.js";
import {
lobbyRouter,
matchRouter,
recaptchaRouter,
userRouter,
} from "./routers/index.js";
import { createTRPCRouter } from "./trpc.js";

/**
Expand All @@ -10,6 +15,7 @@ export const appRouter = createTRPCRouter({
lobby: lobbyRouter,
match: matchRouter,
user: userRouter,
recaptcha: recaptchaRouter,
});

// export type definition of API
Expand Down
1 change: 1 addition & 0 deletions src/server/api/routers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./lobby.js";
export * from "./match.js";
export * from "./user.js";
export * from "./recaptcha.js";
55 changes: 55 additions & 0 deletions src/server/api/routers/recaptcha.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createTRPCRouter, protectedProcedure } from "../trpc.js";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { env } from "~/env.mjs";
import { type RecaptchaResponse } from "~/types/recaptcha";

const SCORE_THRESHOLD = 0.5;
export const recaptchaRouter = createTRPCRouter({
verify: protectedProcedure
.input(z.object({ captchaToken: z.string() }))
.mutation(async ({ input }) => {
const { captchaToken } = input;

// Check if it's (development mode)
if (env.NODE_ENV === "development") {
return false;
}

try {
// Verify the Google reCaptcha token v3
const response = await fetch(
"https://www.google.com/recaptcha/api/siteverify",
{
method: "POST",
headers: {
"Content-Type":
"application/x-www-form-urlencoded; charset=utf-8",
},
body: `secret=${env.RECAPTCHA_SECRET_KEY}&response=${captchaToken}`,
},
);

/**
* The structure of response from the verify API is
* {
* "success": true | false, // whether this request was a valid reCAPTCHA token for your site
* "challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
* "hostname": string, // the hostname of the site where the reCAPTCHA was solved
* "error-codes": [...] // optional
}
*/
const json = (await response.json()) as RecaptchaResponse;

if (!json.success) {
console.log(`Recaptcha token is not valid`);
throw new TRPCError({ code: "BAD_REQUEST" });
Comment thread
privilegemendes marked this conversation as resolved.
}

// If the score is below < SCORE_THRESHOLD, return true
return json.score < SCORE_THRESHOLD;
} catch (error) {
console.log(`Recaptcha cannot be verified`);
}
}),
});
1 change: 1 addition & 0 deletions src/service/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./profanity-filter/index.js";
export * from "./recaptcha/index.js";
1 change: 1 addition & 0 deletions src/service/recaptcha/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./recaptcha.js";
Loading