Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
295 changes: 295 additions & 0 deletions apps/docs/content/docs/dev/captcha.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
---
title: Captcha
description: Protect your forms and API call with captcha validation.
---

## Support

VitNode supports multiple captcha providers. You can choose the one that fits your needs. Currently, we support:

<Cards>
<Card
title="Cloudflare Turnstile"
description="By Cloudflare"
href="/docs/guides/captcha/cloudflare"
/>
<Card
title="reCAPTCHA v3"
description="By Google"
href="/docs/guides/captcha/recaptcha"
/>
</Cards>

If you need more providers, feel free to open a **Feature Request** on our [GitHub repository](https://github.com/aXenDeveloper/vitnode/issues) :)

## Usage

In this example, we will show you how to use captcha in your forms. We will use the `AutoForm` component to render the form and handle the captcha validation.

import { Step, Steps } from 'fumadocs-ui/components/steps';

<Steps>
<Step>

### Activate captcha in route

Add `withCaptcha` to your route config to enable captcha validation for this route.

```ts title="plugins/{plugin_name}/src/routes/example.ts"
import { buildRoute } from '@vitnode/core/api/lib/route';

export const exampleRoute = buildRoute({
...CONFIG_PLUGIN,
route: {
method: 'post',
description: 'Create a new user',
path: '/sign_up',
withCaptcha: true, // [!code ++]
},
handler: async c => {},
});
```

</Step>
<Step>

### Get config from middleware API

Get captcha config from middleware API in your view and pass it to your `'use client';` component.

```tsx title="plugins/{plugin_name}/src/app/sing_up/page.tsx"
import { getMiddlewareApi } from '@vitnode/core/lib/api/get-middleware-api'; // [!code ++]

export const SignUpView = async () => {
const { captcha } = await getMiddlewareApi(); // [!code ++]

return <FormSignUp captcha={captcha} />;
};
```

</Step>
<Step>

### Use in form

Get the `captcha` config from the props and pass it to the `AutoForm` component. This will render the captcha widget in your form.

```tsx title="plugins/{plugin_name}/src/components/form/sign-up/sign-up.tsx"
'use client';

import { AutoForm } from '@vitnode/core/components/form/auto-form';

export const FormSignUp = ({
captcha, // [!code ++]
}: {
captcha: z.infer<typeof routeMiddlewareSchema>['captcha']; // [!code ++]
}) => {
return (
<AutoForm<typeof formSchema>
captcha={captcha} // [!code ++]
fields={[]}
formSchema={formSchema}
/>
);
};
```

<Card
title="AutoForm"
description="Lear more about the AutoForm component and how to use it."
href="/docs/ui/auto-form"
/>

</Step>
<Step>

### Submit form with captcha

In your form submission handler, you can get the `captchaToken` from the form submission context and pass it to your mutation API.

```tsx title="plugins/{plugin_name}/src/components/form/sign-up/sign-up.tsx"
'use client';

import {
AutoForm,
type AutoFormOnSubmit, // [!code ++]
} from '@vitnode/core/components/form/auto-form';

export const FormSignUp = ({
captcha,
}: {
captcha: z.infer<typeof routeMiddlewareSchema>['captcha'];
}) => {
const onSubmit: AutoFormOnSubmit<typeof formSchema> = async (
values,
form,
{ captchaToken }, // [!code ++]
) => {
// Call your mutation API with captcha token
await mutationApi({
...values,
captchaToken, // [!code ++]
});

// Handle success or error
};

return (
<AutoForm<typeof formSchema>
captcha={captcha}
fields={[]}
onSubmit={onSubmit} // [!code ++]
formSchema={formSchema}
/>
);
};
```

Next, you need to set `captchaToken` in your mutation API call. This token is provided by the `AutoForm` component when the form is submitted.

```tsx title="plugins/{plugin_name}/src/components/form/sign-up/mutation-api.ts"
'use server';

import type { z } from 'zod';

import { fetcher } from '@vitnode/core/lib/fetcher';

export const mutationApi = async ({
captchaToken, // [!code ++]
...input
// [!code ++]
}: z.infer<typeof zodSignUpSchema> & { captchaToken }) => {
const res = await fetcher(usersModule, {
path: '/sign_up',
method: 'post',
module: 'users',
captchaToken, // [!code ++]
args: {
body: input,
},
});

if (res.status !== 201) {
return { error: await res.text() };
}

const data = await res.json();

return { data };
};
```

</Step>
</Steps>

## Custom Usage

If you want to use captcha in your custom form or somewhere else, follow these steps.

<Steps>
<Step>

### Activate captcha in route

```ts title="plugins/{plugin_name}/src/routes/example.ts"
import { buildRoute } from '@vitnode/core/api/lib/route';

export const exampleRoute = buildRoute({
...CONFIG_PLUGIN,
route: {
method: 'post',
description: 'Create a new user',
path: '/sign_up',
withCaptcha: true, // [!code ++]
},
handler: async c => {},
});
```

</Step>
<Step>

### Get config from middleware API

```tsx title="plugins/{plugin_name}/src/app/sing_up/page.tsx"
import { getMiddlewareApi } from '@vitnode/core/lib/api/get-middleware-api'; // [!code ++]

export const SignUpView = async () => {
const { captcha } = await getMiddlewareApi(); // [!code ++]

return <FormSignUp captcha={captcha} />;
};
```

</Step>
<Step>

### Use `useCaptcha` hook

Inside your client component, use the `useCaptcha` hook to handle captcha rendering and validation. Remember to add `div` with `id="vitnode_captcha"` where you want the captcha widget to appear.

```tsx title="plugins/{plugin_name}/src/components/form/sign-up/sign-up.tsx"
'use client';

import { AutoForm } from '@vitnode/core/components/form/auto-form';

export const FormSignUp = ({
captcha, // [!code ++]
}: {
captcha: z.infer<typeof routeMiddlewareSchema>['captcha']; // [!code ++]
}) => {
// [!code ++]
const { isReady, getToken, onReset } = useCaptcha(captcha);

const onSubmit = async () => {
await mutationApi({
// ...other values,
captchaToken: await getToken(), // [!code ++]
});

// Handle success or error
// [!code ++]
onReset(); // Reset captcha after submission
};

return (
<form onSubmit={onSubmit}>
{/* Render captcha widget */}
{/* [!code ++] */}
<div id="vitnode_captcha" />

<Button disabled={!isReady}>Submit</Button>
</form>
);
};
```

</Step>
<Step>

### Submit form with captcha

```tsx title="plugins/{plugin_name}/src/components/form/sign-up/mutation-api.ts"
'use server';

import type { z } from 'zod';

import { fetcher } from '@vitnode/core/lib/fetcher';

export const mutationApi = async ({
captchaToken, // [!code ++]
}: {
// [!code ++]
captchaToken;
}) => {
await fetcher(usersModule, {
path: '/test',
method: 'post',
module: 'blog',
captchaToken, // [!code ++]
});
};
```

</Step>
</Steps>
4 changes: 4 additions & 0 deletions apps/docs/content/docs/dev/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ npx create-vitnode-app@canary
```

</Tabs>

## Why VitNode?

something here
1 change: 1 addition & 0 deletions apps/docs/content/docs/dev/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"---Framework---",
"config",
"logging",
"captcha",
"---Advanced---",
"..."
]
Expand Down
50 changes: 17 additions & 33 deletions apps/docs/content/docs/guides/captcha/cloudflare.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ Follow the instructions to add your domain. If you have any trouble, you can che

Go to the `Turnstile` section and create a new widget.

// import homeTurnstile from '@/assets/guides/captcha/cloudflare.png';
import homeTurnstile from './cloudflare.png';

{/* <ImgDocs src={homeTurnstile} alt="Cloudflare Turnstile Home" /> */}
import { ImgDocs } from '@/components/fumadocs/img';

<ImgDocs src={homeTurnstile} alt="Cloudflare Turnstile Home" />

</Step>

Expand Down Expand Up @@ -62,8 +64,8 @@ After you create the widget, you will get the `Site Key` and `Secret Key`. Save
Add the keys to your `.env` file.

```bash
CAPTCHA_SECRET_KEY=XXX
CAPTCHA_SITE_KEY=XXX
CLOUDFLARE_TURNSTILE_SITE_KEY=XXX
CLOUDFLARE_TURNSTILE_SECRET_KEY=XXX
```

</Step>
Expand All @@ -72,35 +74,17 @@ CAPTCHA_SITE_KEY=XXX

## Provide keys to VitNode

Edit your `app.module.ts` file and add `captcha` object to the `VitNodeModule` configuration.

```ts
@Module({
imports: [
VitNodeCoreModule.register({
database: {
config: DATABASE_ENVS,
schemaDatabase,
},
// [!code ++]
captcha: {
// [!code ++]
type: 'cloudflare_turnstile',
// [!code ++]
site_key: process.env.CAPTCHA_SITE_KEY,
// [!code ++]
secret_key: process.env.CAPTCHA_SECRET_KEY,
// [!code ++]
},
}),
DatabaseModule,
PluginsModule,
CacheModule.register({
isGlobal: true,
}),
],
})
export class AppModule {}
```ts title="src/vitnode.api.config.ts"
import { buildApiConfig } from '@vitnode/core/vitnode.config';

export const vitNodeApiConfig = buildApiConfig({
// [!code ++:5]
captcha: {
type: 'cloudflare_turnstile',
siteKey: process.env.CLOUDFLARE_TURNSTILE_SITE_KEY,
secretKey: process.env.CLOUDFLARE_TURNSTILE_SECRET_KEY,
},
});
```

</Step>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading