├── .eslintrc.json ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── app ├── api │ ├── auth │ │ └── [...nextauth] │ │ │ └── route.ts │ ├── chat │ │ └── route.ts │ └── webhook.ts ├── chat │ └── page.tsx ├── globals.css ├── layout.tsx └── page.tsx ├── components.json ├── components ├── BlurredClipPath.tsx ├── CompanyLogos.tsx ├── DownloadPrompt.tsx ├── Footer.tsx ├── HandleAuth.tsx ├── Header.tsx ├── Hero.tsx ├── NavbarSide.tsx ├── Pricing.tsx ├── PricingButtonOptions.tsx ├── Testimonials.tsx ├── ThemeProvider.tsx ├── ThemeToggle.tsx └── ui │ ├── avatar.tsx │ ├── badge.tsx │ ├── button.tsx │ ├── card.tsx │ ├── dropdown-menu.tsx │ ├── input.tsx │ ├── separator.tsx │ └── sheet.tsx ├── config └── index.ts ├── lib ├── authOptions.ts ├── formatCurrency.ts └── utils.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── abbysmith.jpg ├── app-screenshot.png ├── craignelson.jpg ├── johndoe.jpg └── nuro-logo.png ├── tailwind.config.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | .env 5 | /node_modules 6 | /.pnp 7 | .pnp.js 8 | .yarn/install-state.gz 9 | 10 | # testing 11 | /coverage 12 | 13 | # next.js 14 | /.next/ 15 | /out/ 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuro AI 2 | 3 | Nuro AI is a AI chatbot SaaS that uses Open AI and GPT-3. Users can communicate freely with Chat GPT and gather information by asking questions. Users can also login thorugh the use of NextAuth.js, implementing a Google provider to allow users to sign in with their Gmail account. A full Stripe payment process is also added, allowing users to choose a monthly plan. 4 | 5 | ![Safari (Big Sur) - Dark](https://github.com/TheHamzaDev/Nuro-AI/assets/143728239/06537cac-ebc6-438d-81e3-dee6b7f6d4ad) 6 | 7 | ## Getting Started 8 | 9 | FIrst, clone the repository and install the necessary dependencies: 10 | 11 | ```bash 12 | npm install 13 | # or 14 | yarn install 15 | # or 16 | pnpm install 17 | ``` 18 | 19 | Then run the development server: 20 | 21 | ```bash 22 | npm run dev 23 | # or 24 | yarn dev 25 | # or 26 | pnpm dev 27 | ``` 28 | 29 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 30 | 31 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { authOptions } from "@/lib/authOptions"; 2 | import NextAuth from "next-auth/next"; 3 | 4 | const handler = NextAuth(authOptions); 5 | export { handler as GET, handler as POST }; 6 | -------------------------------------------------------------------------------- /app/api/chat/route.ts: -------------------------------------------------------------------------------- 1 | import { OpenAIStream, StreamingTextResponse } from "ai"; 2 | import { Configuration, OpenAIApi } from "openai-edge"; 3 | 4 | export const runtime = "edge"; 5 | 6 | const config = new Configuration({ 7 | apiKey: process.env.OPENAI_KEY, 8 | }); 9 | 10 | const openai = new OpenAIApi(config); 11 | 12 | export async function POST(req: Request) { 13 | const body = await req.json(); 14 | const { messages } = body; 15 | 16 | //chat streaming completion 17 | const response = await openai.createChatCompletion({ 18 | model: "gpt-3.5-turbo", 19 | messages, 20 | stream: true, 21 | }); 22 | 23 | const stream = OpenAIStream(response); 24 | return new StreamingTextResponse(stream); 25 | } 26 | -------------------------------------------------------------------------------- /app/api/webhook.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import getRawBody from "raw-body"; 3 | import Stripe from "stripe"; 4 | 5 | const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, { 6 | apiVersion: "2023-10-16", 7 | }); 8 | 9 | const endpointSecret = process.env.WEBHOOK_SECRET as string; 10 | 11 | // included to avoid 'stream.not.readable' error 12 | export const config = { 13 | api: { 14 | bodyParser: false, 15 | }, 16 | }; 17 | 18 | export default async function handler( 19 | req: NextApiRequest, 20 | res: NextApiResponse 21 | ) { 22 | try { 23 | console.log("req.headers", req.headers); 24 | if (req.method !== "POST") { 25 | return res.status(405).send("Must send POST request only"); 26 | } 27 | 28 | const sig: any = req.headers["stripe-signature"]; 29 | const rawBody = await getRawBody(req); 30 | 31 | let event; 32 | 33 | try { 34 | event = stripe.webhooks.constructEvent(rawBody, sig, endpointSecret); 35 | } catch (error: any) { 36 | return res.status(400).send(`Webhook Error: ${error.message}`); 37 | } 38 | 39 | console.log("event.type", JSON.stringify(event.type)); 40 | 41 | if (event.type === "checkout.session.completed") { 42 | const sessionWithLineItems = await stripe.checkout.sessions.retrieve( 43 | (event.data.object as any).id, 44 | { 45 | expand: ["line_items"], 46 | } 47 | ); 48 | const lineItems = sessionWithLineItems.line_items; 49 | 50 | if (!lineItems) return res.status(500).send("Internal Server Error"); 51 | 52 | try { 53 | //save the data 54 | console.log("Order Fulfilled"); 55 | console.log("data", lineItems.data); 56 | console.log( 57 | "Customer Email", 58 | (event.data.object as any).customer_details.email 59 | ); 60 | console.log("Created", (event.data.object as any).created); 61 | } catch (error) { 62 | console.log("Unable to save order"); 63 | } 64 | } 65 | res.status(200).end(); 66 | } catch (error) { 67 | console.error(error); 68 | res.status(500).json("Internal Server Error"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/chat/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { Input } from "@/components/ui/input"; 5 | import { ArrowRightIcon, ComponentBooleanIcon } from "@radix-ui/react-icons"; 6 | import { useChat } from "ai/react"; 7 | 8 | export default function ChatBot() { 9 | const { messages, input, handleInputChange, handleSubmit } = useChat(); 10 | return ( 11 |
12 |
13 |

