Skip to content

Commit

Permalink
chore: key dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
arshad-yaseen committed Dec 3, 2023
1 parent d4358d5 commit 96927c8
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 92 deletions.
30 changes: 15 additions & 15 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ export async function POST(req: Request): Promise<Response> {

const { sessionUser: user } = await getCurrentUser()

if (!user?.id) {
return ServerResponse.unauthorized()
}

// Get the User provided API Key and API key compatible OpenAI model from KV store.
const api_key_with_model_from_kv = await kvgetdec(user.id, "api_key")
const api_key_from_kv = api_key_with_model_from_kv?.split("::")[0]
const model_from_kv = api_key_with_model_from_kv?.split("::")[1]

// The count of the number of times the user has used the AI.
const user_ai_run_count = await kvget(user?.id!, "ai_run_count")
const { isPro } = await getUserSubscriptionPlan(user?.id!)
Expand All @@ -37,18 +46,14 @@ export async function POST(req: Request): Promise<Response> {
if (
user_ai_run_count !== undefined &&
Number(user_ai_run_count) > free_credits &&
!isPro
!isPro && !api_key_from_kv && !api_key
) {
return ServerResponse.error(
"You have exceeded the free credits limit, please upgrade to pro plan to continue using the AI.",
402
)
}

if (!user?.id) {
return ServerResponse.unauthorized()
}

if (!openai_body) {
return ServerResponse.badRequest("Missing openai_body")
} else if (!openai_body.messages) {
Expand All @@ -59,19 +64,14 @@ export async function POST(req: Request): Promise<Response> {
return ServerResponse.unauthorized()
}

// Get the User provided API Key and API key compatible OpenAI model from KV store.
const api_key_with_model_from_kv = await kvgetdec(user.id, "api_key")
const api_key_from_kv = api_key_with_model_from_kv?.split("::")[0]
const model_from_kv = api_key_with_model_from_kv?.split("::")[1]

let OPENAI_API_KEY

if (api_key) {
if (isPro) {
OPENAI_API_KEY = env.OPENAI_API_KEY
} else if (api_key) {
OPENAI_API_KEY = api_key
} else if (api_key_from_kv) {
OPENAI_API_KEY = api_key_from_kv
} else if (env.OPENAI_API_KEY && isPro) {
OPENAI_API_KEY = env.OPENAI_API_KEY
}

if (!OPENAI_API_KEY) {
Expand All @@ -81,12 +81,12 @@ export async function POST(req: Request): Promise<Response> {
if (!isCorrectApiKey(OPENAI_API_KEY)) {
return ServerResponse.unauthorized("Invalid OPENAI_API_KEY")
}

const openai = new OpenAI({ apiKey: OPENAI_API_KEY })

const payload: OpenAI.ChatCompletionCreateParams = {
...openai_body,
model: model_from_kv || type === "chat" ? models.chat : models.vision,
model: model_from_kv ? model_from_kv : type === "chat" ? models.chat : models.vision,
stream: stream_response,
}

Expand Down
8 changes: 0 additions & 8 deletions app/api/webhooks/stripe/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Stripe from "stripe"

import { env } from "@/env.mjs"
import { db } from "@/lib/db"
import { kvset } from "@/lib/kv"
import { stripe } from "@/lib/stripe"

export async function POST(req: Request) {
Expand Down Expand Up @@ -57,13 +56,6 @@ export async function POST(req: Request) {
session.subscription as string
)

// set to kv store isPro
try {
await kvset(session?.metadata?.userId!, "isPro", true)
} catch (error) {
console.error("KV set error:", error)
}

// Update the price id and set the new period end.
await db.user.update({
where: {
Expand Down
60 changes: 32 additions & 28 deletions components/bring-api-key.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react"
import { OPENAI_USAGE_POLICIES } from "@/constants/links"
import { isCorrectApiKey, validateApiKey } from "@/utils/openai"
import { ArrowTopRightIcon } from "@radix-ui/react-icons"
import { Loader2Icon } from "lucide-react"
import { KeyIcon, Loader2Icon, Trash2Icon } from "lucide-react"
import { toast } from "sonner"

import { models } from "@/config/ai"
import { Button } from "@/components/ui/button"
import { Button, ButtonProps } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
Dialog,
Expand All @@ -18,12 +18,10 @@ import {
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { cn } from "@/lib/utils"

const BringApiKey = () => {
const BringApiKey = (props: ButtonProps) => {
const [apiKey, setApiKey] = useState<string>("")
const [supportedOpenAiModel, setSupportedOpenAiModel] = useState<string>(
models.chat
)
const [accepted, setAccepted] = useState<boolean | "indeterminate">(false)
const [isSecureOpen, setIsSecureOpen] = useState<boolean>(false)
const [saving, setSaving] = useState<boolean>(false)
Expand All @@ -43,26 +41,32 @@ const BringApiKey = () => {
const isValid = await validateApiKey(apiKey)

if (isValid.error) {
if (isValid.code === "unsupported_api_key") {
setSupportedOpenAiModel(models.chat_old)
await save()
if (isValid.statusCode === 404) {
console.log("Hello 2", models.chat_old);
console.log("Is valid", isValid);

await save(models.chat_old)
} else {
toast.error(isValid.message)
}
setSaving(false)
} else {
await save()
console.log("Hello 3", models.chat);

await save(models.chat)
}
}

const save = async () => {
const keyToSave = `${apiKey}::${supportedOpenAiModel}`
const save = async (model: string) => {
console.log("Hello", model);

const keyToSave = `${apiKey}::${model}`
const res = await fetch("/api/api-key", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ keyToSave }),
body: JSON.stringify({ apiKey: keyToSave }),
})

setSaving(false)
Expand Down Expand Up @@ -100,6 +104,7 @@ const BringApiKey = () => {
setSaving(false)
setDeleting(false)
setIsApiKeyFromSession(false)
setIsDialogOpen(false)
}

useEffect(() => {
Expand All @@ -117,18 +122,19 @@ const BringApiKey = () => {
return (
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="mr-2" variant={"outline"}>
<Button className={cn("w-full", props.className)} {...props}>
<KeyIcon className="inline-block h-4 w-4 mr-2" />
{isApiKeyFromSession ? "Change" : "Bring"} OpenAI API Key
</Button>
</DialogTrigger>
<DialogContent className=" md:!rounded-xl md:p-12">
<DialogContent className=" md:!rounded-xl md:p-10">
<DialogHeader>
<DialogTitle className="text-center text-2xl font-semibold">
OpenAI API key
Your own OpenAI API Key
</DialogTitle>
<DialogDescription>
<p className="text-gray-9 text-center tracking-tight">
You need to bring your OpenAI API key to generate code.
<p className="text-muted-foreground text-center tracking-tight">
You need to bring your OpenAI API key to use AI.
</p>
</DialogDescription>
</DialogHeader>
Expand All @@ -138,15 +144,15 @@ const BringApiKey = () => {
<Input
type="text"
placeholder="Enter your OpenAI API key"
className="h-11 w-full border-2 transition-[border] duration-300 focus-visible:border-primary focus-visible:ring-0 focus-visible:ring-transparent"
className="h-11 w-full "
value={apiKey}
spellCheck={false}
onChange={(e) => {
setApiKey(e.target.value)
}}
/>
</div>
<div className="flex w-full justify-between">
<div className="flex w-full justify-between py-1">
<div className="flex items-center space-x-2">
<Checkbox
onCheckedChange={(checked: boolean) => {
Expand Down Expand Up @@ -176,12 +182,10 @@ const BringApiKey = () => {
</div>
{isSecureOpen && (
<div className="flex flex-col space-y-5">
<p className="text-gray-9 text-sm">
Your API key is exclusively stored in session store with
encrypted. guaranteeing maximum security and privacy during your
usage.
<p className="text-muted-foreground text-sm">
Your API key is stored exclusively in the session store and is encrypted, guaranteeing maximum security and privacy.
</p>
<p className="text-gray-9 text-sm">
<p className="text-muted-foreground text-sm">
Our website is open source. Check out the code to see how we
protect your API key!
</p>
Expand All @@ -191,8 +195,8 @@ const BringApiKey = () => {
<div className="flex w-full justify-end space-x-2">
{isApiKeyFromSession && (
<Button
className="border-error hover:bg-error-lighter rounded-full px-6 transition-colors"
variant={"destructive"}
className="px-6"
variant={"outline"}
onClick={() => handleDelete()}
disabled={saving || deleting}
>
Expand All @@ -210,7 +214,7 @@ const BringApiKey = () => {
disabled={
saving || deleting || !isCorrectApiKey(apiKey) || !accepted
}
className="rounded-full px-6 transition-colors"
className="px-6"
onClick={() => saveApiKey(apiKey)}
>
<Loader2Icon
Expand Down
2 changes: 2 additions & 0 deletions components/dashboard/dashboard-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {

import SiteAssets from "../site-assets"
import { UserAvatar } from "./user-avatar"
import BringApiKey from "@/components/bring-api-key"

interface UserAccountNavProps extends React.HTMLAttributes<HTMLDivElement> {
user: Pick<User, "name" | "image" | "email">
Expand All @@ -32,6 +33,7 @@ function DashboardHeader({ user }: UserAccountNavProps) {
</div>

<div className={`flex h-full w-1/2 items-center justify-end`}>
<BringApiKey className="w-fit mr-6" variant={"outline"} />
<DropdownMenu>
<DropdownMenuTrigger>
<UserAvatar
Expand Down
2 changes: 1 addition & 1 deletion components/editor/ai-tools-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function AIToolsSection() {
<>
<div
className={`relative hidden h-full ${
isToolsPanelCollapsed ? "invisible w-0 opacity-0" : "min-w-[18%]"
isToolsPanelCollapsed ? "invisible w-0 opacity-0" : "min-w-[19%]"
} flex-col items-center border-r p-6 lg:flex `}
>
<Tabs defaultValue="tools" className="w-full">
Expand Down
2 changes: 1 addition & 1 deletion components/editor/ai-tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {

import { Button } from "../ui/button"
import { Input } from "../ui/input"
import UpgradeToPRODialog from "./upgrade-to-pro-dialog"
import UpgradeToPRODialog from "../upgrade-to-pro-dialog"

function AITools() {
const [requestingToAPI, setRequestingToAPI] = useState(false)
Expand Down
2 changes: 1 addition & 1 deletion components/editor/ask-ai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import CodeBlock from "../code-block"
import { Button } from "../ui/button"
import { Input } from "../ui/input"
import ParseMarkdown from "./parse-markdown"
import UpgradeToPRODialog from "./upgrade-to-pro-dialog"
import UpgradeToPRODialog from "../upgrade-to-pro-dialog"

function AskAI() {
const [isDialogOpen, setIsDialogOpen] = useState(false)
Expand Down
2 changes: 1 addition & 1 deletion components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const buttonVariants = cva(
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
"shadow-border-small bg-transparent hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { useRouter } from "next/navigation"

import BringApiKey from "@/components/bring-api-key"

import { Button } from "../ui/button"
import { Button } from "./ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "../ui/dialog"
} from "./ui/dialog"

function UpgradeToPRODialog({
open,
Expand All @@ -36,14 +36,15 @@ function UpgradeToPRODialog({
plan for unlimited AI access.
</DialogDescription>

<div className="mt-4 flex w-full space-x-4">
<BringApiKey />
<div className="mt-6 flex flex-col w-full items-center space-x-2">
<Button
onClick={() => router.push("/dashboard/billing")}
className="mt-2 w-full"
className="w-full"
>
Upgrade
</Button>
<span className="py-1.5">or</span>
<BringApiKey variant={"outline"} />
</div>
</DialogContent>
</Dialog>
Expand Down
24 changes: 2 additions & 22 deletions config/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,6 @@ export const example_vision_api_messages = [
export const example_chat_api_messages = [
{
role: "user",
content: "Hello, I'm Arshad.",
},
{
role: "assistant",
content: "Hi, Arshad. How can I help you today?",
},
{
role: "user",
content: "I need help with writing a blog post.",
},
{
role: "assistant",
content: "Sure, I can help you with that.",
},
{
role: "user",
content: "Great, thanks!",
},
{
role: "assistant",
content: "No problem!",
},
content: "Hey",
}
]
Loading

0 comments on commit 96927c8

Please sign in to comment.