Skip to content

Commit 4356047

Browse files
authored
Initial commit
0 parents  commit 4356047

200 files changed

Lines changed: 32684 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/ultracite.mdc

Lines changed: 333 additions & 0 deletions
Large diffs are not rendered by default.

.env.example

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generate a random secret: https://generate-secret.vercel.app/32 or `openssl rand -base64 32`
2+
AUTH_SECRET=****
3+
4+
# The following keys below are automatically created and
5+
# added to your environment when you deploy on vercel
6+
7+
# AI Gateway API Key (required for non-Vercel deployments)
8+
# For Vercel deployments, OIDC tokens are used automatically
9+
# https://vercel.com/ai-gateway
10+
AI_GATEWAY_API_KEY=****
11+
12+
13+
# Instructions to create a Vercel Blob Store here: https://vercel.com/docs/storage/vercel-blob
14+
BLOB_READ_WRITE_TOKEN=****
15+
16+
# Instructions to create a PostgreSQL database here: https://vercel.com/docs/storage/vercel-postgres/quickstart
17+
POSTGRES_URL=****
18+
19+
20+
# Instructions to create a Redis store here:
21+
# https://vercel.com/docs/redis
22+
REDIS_URL=****

.github/workflows/lint.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Lint
2+
on:
3+
push:
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-22.04
8+
strategy:
9+
matrix:
10+
node-version: [20]
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Install pnpm
14+
uses: pnpm/action-setup@v4
15+
with:
16+
version: 9.12.3
17+
- name: Use Node.js ${{ matrix.node-version }}
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: ${{ matrix.node-version }}
21+
cache: "pnpm"
22+
- name: Install dependencies
23+
run: pnpm install
24+
- name: Run lint
25+
run: pnpm lint

