Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bbafab5
perf: Add react compiler
aXenDeveloper Nov 9, 2025
8f03897
Merge branch 'canary' into feat/package_json_for_plugins
aXenDeveloper Nov 10, 2025
ee96f34
refactor(email): Create nodemailer package
aXenDeveloper Nov 11, 2025
fd09aed
refactor(email): Move resend to package
aXenDeveloper Nov 11, 2025
a096ddc
refactor(cron): Move node-cron to package
aXenDeveloper Nov 11, 2025
aab1a54
Merge pull request #683 from aXenDeveloper/refactor/packages_email
aXenDeveloper Nov 11, 2025
ba466a4
feat(plugin): Add create package.json
aXenDeveloper Nov 11, 2025
703aa11
ci: version bump to v1.2.0-canary.61
aXenDeveloper Nov 11, 2025
ed3cb2c
refactor(prepare): ✨ refactor directory creation to handle multiple p…
aXenDeveloper Nov 11, 2025
9de53c2
ci: version bump to v1.2.0-canary.62
aXenDeveloper Nov 11, 2025
c6e8448
refactor(plugin): ✨ update npmignore handling and add template
aXenDeveloper Nov 11, 2025
d5d3f8f
refactor(npmignore): ✨ update .npmignore for clarity and organization
aXenDeveloper Nov 11, 2025
c3281dc
ci: version bump to v1.2.0-canary.63
aXenDeveloper Nov 11, 2025
0bc1006
refactor(plugin): ✨ update path resolution for plugin template
aXenDeveloper Nov 11, 2025
a68c484
ci: version bump to v1.2.0-canary.64
aXenDeveloper Nov 11, 2025
f2f0111
refactor(plugin): ✨ update parameter names for clarity in `createPlug…
aXenDeveloper Nov 11, 2025
df0031c
ci: version bump to v1.2.0-canary.65
aXenDeveloper Nov 11, 2025
6524622
refactor(plugin): ✨ update import paths to include file extensions
aXenDeveloper Nov 11, 2025
a27322e
ci: version bump to v1.2.0-canary.66
aXenDeveloper Nov 11, 2025
2ab1867
feat(plugin): ✨ add functionality to register new plugins in workspace
aXenDeveloper Nov 12, 2025
db1928b
ci: version bump to v1.2.0-canary.67
aXenDeveloper Nov 12, 2025
f6656a6
refactor(plugin): ✨ improve logic for skipping packages in the same p…
aXenDeveloper Nov 12, 2025
dfeb025
ci: version bump to v1.2.0-canary.68
aXenDeveloper Nov 12, 2025
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 --filter @vitnode/node-cron --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 --filter @vitnode/node-cron --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 --filter @vitnode/node-cron --tag latest --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
Expand Down
11 changes: 6 additions & 5 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api",
"version": "1.2.0-canary.60",
"version": "1.2.0-canary.68",
"private": true,
"type": "module",
"scripts": {
Expand All @@ -19,20 +19,21 @@
"@vitnode/core": "workspace:*",
"drizzle-kit": "^0.31.6",
"drizzle-orm": "^0.44.7",
"hono": "^4.10.4",
"next-intl": "^4.5.0",
"hono": "^4.10.5",
"next-intl": "^4.5.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"use-intl": "^4.5.0",
"use-intl": "^4.5.1",
"zod": "^4.1.12"
},
"devDependencies": {
"@hono/node-server": "^1.19.6",
"@react-email/components": "^1.0.0",
"@types/node": "^24.10.0",
"@types/react": "^19.2.2",
"@types/react": "^19.2.3",
"@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