Skip to content

Commit

Permalink
feat(auth): add a turnstile widget
Browse files Browse the repository at this point in the history
closes #627
  • Loading branch information
ygrishajev committed Jan 15, 2025
1 parent 3fd5195 commit bbc7134
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 47 deletions.
1 change: 1 addition & 0 deletions apps/deploy-web/.env.local.sample
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ BASE_API_SANDBOX_URL=http://${BASE_API_MAINNET_URL}:3080
GITHUB_CLIENT_SECRET=GITHUB_CLIENT_SECRET
BITBUCKET_CLIENT_SECRET=BITBUCKET_CLIENT_SECRET
GITLAB_CLIENT_SECRET=GITLAB_CLIENT_SECRET
NEXT_PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAA5S2ADjyKnHmdzo
2 changes: 2 additions & 0 deletions apps/deploy-web/env/.env.production
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
AUTH0_BASE_URL=https://console.akash.network
AUTH0_ISSUER_BASE_URL=https://auth.cloudmos.io

NEXT_PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAA5Stdp3_0Q5FClX

NEXT_PUBLIC_DEFAULT_NETWORK_ID=mainnet
NEXT_PUBLIC_BILLING_ENABLED=true
NEXT_PUBLIC_MANAGED_WALLET_NETWORK_ID=mainnet
Expand Down
2 changes: 2 additions & 0 deletions apps/deploy-web/env/.env.staging
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
AUTH0_BASE_URL=https://console-beta.akash.network
AUTH0_ISSUER_BASE_URL=https://dev-5aprb0lr.us.auth0.com

NEXT_PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAA5Stdp3_0Q5FClX

NEXT_PUBLIC_DEFAULT_NETWORK_ID=mainnet
NEXT_PUBLIC_BILLING_ENABLED=true
NEXT_PUBLIC_MANAGED_WALLET_NETWORK_ID=mainnet
Expand Down
1 change: 1 addition & 0 deletions apps/deploy-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@emotion/server": "^11.4.0",
"@emotion/styled": "^11.8.1",
"@hookform/resolvers": "^3.9.0",
"@marsidev/react-turnstile": "^1.1.0",
"@monaco-editor/react": "^4.6.0",
"@mui/icons-material": "^5.11.11",
"@mui/material": "^5.4.4",
Expand Down
75 changes: 75 additions & 0 deletions apps/deploy-web/src/components/turnstile/Turnstile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use client";

import { FC, useEffect, useMemo, useRef, useState } from "react";
import { MdInfo } from "react-icons/md";
import { Button } from "@akashnetwork/ui/components";
import { Turnstile as ReactTurnstile, TurnstileInstance } from "@marsidev/react-turnstile";
import classnames from "classnames";

import { browserEnvConfig } from "@src/config/browser-env.config";

type TurnstileStatus = "uninitialized" | "solved" | "expired" | "error" | "dismissed";

const VISIBILITY_STATUSES: TurnstileStatus[] = ["uninitialized", "expired", "error"];