.github/workflows/playwright.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Playwright Tests
2+
on:
3+
push:
4+
branches: [main, master]
5+
pull_request:
6+
branches: [main, master]
7+
8+
jobs:
9+
test:
10+
timeout-minutes: 30
11+
runs-on: ubuntu-latest
12+
env:
13+
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
14+
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
15+
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
16+
REDIS_URL: ${{ secrets.REDIS_URL }}
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 1
22+
23+
- uses: actions/setup-node@v4
24+
with:
25+
node-version: lts/*
26+
27+
- name: Install pnpm
28+
uses: pnpm/action-setup@v2
29+
with:
30+
version: latest
31+
run_install: false
32+
33+
- name: Get pnpm store directory
34+
id: pnpm-cache
35+
shell: bash
36+
run: |
37+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
38+
39+
- uses: actions/cache@v3
40+
with:
41+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
42+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
43+
restore-keys: |
44+
${{ runner.os }}-pnpm-store-
45+
46+
- uses: actions/setup-node@v4
47+
with:
48+
node-version: lts/*
49+
cache: "pnpm"
50+
51+
- name: Install dependencies
52+
run: pnpm install --frozen-lockfile
53+
54+
- name: Cache Playwright browsers
55+
uses: actions/cache@v3
56+
id: playwright-cache
57+
with:
58+
path: ~/.cache/ms-playwright
59+
key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}
60+
61+
- name: Install Playwright Browsers
62+
if: steps.playwright-cache.outputs.cache-hit != 'true'
63+
run: pnpm exec playwright install --with-deps chromium
64+
65+
- name: Run Playwright tests
66+
run: pnpm test
67+
68+
- uses: actions/upload-artifact@v4
69+
if: always() && !cancelled()
70+
with:
71+
name: playwright-report
72+
path: playwright-report/
73+
retention-days: 7

.gitignore

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
node_modules
5+
.pnp
6+
.pnp.js
7+
8+
# testing
9+
coverage
10+
11+
# next.js
12+
.next/
13+
out/
14+
build
15+
16+
# misc
17+
.DS_Store
18+
*.pem
19+
20+
# debug
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*
24+
.pnpm-debug.log*
25+
26+
# local env files
27+
.env.local
28+
.env.development.local
29+
.env.test.local
30+
.env.production.local
31+
32+
# turbo
33+
.turbo
34+
35+
.env
36+
.vercel
37+
.env*.local
38+
39+
# Playwright
40+
/test-results/
41+
/playwright-report/
42+
/blob-report/
43+
/playwright/*

.vscode/extensions.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"recommendations": ["biomejs.biome"]
3+
}

.vscode/settings.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"editor.defaultFormatter": "esbenp.prettier-vscode",
3+
"[javascript]": {
4+
"editor.defaultFormatter": "biomejs.biome"
5+
},
6+
"[typescript]": {
7+
"editor.defaultFormatter": "biomejs.biome"
8+
},
9+
"[javascriptreact]": {
10+
"editor.defaultFormatter": "biomejs.biome"
11+
},
12+
"[typescriptreact]": {
13+
"editor.defaultFormatter": "biomejs.biome"
14+
},
15+
"[json]": {
16+
"editor.defaultFormatter": "biomejs.biome"
17+
},
18+
"[jsonc]": {
19+
"editor.defaultFormatter": "biomejs.biome"
20+
},
21+
"[css]": {
22+
"editor.defaultFormatter": "biomejs.biome"
23+
},
24+
"[graphql]": {
25+
"editor.defaultFormatter": "biomejs.biome"
26+
},
27+
"typescript.tsdk": "node_modules/typescript/lib",
28+
"editor.formatOnSave": true,
29+
"editor.formatOnPaste": true,
30+
"emmet.showExpandedAbbreviation": "never",
31+
"editor.codeActionsOnSave": {
32+
"source.fixAll.biome": "explicit",
33+
"source.organizeImports.biome": "explicit"
34+
}
35+
}

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2024 Vercel, Inc.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<a href="https://chat.vercel.ai/">
2+
<img alt="Next.js 14 and App Router-ready AI chatbot." src="app/(chat)/opengraph-image.png">
3+
<h1 align="center">Chat SDK</h1>
4+
</a>
5+
6+
<p align="center">
7+
Chat SDK is a free, open-source template built with Next.js and the AI SDK that helps you quickly build powerful chatbot applications.
8+
</p>
9+
10+
<p align="center">
11+
<a href="https://chat-sdk.dev"><strong>Read Docs</strong></a> ·
12+
<a href="#features"><strong>Features</strong></a> ·
13+
<a href="#model-providers"><strong>Model Providers</strong></a> ·
14+
<a href="#deploy-your-own"><strong>Deploy Your Own</strong></a> ·
15+
<a href="#running-locally"><strong>Running locally</strong></a>
16+
</p>
17+
<br/>
18+
19+
## Features
20+
21+
- [Next.js](https://nextjs.org) App Router
22+
- Advanced routing for seamless navigation and performance
23+
- React Server Components (RSCs) and Server Actions for server-side rendering and increased performance
24+
- [AI SDK](https://ai-sdk.dev/docs/introduction)
25+
- Unified API for generating text, structured objects, and tool calls with LLMs
26+
- Hooks for building dynamic chat and generative user interfaces
27+
- Supports xAI (default), OpenAI, Fireworks, and other model providers
28+
- [shadcn/ui](https://ui.shadcn.com)
29+
- Styling with [Tailwind CSS](https://tailwindcss.com)
30+
- Component primitives from [Radix UI](https://radix-ui.com) for accessibility and flexibility
31+
- Data Persistence
32+
- [Neon Serverless Postgres](https://vercel.com/marketplace/neon) for saving chat history and user data
33+
- [Vercel Blob](https://vercel.com/storage/blob) for efficient file storage
34+
- [Auth.js](https://authjs.dev)
35+
- Simple and secure authentication
36+
37+
## Model Providers
38+
39+
This template uses the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway) to access multiple AI models through a unified interface. The default configuration includes [xAI](https://x.ai) models (`grok-2-vision-1212`, `grok-3-mini`) routed through the gateway.
40+
41+
### AI Gateway Authentication
42+
43+
**For Vercel deployments**: Authentication is handled automatically via OIDC tokens.
44+
45+
**For non-Vercel deployments**: You need to provide an AI Gateway API key by setting the `AI_GATEWAY_API_KEY` environment variable in your `.env.local` file.
46+
47+
With the [AI SDK](https://ai-sdk.dev/docs/introduction), you can also switch to direct LLM providers like [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), and [many more](https://ai-sdk.dev/providers/ai-sdk-providers) with just a few lines of code.
48+
49+
## Deploy Your Own
50+
51+
You can deploy your own version of the Next.js AI Chatbot to Vercel with one click:
52+
53+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/templates/next.js/nextjs-ai-chatbot)
54+
55+
## Running locally
56+
57+
You will need to use the environment variables [defined in `.env.example`](.env.example) to run Next.js AI Chatbot. It's recommended you use [Vercel Environment Variables](https://vercel.com/docs/projects/environment-variables) for this, but a `.env` file is all that is necessary.
58+
59+
> Note: You should not commit your `.env` file or it will expose secrets that will allow others to control access to your various AI and authentication provider accounts.
60+
61+
1. Install Vercel CLI: `npm i -g vercel`
62+
2. Link local instance with Vercel and GitHub accounts (creates `.vercel` directory): `vercel link`
63+
3. Download your environment variables: `vercel env pull`
64+
65+
```bash
66+
pnpm install
67+
pnpm dev
68+
```
69+
70+
Your app template should now be running on [localhost:3000](http://localhost:3000).

app/(auth)/actions.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"use server";
2+
3+
import { z } from "zod";
4+
5+
import { createUser, getUser } from "@/lib/db/queries";
6+
7+
import { signIn } from "./auth";
8+
9+
const authFormSchema = z.object({
10+
email: z.string().email(),
11+
password: z.string().min(6),
12+
});
13+
14+
export type LoginActionState = {
15+
status: "idle" | "in_progress" | "success" | "failed" | "invalid_data";
16+
};
17+
18+
export const login = async (
19+
_: LoginActionState,
20+
formData: FormData
21+
): Promise<LoginActionState> => {
22+
try {
23+
const validatedData = authFormSchema.parse({
24+
email: formData.get("email"),
25+
password: formData.get("password"),
26+
});
27+
28+
await signIn("credentials", {
29+
email: validatedData.email,
30+
password: validatedData.password,
31+
redirect: false,
32+
});
33+
34+
return { status: "success" };
35+
} catch (error) {
36+
if (error instanceof z.ZodError) {
37+
return { status: "invalid_data" };
38+
}
39+
40+
return { status: "failed" };
41+
}
42+
};
43+
44+
export type RegisterActionState = {
45+
status:
46+
| "idle"
47+
| "in_progress"
48+
| "success"
49+
| "failed"
50+
| "user_exists"
51+
| "invalid_data";
52+
};
53+
54+
export const register = async (
55+
_: RegisterActionState,
56+
formData: FormData
57+
): Promise<RegisterActionState> => {
58+
try {
59+
const validatedData = authFormSchema.parse({
60+
email: formData.get("email"),
61+
password: formData.get("password"),
62+
});
63+
64+
const [user] = await getUser(validatedData.email);
65+
66+
if (user) {
67+
return { status: "user_exists" } as RegisterActionState;
68+
}
69+
await createUser(validatedData.email, validatedData.password);
70+
await signIn("credentials", {
71+
email: validatedData.email,
72+
password: validatedData.password,
73+
redirect: false,
74+
});
75+
76+
return { status: "success" };
77+
} catch (error) {
78+
if (error instanceof z.ZodError) {
79+
return { status: "invalid_data" };
80+
}
81+
82+
return { status: "failed" };
83+
}
84+
};

0 commit comments

Comments
 (0)