From df66eef919a3eda0569c94b7ab79523aa3957968 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sun, 26 Mar 2023 11:58:25 +0000 Subject: [PATCH 1/4] feat: support using user api key --- app/api/chat-stream/route.ts | 16 +++++++++++----- app/api/chat/route.ts | 29 ++++++++++++++++------------- app/components/settings.tsx | 14 ++++++++++++++ app/locales/cn.ts | 5 +++++ app/requests.ts | 4 ++++ app/store/access.ts | 6 ++++++ middleware.ts | 3 ++- 7 files changed, 58 insertions(+), 19 deletions(-) diff --git a/app/api/chat-stream/route.ts b/app/api/chat-stream/route.ts index 16c5950c..8803a425 100644 --- a/app/api/chat-stream/route.ts +++ b/app/api/chat-stream/route.ts @@ -2,19 +2,25 @@ import type { ChatRequest } from "../chat/typing"; import { createParser } from "eventsource-parser"; import { NextRequest } from "next/server"; -const apiKey = process.env.OPENAI_API_KEY; - -async function createStream(payload: ReadableStream) { +async function createStream(req: NextRequest) { const encoder = new TextEncoder(); const decoder = new TextDecoder(); + let apiKey = process.env.OPENAI_API_KEY; + + const userApiKey = req.headers.get("token"); + if (userApiKey) { + apiKey = userApiKey; + console.log("[Stream] using user api key"); + } + const res = await fetch("https://api.openai.com/v1/chat/completions", { headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }, method: "POST", - body: payload, + body: req.body, }); const stream = new ReadableStream({ @@ -49,7 +55,7 @@ async function createStream(payload: ReadableStream) { export async function POST(req: NextRequest) { try { - const stream = await createStream(req.body!); + const stream = await createStream(req); return new Response(stream); } catch (error) { console.error("[Chat Stream]", error); diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index c4e41ca3..18c7db14 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,23 +1,26 @@ import { OpenAIApi, Configuration } from "openai"; import { ChatRequest } from "./typing"; -const apiKey = process.env.OPENAI_API_KEY; - -const openai = new OpenAIApi( - new Configuration({ - apiKey, - }) -); - export async function POST(req: Request) { try { - const requestBody = (await req.json()) as ChatRequest; - const completion = await openai!.createChatCompletion( - { - ...requestBody, - } + let apiKey = process.env.OPENAI_API_KEY; + + const userApiKey = req.headers.get("token"); + if (userApiKey) { + apiKey = userApiKey; + } + + const openai = new OpenAIApi( + new Configuration({ + apiKey, + }) ); + const requestBody = (await req.json()) as ChatRequest; + const completion = await openai!.createChatCompletion({ + ...requestBody, + }); + return new Response(JSON.stringify(completion.data)); } catch (e) { console.error("[Chat] ", e); diff --git a/app/components/settings.tsx b/app/components/settings.tsx index a0a477af..56165daa 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -257,6 +257,20 @@ export function Settings(props: { closeSettings: () => void }) { <> )} + + { + accessStore.updateToken(e.currentTarget.value); + }} + > + + 0) { + headers["token"] = accessStore.token; + } + return headers; } diff --git a/app/store/access.ts b/app/store/access.ts index 4ec2111c..9c61dfa0 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -4,7 +4,9 @@ import { queryMeta } from "../utils"; export interface AccessControlStore { accessCode: string; + token: string; + updateToken: (_: string) => void; updateCode: (_: string) => void; enabledAccessControl: () => boolean; } @@ -14,6 +16,7 @@ export const ACCESS_KEY = "access-control"; export const useAccessStore = create()( persist( (set, get) => ({ + token: "", accessCode: "", enabledAccessControl() { return queryMeta("access") === "enabled"; @@ -21,6 +24,9 @@ export const useAccessStore = create()( updateCode(code: string) { set((state) => ({ accessCode: code })); }, + updateToken(token: string) { + set((state) => ({ token })); + }, }), { name: ACCESS_KEY, diff --git a/middleware.ts b/middleware.ts index 0ab3a101..7e671ff1 100644 --- a/middleware.ts +++ b/middleware.ts @@ -8,13 +8,14 @@ export const config = { export function middleware(req: NextRequest, res: NextResponse) { const accessCode = req.headers.get("access-code"); + const token = req.headers.get("token"); const hashedCode = md5.hash(accessCode ?? "").trim(); console.log("[Auth] allowed hashed codes: ", [...ACCESS_CODES]); console.log("[Auth] got access code:", accessCode); console.log("[Auth] hashed access code:", hashedCode); - if (ACCESS_CODES.size > 0 && !ACCESS_CODES.has(hashedCode)) { + if (ACCESS_CODES.size > 0 && !ACCESS_CODES.has(hashedCode) && !token) { return NextResponse.json( { needAccessCode: true, From e57bd5180939f4f134c5a3fb47db7f7203ad6f4a Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sun, 26 Mar 2023 12:29:02 +0000 Subject: [PATCH 2/4] feat: #9 add copy code button --- app/components/markdown.tsx | 29 +++++++++++++++++++++++++---- app/page.tsx | 2 +- app/styles/globals.scss | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 83bfe4ef..6e0e6d86 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -4,15 +4,36 @@ import RemarkMath from "remark-math"; import RehypeKatex from "rehype-katex"; import RemarkGfm from "remark-gfm"; import RehypePrsim from "rehype-prism-plus"; +import { useRef } from "react"; +import { copyToClipboard } from "../utils"; + +export function PreCode(props: { children: any }) { + const ref = useRef(null); + + return ( +
+       {
+          if (ref.current) {
+            const code = ref.current.innerText;
+            copyToClipboard(code);
+          }
+        }}
+      >
+      {props.children}
+    
+ ); +} export function Markdown(props: { content: string }) { return ( {props.content} diff --git a/app/page.tsx b/app/page.tsx index a986da3f..54300e71 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,5 @@ import { Analytics } from "@vercel/analytics/react"; -import { Home } from './components/home' +import { Home } from "./components/home"; export default function App() { return ( diff --git a/app/styles/globals.scss b/app/styles/globals.scss index e7d35226..46f074df 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -206,3 +206,36 @@ div.math { text-decoration: underline; } } + +pre { + position: relative; + + &:hover .copy-code-button { + pointer-events: all; + transform: translateX(0px); + opacity: 0.5; + } + + .copy-code-button { + position: absolute; + right: 10px; + cursor: pointer; + padding: 0px 5px; + background-color: var(--black); + color: var(--white); + border: var(--border-in-light); + border-radius: 10px; + transform: translateX(10px); + pointer-events: none; + opacity: 0; + transition: all ease 0.3s; + + &:after { + content: "copy"; + } + + &:hover { + opacity: 1; + } + } +} From b57663bf02d445fd100a82d0557cbd354506c0d8 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sun, 26 Mar 2023 12:32:22 +0000 Subject: [PATCH 3/4] feat: now support gpt-4 model --- app/store/app.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/store/app.ts b/app/store/app.ts index e587a731..703078ad 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -49,22 +49,24 @@ export interface ChatConfig { export type ModelConfig = ChatConfig["modelConfig"]; +const ENABLE_GPT4 = true; + export const ALL_MODELS = [ { name: "gpt-4", - available: false, + available: ENABLE_GPT4, }, { name: "gpt-4-0314", - available: false, + available: ENABLE_GPT4, }, { name: "gpt-4-32k", - available: false, + available: ENABLE_GPT4, }, { name: "gpt-4-32k-0314", - available: false, + available: ENABLE_GPT4, }, { name: "gpt-3.5-turbo", From f858407f9a7d3fc37c3b0405bd37fa4a0c08fb9d Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Sun, 26 Mar 2023 12:35:15 +0000 Subject: [PATCH 4/4] fixup --- app/locales/en.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/locales/en.ts b/app/locales/en.ts index e10898b3..5401cda4 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -74,6 +74,11 @@ const en: LocaleType = { SubTitle: "Will compress if uncompressed messages length exceeds the value", }, + Token: { + Title: "API Key", + SubTitle: "Use your key to ignore access code limit", + Placeholder: "OpenAI API Key", + }, AccessCode: { Title: "Access Code", SubTitle: "Access control enabled",