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
5 changes: 2 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
POSTGRES_URL=postgresql://root:root@localhost:5432/vitnode

NEXT_PUBLIC_BACKEND_URL=http://localhost:3000
NEXT_PUBLIC_BACKEND_CLIENT_URL=http://localhost:3000
NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:3000
NEXT_PUBLIC_WEB_URL=http://localhost:3000

# === Docker Database Postgres ===
POSTGRES_USER=root
Expand Down
12 changes: 12 additions & 0 deletions apps/api/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineVitNodeDrizzleConfig } from '@vitnode/core/drizzle.config';

import { POSTGRES_URL, vitNodeApiConfig } from './src/vitnode.api.config';

export default defineVitNodeDrizzleConfig({
vitNodeApiConfig,
out: './migrations/',
dialect: 'postgresql',
dbCredentials: {
url: POSTGRES_URL,
},
});
8 changes: 8 additions & 0 deletions apps/api/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import eslintVitNode from '@vitnode/eslint-config/eslint';

export default [
...eslintVitNode,
{
ignores: ['drizzle.config.ts'],
},
];
42 changes: 42 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "api",
"version": "1.2.0-canary.31",
"private": true,
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc && tsc-alias -p tsconfig.json",
"start": "node dist/index.js",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"drizzle-kit": "drizzle-kit"
},
"dependencies": {
"@hono/zod-openapi": "^0.19.8",
"@hono/zod-validator": "^0.7.0",
"@react-email/components": "^0.1.1",
"@vitnode/core": "workspace:*",
"drizzle-kit": "^0.31.3",
"drizzle-orm": "^0.44.2",
"hono": "^4.8.3",
"next-intl": "^4.3.1",
"react": "^19.1",
"react-dom": "^19.1",
"zod": "^3.25.67"
},
"devDependencies": {
"@hono/node-server": "^1.15.0",
"@types/node": "^24",
"@types/react": "^19.1",
"@types/react-dom": "^19.1",
"@vitnode/eslint-config": "workspace:*",
"dotenv": "^17.1.0",
"eslint": "^9.29.0",
"prettier": "^3.6.1",
"prettier-plugin-tailwindcss": "^0.6.12",
"react-email": "^4.0.17",
"tsc-alias": "^1.8.16",
"tsx": "^4.20.3",
"typescript": "^5.8.3"
}
}
29 changes: 29 additions & 0 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { serve } from '@hono/node-server';
import { OpenAPIHono } from '@hono/zod-openapi';
import { VitNodeAPI } from '@vitnode/core/api/config';

import { vitNodeApiConfig } from './vitnode.api.config.js';
import { vitNodeConfig } from './vitnode.config.js';

const app = new OpenAPIHono().basePath('/api');

VitNodeAPI({
app,
vitNodeApiConfig,
vitNodeConfig,
});

serve(
{
fetch: app.fetch,
port: 8080,
},
info => {
const initMessage = '\x1b[34m[VitNode]\x1b[0m';

// eslint-disable-next-line no-console
console.log(
`${initMessage} API server is running on http://localhost:${info.port}`,
);
},
);
17 changes: 17 additions & 0 deletions apps/api/src/vitnode.api.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { buildApiConfig } from '@vitnode/core/vitnode.config';
import * as dotenv from 'dotenv';
import { drizzle } from 'drizzle-orm/postgres-js';

dotenv.config();

export const POSTGRES_URL =
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode';

export const vitNodeApiConfig = buildApiConfig({
plugins: [],
dbProvider: drizzle({
connection: POSTGRES_URL,
casing: 'camelCase',
}),
});
32 changes: 32 additions & 0 deletions apps/api/src/vitnode.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { buildConfig, handleRequestConfig } from '@vitnode/core/vitnode.config';
import { getRequestConfig } from 'next-intl/server';

export const vitNodeConfig = buildConfig({
metadata: {
title: 'VitNode',
shortTitle: 'VitNode',
},
plugins: [],
i18n: {
locales: [
{
code: 'en',
name: 'English',
},
],
defaultLocale: 'en',
},
theme: {
defaultTheme: 'light',
},
});

// This is the request config for the app. It will be used in the app router.
export default getRequestConfig(
async ({ requestLocale }) =>
await handleRequestConfig({
requestLocale,
vitNodeConfig,
pathToMessages: async path => await import(`./locales/${path}`),
}),
);
16 changes: 16 additions & 0 deletions apps/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vitnode/eslint-config/tsconfig",
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"baseUrl": ".",
"outDir": "./dist",
"types": ["node"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules"]
}
1 change: 1 addition & 0 deletions apps/docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# deps
/node_modules
.turbo