14 | Start your conversation with Nuro. 15 |

16 |

17 | With Nuro, you can enjoy smart, friendly, and convenient communication 18 | anytime, anywhere. 19 |

20 |
21 |
22 |
23 | 24 | 25 | Hello, ask me anything! 26 | 27 |
28 |
29 | {messages.map((message) => ( 30 |
36 |
37 | {message.role === "assistant" && ( 38 | 39 | )} 40 | {message.content} 41 |
42 |
43 | ))} 44 |
48 | 55 | 58 |
59 |
60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 0 0% 3.9%; 9 | 10 | --card: 0 0% 100%; 11 | --card-foreground: 0 0% 3.9%; 12 | 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 0 0% 3.9%; 15 | 16 | --primary: 0 0% 9%; 17 | --primary-foreground: 0 0% 98%; 18 | 19 | --secondary: 0 0% 96.1%; 20 | --secondary-foreground: 0 0% 9%; 21 | 22 | --muted: 0 0% 96.1%; 23 | --muted-foreground: 0 0% 45.1%; 24 | 25 | --accent: 0 0% 96.1%; 26 | --accent-foreground: 0 0% 9%; 27 | 28 | --destructive: 0 84.2% 60.2%; 29 | --destructive-foreground: 0 0% 98%; 30 | 31 | --border: 0 0% 89.8%; 32 | --input: 0 0% 89.8%; 33 | --ring: 0 0% 3.9%; 34 | 35 | --radius: 0.5rem; 36 | } 37 | 38 | .dark { 39 | --background: 0 0% 3.9%; 40 | --foreground: 0 0% 98%; 41 | 42 | --card: 0 0% 3.9%; 43 | --card-foreground: 0 0% 98%; 44 | 45 | --popover: 0 0% 3.9%; 46 | --popover-foreground: 0 0% 98%; 47 | 48 | --primary: 0 0% 98%; 49 | --primary-foreground: 0 0% 9%; 50 | 51 | --secondary: 0 0% 14.9%; 52 | --secondary-foreground: 0 0% 98%; 53 | 54 | --muted: 0 0% 14.9%; 55 | --muted-foreground: 0 0% 63.9%; 56 | 57 | --accent: 0 0% 14.9%; 58 | --accent-foreground: 0 0% 98%; 59 | 60 | --destructive: 0 62.8% 30.6%; 61 | --destructive-foreground: 0 0% 98%; 62 | 63 | --border: 0 0% 14.9%; 64 | --input: 0 0% 14.9%; 65 | --ring: 0 0% 83.1%; 66 | } 67 | } 68 | 69 | @layer base { 70 | * { 71 | @apply border-border; 72 | } 73 | body { 74 | @apply bg-background text-foreground; 75 | } 76 | svg { 77 | @apply fill-foreground/60 hover:fill-foreground transition-all; 78 | } 79 | } 80 | 81 | @layer utilities { 82 | .container { 83 | @apply max-w-screen-2xl m-auto; 84 | } 85 | } 86 | 87 | .no-scrollbar { 88 | -ms-overflow-style: none; /* Internet Explorer 10+ */ 89 | scrollbar-width: none; /* Firefox */ 90 | } 91 | .no-scrollbar::-webkit-scrollbar { 92 | display: none; /* Safari and Chrome */ 93 | } 94 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/components/Header"; 2 | import { ThemeProvider } from "@/components/ThemeProvider"; 3 | import type { Metadata } from "next"; 4 | import { Inter } from "next/font/google"; 5 | import "./globals.css"; 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Nuro — AI ChatBot", 11 | description: "An SaaS website showcasing OpenAI", 12 | icons: { 13 | icon: "/nuro-logo.png", 14 | }, 15 | }; 16 | 17 | export default function RootLayout({ 18 | children, 19 | }: Readonly<{ 20 | children: React.ReactNode; 21 | }>) { 22 | return ( 23 | 24 | 25 | 31 |
32 |
{children}
33 | 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import DownloadPrompt from "@/components/DownloadPrompt"; 2 | import Footer from "@/components/Footer"; 3 | import Hero from "@/components/Hero"; 4 | import Pricing from "@/components/Pricing"; 5 | import Testimonials from "@/components/Testimonials"; 6 | 7 | export default function Home() { 8 | return ( 9 | <> 10 | 11 | 12 | 13 | 14 |