forked from XiaoMo/ChatGPT-Next-Web
feat: support using user api key
This commit is contained in:
parent
1e89fe14ac
commit
df66eef919
@ -2,19 +2,25 @@ import type { ChatRequest } from "../chat/typing";
|
|||||||
import { createParser } from "eventsource-parser";
|
import { createParser } from "eventsource-parser";
|
||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
const apiKey = process.env.OPENAI_API_KEY;
|
async function createStream(req: NextRequest) {
|
||||||
|
|
||||||
async function createStream(payload: ReadableStream<Uint8Array>) {
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const decoder = new TextDecoder();
|
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", {
|
const res = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${apiKey}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: payload,
|
body: req.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
@ -49,7 +55,7 @@ async function createStream(payload: ReadableStream<Uint8Array>) {
|
|||||||
|
|
||||||
export async function POST(req: NextRequest) {
|
export async function POST(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const stream = await createStream(req.body!);
|
const stream = await createStream(req);
|
||||||
return new Response(stream);
|
return new Response(stream);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[Chat Stream]", error);
|
console.error("[Chat Stream]", error);
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
import { OpenAIApi, Configuration } from "openai";
|
import { OpenAIApi, Configuration } from "openai";
|
||||||
import { ChatRequest } from "./typing";
|
import { ChatRequest } from "./typing";
|
||||||
|
|
||||||
const apiKey = process.env.OPENAI_API_KEY;
|
export async function POST(req: Request) {
|
||||||
|
try {
|
||||||
|
let apiKey = process.env.OPENAI_API_KEY;
|
||||||
|
|
||||||
const openai = new OpenAIApi(
|
const userApiKey = req.headers.get("token");
|
||||||
|
if (userApiKey) {
|
||||||
|
apiKey = userApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openai = new OpenAIApi(
|
||||||
new Configuration({
|
new Configuration({
|
||||||
apiKey,
|
apiKey,
|
||||||
})
|
})
|
||||||
);
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
try {
|
|
||||||
const requestBody = (await req.json()) as ChatRequest;
|
|
||||||
const completion = await openai!.createChatCompletion(
|
|
||||||
{
|
|
||||||
...requestBody,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const requestBody = (await req.json()) as ChatRequest;
|
||||||
|
const completion = await openai!.createChatCompletion({
|
||||||
|
...requestBody,
|
||||||
|
});
|
||||||
|
|
||||||
return new Response(JSON.stringify(completion.data));
|
return new Response(JSON.stringify(completion.data));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[Chat] ", e);
|
console.error("[Chat] ", e);
|
||||||
|
@ -257,6 +257,20 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
title={Locale.Settings.Token.Title}
|
||||||
|
subTitle={Locale.Settings.Token.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
value={accessStore.token}
|
||||||
|
type="text"
|
||||||
|
placeholder={Locale.Settings.Token.Placeholder}
|
||||||
|
onChange={(e) => {
|
||||||
|
accessStore.updateToken(e.currentTarget.value);
|
||||||
|
}}
|
||||||
|
></input>
|
||||||
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title={Locale.Settings.HistoryCount.Title}
|
title={Locale.Settings.HistoryCount.Title}
|
||||||
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
||||||
|
@ -69,6 +69,11 @@ const cn = {
|
|||||||
Title: "历史消息长度压缩阈值",
|
Title: "历史消息长度压缩阈值",
|
||||||
SubTitle: "当未压缩的历史消息超过该值时,将进行压缩",
|
SubTitle: "当未压缩的历史消息超过该值时,将进行压缩",
|
||||||
},
|
},
|
||||||
|
Token: {
|
||||||
|
Title: "API Key",
|
||||||
|
SubTitle: "使用自己的 Key 可绕过受控访问限制",
|
||||||
|
Placeholder: "OpenAI API Key",
|
||||||
|
},
|
||||||
AccessCode: {
|
AccessCode: {
|
||||||
Title: "访问码",
|
Title: "访问码",
|
||||||
SubTitle: "现在是受控访问状态",
|
SubTitle: "现在是受控访问状态",
|
||||||
|
@ -35,6 +35,10 @@ function getHeaders() {
|
|||||||
headers["access-code"] = accessStore.accessCode;
|
headers["access-code"] = accessStore.accessCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (accessStore.token && accessStore.token.length > 0) {
|
||||||
|
headers["token"] = accessStore.token;
|
||||||
|
}
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ import { queryMeta } from "../utils";
|
|||||||
|
|
||||||
export interface AccessControlStore {
|
export interface AccessControlStore {
|
||||||
accessCode: string;
|
accessCode: string;
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
updateToken: (_: string) => void;
|
||||||
updateCode: (_: string) => void;
|
updateCode: (_: string) => void;
|
||||||
enabledAccessControl: () => boolean;
|
enabledAccessControl: () => boolean;
|
||||||
}
|
}
|
||||||
@ -14,6 +16,7 @@ export const ACCESS_KEY = "access-control";
|
|||||||
export const useAccessStore = create<AccessControlStore>()(
|
export const useAccessStore = create<AccessControlStore>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
|
token: "",
|
||||||
accessCode: "",
|
accessCode: "",
|
||||||
enabledAccessControl() {
|
enabledAccessControl() {
|
||||||
return queryMeta("access") === "enabled";
|
return queryMeta("access") === "enabled";
|
||||||
@ -21,6 +24,9 @@ export const useAccessStore = create<AccessControlStore>()(
|
|||||||
updateCode(code: string) {
|
updateCode(code: string) {
|
||||||
set((state) => ({ accessCode: code }));
|
set((state) => ({ accessCode: code }));
|
||||||
},
|
},
|
||||||
|
updateToken(token: string) {
|
||||||
|
set((state) => ({ token }));
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: ACCESS_KEY,
|
name: ACCESS_KEY,
|
||||||
|
@ -8,13 +8,14 @@ export const config = {
|
|||||||
|
|
||||||
export function middleware(req: NextRequest, res: NextResponse) {
|
export function middleware(req: NextRequest, res: NextResponse) {
|
||||||
const accessCode = req.headers.get("access-code");
|
const accessCode = req.headers.get("access-code");
|
||||||
|
const token = req.headers.get("token");
|
||||||
const hashedCode = md5.hash(accessCode ?? "").trim();
|
const hashedCode = md5.hash(accessCode ?? "").trim();
|
||||||
|
|
||||||
console.log("[Auth] allowed hashed codes: ", [...ACCESS_CODES]);
|
console.log("[Auth] allowed hashed codes: ", [...ACCESS_CODES]);
|
||||||
console.log("[Auth] got access code:", accessCode);
|
console.log("[Auth] got access code:", accessCode);
|
||||||
console.log("[Auth] hashed access code:", hashedCode);
|
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(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
needAccessCode: true,
|
needAccessCode: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user