export const Turnstile: FC = () => {
const turnstileRef = useRef<TurnstileInstance>();
const [status, setStatus] = useState<TurnstileStatus>("uninitialized");
const [isTimingOut, setIsTimingOut] = useState(false);
const isVisible = useMemo(() => !!browserEnvConfig.NEXT_PUBLIC_TURNSTILE_SITE_KEY && VISIBILITY_STATUSES.includes(status), [status]);
const hasActions = useMemo(() => isTimingOut || status === "error", [isTimingOut, status]);

useEffect(() => {
if (isVisible) {
const timeout = setTimeout(() => {
setIsTimingOut(true);
}, 5000);

return () => clearTimeout(timeout);
}
}, [isVisible]);

return (
<>
<div className={classnames({ hidden: !isVisible }, "fixed inset-0 z-[101] flex content-center items-center justify-center bg-white bg-opacity-90")}>
<div className="flex flex-col items-center">
<h3 className="mb-2 text-2xl font-bold">We are verifying you are a human. This may take a few seconds</h3>
<p className="mb-8">Reviewing the security of your connection before proceeding</p>
<div className="h-[66px]">
<ReactTurnstile
ref={turnstileRef}
siteKey={browserEnvConfig.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
onError={() => setStatus("error")}
onExpire={() => setStatus("expired")}
onSuccess={() => setStatus("solved")}
/>
</div>
{status === "error" && <p className="text-red-600">Some error occurred</p>}
<div
className={classnames(
{
"invisible opacity-0": !hasActions,
"visible opacity-100": hasActions
},
"flex flex-col items-center transition-opacity duration-200"
)}
>
<div className="my-8">
<Button onClick={turnstileRef.current?.reset} className="mr-4">
Retry
</Button>
<Button onClick={() => setStatus("dismissed")} variant="link">
Dismiss
</Button>
</div>

<p>
<MdInfo className="mr-1 inline text-xl text-muted-foreground" />
<small>dismissing the check might result into some features not working properly</small>
</p>
</div>
</div>
</div>
</>
);
};
3 changes: 2 additions & 1 deletion apps/deploy-web/src/config/browser-env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ export const browserEnvConfig = validateStaticEnvVars({
NEXT_PUBLIC_GITLAB_CLIENT_ID: process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID,
NEXT_PUBLIC_GITHUB_CLIENT_ID: process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID,
NEXT_PUBLIC_GA_MEASUREMENT_ID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID,
NEXT_PUBLIC_CI_CD_IMAGE_NAME: process.env.NEXT_PUBLIC_CI_CD_IMAGE_NAME
NEXT_PUBLIC_CI_CD_IMAGE_NAME: process.env.NEXT_PUBLIC_CI_CD_IMAGE_NAME,
NEXT_PUBLIC_TURNSTILE_SITE_KEY: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY
});
3 changes: 2 additions & 1 deletion apps/deploy-web/src/config/env-config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export const browserEnvSchema = z.object({
NEXT_PUBLIC_BITBUCKET_CLIENT_ID: z.string().optional(),
NEXT_PUBLIC_GITLAB_CLIENT_ID: z.string().optional(),
NEXT_PUBLIC_GITHUB_CLIENT_ID: z.string().optional(),
NEXT_PUBLIC_CI_CD_IMAGE_NAME: z.string()
NEXT_PUBLIC_CI_CD_IMAGE_NAME: z.string(),
NEXT_PUBLIC_TURNSTILE_SITE_KEY: z.string()
});

export const serverEnvSchema = browserEnvSchema.extend({
Expand Down
91 changes: 48 additions & 43 deletions apps/deploy-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import NProgress from "nprogress";
import GoogleAnalytics from "@src/components/layout/CustomGoogleAnalytics";
import { CustomIntlProvider } from "@src/components/layout/CustomIntlProvider";
import { PageHead } from "@src/components/layout/PageHead";
import { Turnstile } from "@src/components/turnstile/Turnstile";
import { UserProviders } from "@src/components/user/UserProviders";
import { BackgroundTaskProvider } from "@src/context/BackgroundTaskProvider";
import { CertificateProvider } from "@src/context/CertificateProvider";
Expand Down Expand Up @@ -45,51 +46,55 @@ Router.events.on("routeChangeError", () => NProgress.done());

const App: React.FunctionComponent<Props> = props => {
const { Component, pageProps } = props;

return (
<main className={cn("h-full bg-background font-sans tracking-wide antialiased", GeistSans.variable)}>
<PageHead />
<>
<Turnstile />
<main className={cn("h-full bg-background font-sans tracking-wide antialiased", GeistSans.variable)}>
<PageHead />

<AppCacheProvider {...props}>
<CustomIntlProvider>
<QueryClientProvider client={queryClient}>
<JotaiProvider store={store}>
<ThemeProvider attribute="class" defaultTheme="system" storageKey="theme" enableSystem disableTransitionOnChange>
<ColorModeProvider>
<CustomSnackbarProvider>
<GoogleAnalytics />
<UserProviders>
<PricingProvider>
<TooltipProvider>
<SettingsProvider>
<CustomChainProvider>
<PopupProvider>
<WalletProvider>
<ChainParamProvider>
<CertificateProvider>
<BackgroundTaskProvider>
<TemplatesProvider>
<LocalNoteProvider>
<Component {...pageProps} />
</LocalNoteProvider>
</TemplatesProvider>
</BackgroundTaskProvider>
</CertificateProvider>
</ChainParamProvider>
</WalletProvider>
</PopupProvider>
</CustomChainProvider>
</SettingsProvider>
</TooltipProvider>
</PricingProvider>
</UserProviders>
</CustomSnackbarProvider>
</ColorModeProvider>
</ThemeProvider>
</JotaiProvider>
</QueryClientProvider>
</CustomIntlProvider>
</AppCacheProvider>
</main>
<AppCacheProvider {...props}>
<CustomIntlProvider>
<QueryClientProvider client={queryClient}>
<JotaiProvider store={store}>
<ThemeProvider attribute="class" defaultTheme="system" storageKey="theme" enableSystem disableTransitionOnChange>
<ColorModeProvider>
<CustomSnackbarProvider>
<GoogleAnalytics />
<UserProviders>
<PricingProvider>
<TooltipProvider>
<SettingsProvider>
<CustomChainProvider>
<PopupProvider>
<WalletProvider>
<ChainParamProvider>
<CertificateProvider>
<BackgroundTaskProvider>
<TemplatesProvider>
<LocalNoteProvider>
<Component {...pageProps} />
</LocalNoteProvider>
</TemplatesProvider>
</BackgroundTaskProvider>
</CertificateProvider>
</ChainParamProvider>
</WalletProvider>
</PopupProvider>
</CustomChainProvider>
</SettingsProvider>
</TooltipProvider>
</PricingProvider>
</UserProviders>
</CustomSnackbarProvider>
</ColorModeProvider>
</ThemeProvider>
</JotaiProvider>
</QueryClientProvider>
</CustomIntlProvider>
</AppCacheProvider>
</main>
</>
);
};

Expand Down
14 changes: 12 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bbc7134

Please sign in to comment.