Skip to content

Commit

Permalink
feat: verification system #14
Browse files Browse the repository at this point in the history
  • Loading branch information
virtual-designer authored Aug 27, 2024
2 parents 698aa38 + 4c4e125 commit fd99cd4
Show file tree
Hide file tree
Showing 9 changed files with 405 additions and 37 deletions.
20 changes: 20 additions & 0 deletions src/api/verification/verification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getAxiosClient } from "@/client/axios";

type VerifyMemberPayload = {
guildId: string;
userId: string;
captchaToken: string;
token: string;
};

export const verifyMember = async (payload: VerifyMemberPayload) => {
const res = await getAxiosClient().post(
`/guilds/${encodeURIComponent(payload.guildId)}/members/${encodeURIComponent(payload.userId)}/verify`,
{
captchaToken: payload.captchaToken,
token: payload.token,
},
);

return res.data as { success: boolean; error?: string };
};
14 changes: 14 additions & 0 deletions src/app/(main)/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import HTTPErrorView from "@/components/Errors/HTTPErrorView";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "404 Not Found",
};

export default function NotFound() {
return (
<HTTPErrorView statusCode={404} statusText="Not Found">
The requested URL was not found on this server.
</HTTPErrorView>
);
}
111 changes: 83 additions & 28 deletions src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import FeatureCard from "@/components/Cards/FeatureCard";
import HeadingDivider from "@/components/Dividers/HeadingDivider";
import Footer from "@/components/Layout/Footer";
import { DOCS_URL, INVITE_REQUEST_URL } from "@/constants/links";
import activeDevelopmentImage from "@/images/active-development.png";
import autoModerationImage from "@/images/auto-moderation.png";
import backgroundImage from "@/images/background.svg";
Expand Down Expand Up @@ -76,21 +77,25 @@ export default function HomePage() {

<div className="items-center justify-center gap-2 py-5 md:flex md:pt-10 lg:py-20">
<Button
as="a"
startContent={<MdLink size={20} />}
size="lg"
color="primary"
variant="flat"
className="w-full md:w-auto"
href={INVITE_REQUEST_URL}
>
Invite SudoBot
</Button>

<Button
as="a"
endContent={<MdArrowForward size={20} />}
size="lg"
color="primary"
variant="flat"
className="mt-3 w-full md:mt-0 md:w-auto"
href={`${DOCS_URL}/getting-started`}
>
Set up manually
</Button>
Expand All @@ -99,21 +104,30 @@ export default function HomePage() {
</div>

<div className="mx-auto mb-5 max-w-[100svw] px-3 py-4 md:px-7 md:py-6 lg:px-20">
<h2 className="relative block text-center text-2xl lg:text-3xl xl:text-4xl">Why SudoBot?</h2>
<h2 className="relative block text-center text-2xl lg:text-3xl xl:text-4xl">
Why SudoBot?
</h2>

<HeadingDivider size={4} className="mt-4 h-[2px] bg-[#007bff] [background-image:none]" />
<HeadingDivider
size={4}
className="mt-4 h-[2px] bg-[#007bff] [background-image:none]"
/>
<Box pt="2rem" />

<div className="grid grid-cols-1 gap-5">
<div className="grid grid-cols-1 gap-5 md:grid-cols-[repeat(2,1fr)] lg:grid-cols-[repeat(3,1fr)] 2xl:grid-cols-[repeat(5,1fr)]">
<FeatureCard className="relative grid grid-rows-2 gap-5">
<div className="flex flex-col items-center justify-center p-3 text-center">
<HiOutlineBolt size="2rem" className="mx-auto mb-3 block text-[rgb(0,100,255)]" />
<HiOutlineBolt
size="2rem"
className="mx-auto mb-3 block text-[rgb(0,100,255)]"
/>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">
Manual Moderation
</h4>
<p className="text-[#444] dark:text-[#999]">
SudoBot provides a wide range of manual moderation tools to keep your server
SudoBot provides a wide range of manual
moderation tools to keep your server
safe and secure.
</p>
</div>
Expand All @@ -137,9 +151,12 @@ export default function HomePage() {
Smart Auto Moderation
</h4>
<p className="text-[#444] dark:text-[#999]">
SudoBot includes a powerful auto-moderation system that understands your
community&rsquo;s needs and can automatically moderate your server, so you
can focus on other things.
SudoBot includes a powerful
auto-moderation system that
understands your community&rsquo;s
needs and can automatically moderate
your server, so you can focus on
other things.
</p>
</div>
<div className="hidden lg:block">
Expand All @@ -150,9 +167,17 @@ export default function HomePage() {
"Message Scanning",
"Automatic Actions",
].map((feature) => (
<li key={feature} className="flex items-center gap-1">
<HiCheck size="1.2rem" className="text-blue-500" />
<span className="text-black dark:text-white">{feature}</span>
<li
key={feature}
className="flex items-center gap-1"
>
<HiCheck
size="1.2rem"
className="text-blue-500"
/>
<span className="text-black dark:text-white">
{feature}
</span>
</li>
))}
</ul>
Expand All @@ -169,14 +194,20 @@ export default function HomePage() {

<FeatureCard className="relative grid grid-rows-2 gap-5">
<div className="flex flex-col items-center justify-center p-3 text-center">
<HiCode size="2rem" className="mx-auto mb-3 block text-[rgb(0,100,255)]" />
<HiCode
size="2rem"
className="mx-auto mb-3 block text-[rgb(0,100,255)]"
/>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">
Free &amp; Open Source
</h4>
<p className="text-[#444] dark:text-[#999]">
SudoBot is free and open source, respecting your freedom. It is licensed under{" "}
SudoBot is free and open source,
respecting your freedom. It is licensed
under{" "}
<Link href="https://gnu.org/licenses/agpl-3.0.html">
GNU Affero General Public License v3.0
GNU Affero General Public License
v3.0
</Link>
.
</p>
Expand All @@ -200,7 +231,8 @@ export default function HomePage() {
Active Development
</h4>
<p className="text-[#444] dark:text-[#999]">
We are actively adding new features and fixing issues. We always welcome new
We are actively adding new features and
fixing issues. We always welcome new
feature requests or improvement ideas.
</p>
</div>
Expand All @@ -218,10 +250,14 @@ export default function HomePage() {
size="2rem"
className="mx-auto mb-3 block text-[rgb(0,100,255)]"
/>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">Self-Hosted</h4>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">
Self-Hosted
</h4>
<p className="text-[#444] dark:text-[#999]">
Don&rsquo;t want to host the bot yourself? We have a solution for that as well
&mdash; you can invite our self-hosted instance!
Don&rsquo;t want to host the bot
yourself? We have a solution for that as
well &mdash; you can invite our
self-hosted instance!
</p>
</div>
<div className="flex min-h-max flex-col items-center justify-center bg-white dark:bg-black">
Expand All @@ -235,13 +271,17 @@ export default function HomePage() {

<FeatureCard className="relative grid grid-rows-2 gap-5">
<div className="flex flex-col items-center justify-center p-3 text-center">
<HiOutlineCube size="2rem" className="mx-auto mb-3 block text-[rgb(0,100,255)]" />
<HiOutlineCube
size="2rem"
className="mx-auto mb-3 block text-[rgb(0,100,255)]"
/>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">
Highly Customizable
</h4>
<p className="text-[#444] dark:text-[#999]">
SudoBot&rsquo;s configuration system was built in a way so that you can
customize almost everything the bot does.
SudoBot&rsquo;s configuration system was
built in a way so that you can customize
almost everything the bot does.
</p>
</div>
<div className="flex min-h-max flex-col items-center justify-center bg-white dark:bg-black">
Expand All @@ -264,8 +304,11 @@ export default function HomePage() {
Robust Permission System
</h4>
<p className="text-[#444] dark:text-[#999]">
SudoBot uses Hybrid Permission System — you get to choose one of the three
possible modes. By default, it relies on Discord’s permission system.
SudoBot uses Hybrid Permission
System — you get to choose one of
the three possible modes. By
default, it relies on Discord’s
permission system.
</p>
</div>
<div className="hidden lg:block">
Expand All @@ -275,9 +318,17 @@ export default function HomePage() {
"Level-based Permission System",
"Overwrite-based Permission System",
].map((mode) => (
<li key={mode} className="flex items-center gap-1">
<HiCheck size="1.2rem" className="text-blue-500" />
<span className="text-black dark:text-white">{mode}</span>
<li
key={mode}
className="flex items-center gap-1"
>
<HiCheck
size="1.2rem"
className="text-blue-500"
/>
<span className="text-black dark:text-white">
{mode}
</span>
</li>
))}
</ul>
Expand All @@ -298,10 +349,14 @@ export default function HomePage() {
size="2rem"
className="mx-auto mb-3 block text-[rgb(0,100,255)]"
/>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">Secure</h4>
<h4 className="my-2 text-lg text-black dark:text-white lg:text-xl">
Secure
</h4>
<p className="text-[#444] dark:text-[#999]">
SudoBot is designed with security in mind. We take security seriously and are
committed to protecting your data. Open Source always means more secure.
SudoBot is designed with security in
mind. We take security seriously and are
committed to protecting your data. Open
Source always means more secure.
</p>
</div>
<div className="flex min-h-max flex-col items-center justify-center bg-white dark:bg-black">
Expand Down
85 changes: 85 additions & 0 deletions src/app/(main)/verify/guilds/[id]/challenge/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import PageExpired from "@/app/page-expired";
import GuildVerificationGate from "@/components/GuildVerificationGate/GuildVerificationGate";
import { Guild } from "@/types/Guild";
import { ServerComponentProps } from "@/types/ServerComponentProps";
import axios from "axios";
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { cache } from "react";

const getVerificationInfo = cache(async (guildId: string, memberId: string) => {
try {
const info = await axios.get(
`${process.env.NEXT_PUBLIC_API_URL}/guilds/${encodeURIComponent(guildId)}/members/${encodeURIComponent(memberId)}/verify`,
);
return [info.data?.guild as Guild, null] as const;
} catch (error) {
return [null, error] as const;
}
});

export async function generateMetadata({
params,
searchParams,
}: ServerComponentProps): Promise<Metadata> {
const id = params?.id;
const userId = searchParams?.u;
const requestToken = searchParams?.t;

if (!id || !userId || !requestToken) {
return {
title: "419 Page Expired - SudoBot",
};
}

const [guild, error] = id
? await getVerificationInfo(id, userId)
: [null, true];

if (!guild || error) {
return {
title: "404 Not Found - SudoBot",
};
}

return {
title: "Verify to Continue - SudoBot",
robots: {
index: false,
follow: false,
},
};
}

export default async function VerifyOnboardingPage({
params,
searchParams,
}: ServerComponentProps) {
const id = params?.id;
const userId = searchParams?.u;
const requestToken = searchParams?.t;

if (!id || !userId || !requestToken) {
return <PageExpired />;
}

const [guild, error] = id
? await getVerificationInfo(id, userId)
: [null, true];

console.log(guild, error);

if (!guild || error) {
notFound();
}

return (
<div className="mx-5 flex min-h-[90vh] items-center justify-center">
<GuildVerificationGate
guild={guild}
userId={userId}
requestToken={requestToken}
/>
</div>
);
}
15 changes: 15 additions & 0 deletions src/app/page-expired.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import HTTPErrorView from "@/components/Errors/HTTPErrorView";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "419 Page Expired",
};

export default function PageExpired() {
return (
<HTTPErrorView statusCode={419} statusText="Page Expired">
The page has either expired due to inactivity or the request payload
was invalid.
</HTTPErrorView>
);
}
Loading

0 comments on commit fd99cd4

Please sign in to comment.