# generated content
.contentlayer
Expand Down
11 changes: 0 additions & 11 deletions apps/docs/.prettierrc.mjs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"title": "Config",
"title": "Advanced",
"pages": ["..."]
}
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": ["overview", "---Adapters---", "..."]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Captcha
title: Overview
description: Protect your forms and API call with captcha validation.
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { buildRoute } from '@vitnode/core/api/lib/route';
export const testRoute = buildRoute(
{},
{
handler: c => {
c.get('log').warn('This is a test warn log'); // [!code ++]
handler: async c => {
await c.get('log').warn('This is a test warn log'); // [!code ++]

return c.text('test');
},
Expand All @@ -33,15 +33,15 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tabs id='logging-variants' persist items={['Debug', 'Warn', 'Error']}>

```ts tab="Debug"
c.get('log').debug('This is a test debug log');
await c.get('log').debug('This is a test debug log');
```

```ts tab="Warn"
c.get('log').warn('This is a test warning log');
await c.get('log').warn('This is a test warning log');
```

```ts tab="Error"
c.get('log').error('This is a test error log');
await c.get('log').error('This is a test error log');
```

</Tabs>
7 changes: 7 additions & 0 deletions apps/docs/content/docs/dev/deployments/cloud/vercel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ description: How to deploy your Vitnode app on Vercel.
We're working hard to bring you the best documentation experience.
</Callout>

## Limitations

Some features of Vitnode are not supported on Vercel due to its serverless architecture. These include:

- Self-hosted static files
- WebSockets

## Requirements

- A Vercel account
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/content/docs/dev/email/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Email",
"pages": ["overview", "templates", "---Adapters---", "..."]
}
117 changes: 117 additions & 0 deletions apps/docs/content/docs/dev/email/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
title: Overview
description: How to use email functionality in your application.
---

## Adapters

Before you can use email functionality, you need to provide an adapter to your application.

<Cards>
<Card title="Resend" href="/docs/dev/email/resend" />
<Card title="SMTP" href="/docs/dev/email/smtp" />
</Cards>

or create your own custom email adapter...

## Usage

To send an email, you can use the context `c.get('email').send()` in your route handler.

```ts
import { z } from 'zod';
import { buildRoute } from '@vitnode/core/api/lib/route';

export const testRoute = buildRoute({
handler: async c => {
// [!code ++:5]
await c.get('email').send({
to: 'test@test.com',
subject: 'Test Email',
content: 'This is a test email.',
});

return c.text('test');
},
});
```

## Custom Email Adapter

Want to create your own email adapter? You can do it by implementing the `EmailApiPlugin` interface. This allows you to define how emails are sent in your application.

<Steps>
<Step>
### Create adapter

Here is your template for a custom email adapter.

```ts title="src/utils/email/mailer.ts"
import type { EmailApiPlugin } from '@vitnode/core/api/models/email';

export const MailerEmailAdapter = (): EmailApiPlugin => {};
```

</Step>
<Step>

### Add config

If you want to provide config for you adapter, you can do it like this:

```ts title="src/utils/email/mailer.ts"
import type { EmailApiPlugin } from '@vitnode/core/api/models/email';

export const MailerEmailAdapter = ({
// [!code ++:13]
host = '',
port = 587,
secure = false,
user = '',
password = '',
from = '',
}: {
from: string | undefined;
host: string | undefined;
password: string | undefined;
port?: number;
secure?: boolean;
user: string | undefined;
}): EmailApiPlugin => {};
```

</Step>

<Step>

### Add `sendEmail()` method

Implement the `sendEmail()` method to send emails using your custom logic. You can use any email sending library or service.

```ts title="src/utils/email/mailer.ts"
import type { EmailApiPlugin } from '@vitnode/core/api/models/email';

export const MailerEmailAdapter = ({
host = '',
port = 587,
secure = false,
user = '',
password = '',
from = '',
}: {
from: string | undefined;
host: string | undefined;
password: string | undefined;
port?: number;
secure?: boolean;
user: string | undefined;
}): EmailApiPlugin => {
// [!code ++:3]
return {
sendEmail: async ({ metadata, to, subject, html, replyTo, text }) => {},
};
};
```

</Step>
</Steps>
15 changes: 0 additions & 15 deletions apps/docs/content/docs/dev/email/provider.mdx

This file was deleted.

Loading