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
6 changes: 3 additions & 3 deletions .github/workflows/bump_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,21 @@ jobs:

- name: Publish canary
if: ${{ (github.event.inputs.mode == 'bump_and_publish' || github.event.inputs.mode == 'publish') && github.event.inputs.release == 'canary' }}
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --tag canary --no-git-checks --access public
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --filter @vitnode/nodemailer --filter @vitnode/resend --tag canary --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

- name: Publish release candidate
if: ${{ (github.event.inputs.mode == 'bump_and_publish' || github.event.inputs.mode == 'publish') && github.event.inputs.release == 'release-candidate' }}
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --tag rc --no-git-checks --access public
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --filter @vitnode/nodemailer --filter @vitnode/resend --tag rc --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

- name: Publish stable
if: ${{ (github.event.inputs.mode == 'bump_and_publish' || github.event.inputs.mode == 'publish') && github.event.inputs.release == 'stable' }}
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --tag latest --no-git-checks --access public
run: pnpm publish --filter create-vitnode-app --filter @vitnode/core --filter @vitnode/config --filter @vitnode/blog --filter @vitnode/nodemailer --filter @vitnode/resend --tag latest --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
Expand Down
1 change: 1 addition & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitnode/config": "workspace:*",
"@vitnode/nodemailer": "workspace:*",
"dotenv": "^17.2.3",
"eslint": "^9.39.1",
"react-email": "^5.0.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/vitnode.api.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NodemailerEmailAdapter } from "@vitnode/core/api/adapters/email/nodemailer";
import { buildApiConfig } from "@vitnode/core/vitnode.config";
import { NodemailerEmailAdapter } from "@vitnode/nodemailer";
import { config } from "dotenv";
import { drizzle } from "drizzle-orm/postgres-js";

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
116 changes: 116 additions & 0 deletions apps/docs/content/docs/dev/captcha/custom-adapter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
title: Custom Adapter
description: Create your own custom captcha adapter for VitNode.
---

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

## Usage

<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({
pluginId: CONFIG_PLUGIN.pluginId,
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>
144 changes: 21 additions & 123 deletions apps/docs/content/docs/dev/captcha/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ title: Captcha
description: Protect your forms and API call with captcha validation.
---

## Support
import captchaPreview from "./captcha_preview.png";

import { ImgDocs } from "@/components/fumadocs/img";

<ImgDocs src={captchaPreview} alt="Captcha Preview" />

## Providers

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

Expand All @@ -13,7 +19,11 @@ VitNode supports multiple captcha providers. You can choose the one that fits yo
description="By Cloudflare"
href="/docs/guides/captcha/cloudflare"
/>
<Card title="reCAPTCHA v3" description="By Google" href="/docs/guides/captcha/recaptcha" />
<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) :)
Expand All @@ -38,9 +48,9 @@ export const exampleRoute = buildRoute({
method: "post",
description: "Create a new user",
path: "/sign_up",
withCaptcha: true // [!code ++]
withCaptcha: true, // [!code ++]
},
handler: async (c) => {}
handler: async c => {},
});
```

Expand Down Expand Up @@ -74,7 +84,7 @@ Get the `captcha` config from the props and pass it to the `AutoForm` component.
import { AutoForm } from "@vitnode/core/components/form/auto-form";

export const FormSignUp = ({
captcha // [!code ++]
captcha, // [!code ++]
}: {
captcha: z.infer<typeof routeMiddlewareSchema>["captcha"]; // [!code ++]
}) => {
Expand Down Expand Up @@ -106,23 +116,23 @@ In your form submission handler, you can get the `captchaToken` from the form su

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

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

// Handle success or error
Expand Down Expand Up @@ -159,8 +169,8 @@ z.infer<typeof zodSignUpSchema> & { captchaToken: string }) => {
module: "users",
captchaToken, // [!code ++]
args: {
body: input
}
body: input,
},
});

if (res.status !== 201) {
Expand All @@ -175,115 +185,3 @@ z.infer<typeof zodSignUpSchema> & { captchaToken: string }) => {

</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({
pluginId: CONFIG_PLUGIN.pluginId,
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/captcha/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Captcha",
"pages": ["...", "custom-adapter"]
}
Loading