Skip to content

Commit b0d28eb

Browse files
Merge branch 'main' into main
2 parents 064e964 + 613d67e commit b0d28eb

Some content is hidden

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

82 files changed

+3972
-475
lines changed

.github/workflows/deploy_preview.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ name: VercelPreviewDeployment
33
on:
44
pull_request_target:
55
types:
6-
- opened
7-
- synchronize
8-
- reopened
6+
- review_requested
97

108
env:
119
VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }}

.github/workflows/test.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Run Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- "!*"
9+
pull_request:
10+
types:
11+
- review_requested
12+
13+
jobs:
14+
test:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Node.js
22+
uses: actions/setup-node@v3
23+
with:
24+
node-version: 18
25+
cache: "yarn"
26+
27+
- name: Cache node_modules
28+
uses: actions/cache@v4
29+
with:
30+
path: node_modules
31+
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
32+
restore-keys: |
33+
${{ runner.os }}-node_modules-
34+
35+
- name: Install dependencies
36+
run: yarn install
37+
38+
- name: Run Jest tests
39+
run: yarn test:ci

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
1212

1313
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。
1414

15+
[![Saas][Saas-image]][saas-url]
1516
[![Web][Web-image]][web-url]
1617
[![Windows][Windows-image]][download-url]
1718
[![MacOS][MacOS-image]][download-url]
1819
[![Linux][Linux-image]][download-url]
1920

20-
[Web App](https://app.nextchat.dev/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev)
21+
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev)
2122

22-
[网页版](https://app.nextchat.dev/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues)
23+
[NextChatAI](https://nextchat.dev/chat) / [网页版](https://app.nextchat.dev) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues)
2324

25+
[saas-url]: https://nextchat.dev/chat?utm_source=readme
26+
[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
2427
[web-url]: https://app.nextchat.dev/
2528
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
2629
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
@@ -60,7 +63,7 @@ For enterprise inquiries, please contact: **[email protected]**
6063

6164
企业版咨询: **[email protected]**
6265

63-
<img width="300" src="https://github.com/user-attachments/assets/3daeb7b6-ab63-4542-9141-2e4a12c80601">
66+
<img width="300" src="https://github.com/user-attachments/assets/3d4305ac-6e95-489e-884b-51d51db5f692">
6467

6568
## Features
6669

@@ -97,6 +100,7 @@ For enterprise inquiries, please contact: **[email protected]**
97100

98101
## What's New
99102

103+
- 🚀 v2.15.4 The Application supports using Tauri fetch LLM API, MORE SECURITY! [#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379)
100104
- 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
101105
- 🚀 v2.14.0 Now supports Artifacts & SD
102106
- 🚀 v2.10.1 support Google Gemini Pro model.
@@ -134,6 +138,7 @@ For enterprise inquiries, please contact: **[email protected]**
134138

135139
## 最新动态
136140

141+
- 🚀 v2.15.4 客户端支持Tauri本地直接调用大模型API,更安全![#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379)
137142
- 🚀 v2.15.0 现在支持插件功能了!了解更多:[NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
138143
- 🚀 v2.14.0 现在支持 Artifacts & SD 了。
139144
- 🚀 v2.10.1 现在支持 Gemini Pro 模型。
@@ -172,7 +177,7 @@ We recommend that you follow the steps below to re-deploy:
172177

173178
### Enable Automatic Updates
174179

175-
> If you encounter a failure of Upstream Sync execution, please manually sync fork once.
180+
> If you encounter a failure of Upstream Sync execution, please [manually update code](./README.md#manually-updating-code).
176181
177182
After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour:
178183

@@ -329,9 +334,9 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model
329334

330335
User `-all` to disable all default models, `+all` to enable all default models.
331336

332-
For Azure: use `modelName@azure=deploymentName` to customize model name and deployment name.
333-
> Example: `+gpt-3.5-turbo@azure=gpt35` will show option `gpt35(Azure)` in model list.
334-
> If you only can use Azure model, `-all,+gpt-3.5-turbo@azure=gpt35` will `gpt35(Azure)` the only option in model list.
337+
For Azure: use `modelName@Azure=deploymentName` to customize model name and deployment name.
338+
> Example: `+gpt-3.5-turbo@Azure=gpt35` will show option `gpt35(Azure)` in model list.
339+
> If you only can use Azure model, `-all,+gpt-3.5-turbo@Azure=gpt35` will `gpt35(Azure)` the only option in model list.
335340
336341
For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name.
337342
> Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list.

README_CN.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
1010

11-
[企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) /[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
11+
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
1212

1313
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
1414

@@ -54,7 +54,7 @@
5454

5555
### 打开自动更新
5656

57-
> 如果你遇到了 Upstream Sync 执行错误,请手动 Sync Fork 一次!
57+
> 如果你遇到了 Upstream Sync 执行错误,[手动 Sync Fork 一次](./README_CN.md#手动更新代码)
5858
5959
当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows,并启用 Upstream Sync Action,启用之后即可开启每小时定时自动更新:
6060

@@ -216,9 +216,9 @@ ByteDance Api Url.
216216
217217
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
218218

219-
在Azure的模式下,支持使用`modelName@azure=deploymentName`的方式配置模型名称和部署名称(deploy-name)
220-
> 示例:`+gpt-3.5-turbo@azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。
221-
> 如果你只能使用Azure模式,那么设置 `-all,+gpt-3.5-turbo@azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)`
219+
在Azure的模式下,支持使用`modelName@Azure=deploymentName`的方式配置模型名称和部署名称(deploy-name)
220+
> 示例:`+gpt-3.5-turbo@Azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。
221+
> 如果你只能使用Azure模式,那么设置 `-all,+gpt-3.5-turbo@Azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)`
222222
223223
在ByteDance的模式下,支持使用`modelName@bytedance=deploymentName`的方式配置模型名称和部署名称(deploy-name)
224224
> 示例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx`这个配置会在模型列表显示一个`Doubao-lite-4k(ByteDance)`的选项

README_JA.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。
77

8-
[企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
8+
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
99

1010
[<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
1111

@@ -54,7 +54,7 @@
5454

5555
### 自動更新を開く
5656

57-
> Upstream Sync の実行エラーが発生した場合は、手動で Sync Fork してください!
57+
> Upstream Sync の実行エラーが発生した場合は、[手動で Sync Fork](./README_JA.md#手動でコードを更新する) してください!
5858
5959
プロジェクトを fork した後、GitHub の制限により、fork 後のプロジェクトの Actions ページで Workflows を手動で有効にし、Upstream Sync Action を有効にする必要があります。有効化後、毎時の定期自動更新が可能になります:
6060

@@ -207,8 +207,8 @@ ByteDance API の URL。
207207
208208
モデルリストを管理します。`+` でモデルを追加し、`-` でモデルを非表示にし、`モデル名=表示名` でモデルの表示名をカスタマイズし、カンマで区切ります。
209209

210-
Azure モードでは、`modelName@azure=deploymentName` 形式でモデル名とデプロイ名(deploy-name)を設定できます。
211-
> 例:`+gpt-3.5-turbo@azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。
210+
Azure モードでは、`modelName@Azure=deploymentName` 形式でモデル名とデプロイ名(deploy-name)を設定できます。
211+
> 例:`+gpt-3.5-turbo@Azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。
212212
213213
ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデル名とデプロイ名(deploy-name)を設定できます。
214214
> 例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` この設定でモデルリストに `Doubao-lite-4k(ByteDance)` のオプションが表示されます。

app/api/[provider]/[...path]/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { handle as alibabaHandler } from "../../alibaba";
1010
import { handle as moonshotHandler } from "../../moonshot";
1111
import { handle as stabilityHandler } from "../../stability";
1212
import { handle as iflytekHandler } from "../../iflytek";
13+
import { handle as xaiHandler } from "../../xai";
1314
import { handle as proxyHandler } from "../../proxy";
1415

1516
async function handle(
@@ -38,6 +39,8 @@ async function handle(
3839
return stabilityHandler(req, { params });
3940
case ApiPath.Iflytek:
4041
return iflytekHandler(req, { params });
42+
case ApiPath.XAI:
43+
return xaiHandler(req, { params });
4144
case ApiPath.OpenAI:
4245
return openaiHandler(req, { params });
4346
default:

app/api/auth.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
9292
systemApiKey =
9393
serverConfig.iflytekApiKey + ":" + serverConfig.iflytekApiSecret;
9494
break;
95+
case ModelProvider.XAI:
96+
systemApiKey = serverConfig.xaiApiKey;
97+
break;
9598
case ModelProvider.GPT:
9699
default:
97100
if (req.nextUrl.pathname.includes("azure/deployments")) {

app/api/google.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export async function handle(
2323
});
2424
}
2525

26-
const bearToken = req.headers.get("Authorization") ?? "";
26+
const bearToken =
27+
req.headers.get("x-goog-api-key") || req.headers.get("Authorization") || "";
2728
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
2829

2930
const apiKey = token ? token : serverConfig.googleApiKey;
@@ -91,15 +92,18 @@ async function request(req: NextRequest, apiKey: string) {
9192
},
9293
10 * 60 * 1000,
9394
);
94-
const fetchUrl = `${baseUrl}${path}?key=${apiKey}${
95-
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : ""
95+
const fetchUrl = `${baseUrl}${path}${
96+
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "?alt=sse" : ""
9697
}`;
9798

9899
console.log("[Fetch Url] ", fetchUrl);
99100
const fetchOptions: RequestInit = {
100101
headers: {
101102
"Content-Type": "application/json",
102103
"Cache-Control": "no-store",
104+
"x-goog-api-key":
105+
req.headers.get("x-goog-api-key") ||
106+
(req.headers.get("Authorization") ?? "").replace("Bearer ", ""),
103107
},
104108
method: req.method,
105109
body: req.body,

app/api/openai.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { NextRequest, NextResponse } from "next/server";
66
import { auth } from "./auth";
77
import { requestOpenai } from "./common";
88

9-
const ALLOWD_PATH = new Set(Object.values(OpenaiPath));
9+
const ALLOWED_PATH = new Set(Object.values(OpenaiPath));
1010

1111
function getModels(remoteModelRes: OpenAIListModelResponse) {
1212
const config = getServerSideConfig();
@@ -34,7 +34,7 @@ export async function handle(
3434

3535
const subpath = params.path.join("/");
3636

37-
if (!ALLOWD_PATH.has(subpath)) {
37+
if (!ALLOWED_PATH.has(subpath)) {
3838
console.log("[OpenAI Route] forbidden path ", subpath);
3939
return NextResponse.json(
4040
{

app/api/xai.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { getServerSideConfig } from "@/app/config/server";
2+
import {
3+
XAI_BASE_URL,
4+
ApiPath,
5+
ModelProvider,
6+
ServiceProvider,
7+
} from "@/app/constant";
8+
import { prettyObject } from "@/app/utils/format";
9+
import { NextRequest, NextResponse } from "next/server";
10+
import { auth } from "@/app/api/auth";
11+
import { isModelAvailableInServer } from "@/app/utils/model";
12+
13+
const serverConfig = getServerSideConfig();
14+
15+
export async function handle(
16+
req: NextRequest,
17+
{ params }: { params: { path: string[] } },
18+
) {
19+
console.log("[XAI Route] params ", params);
20+
21+
if (req.method === "OPTIONS") {
22+
return NextResponse.json({ body: "OK" }, { status: 200 });
23+
}
24+
25+
const authResult = auth(req, ModelProvider.XAI);
26+
if (authResult.error) {
27+
return NextResponse.json(authResult, {
28+
status: 401,
29+
});
30+
}
31+
32+
try {
33+
const response = await request(req);
34+
return response;
35+
} catch (e) {
36+
console.error("[XAI] ", e);
37+
return NextResponse.json(prettyObject(e));
38+
}
39+
}
40+
41+
async function request(req: NextRequest) {
42+
const controller = new AbortController();
43+
44+
// alibaba use base url or just remove the path
45+
let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.XAI, "");
46+
47+
let baseUrl = serverConfig.xaiUrl || XAI_BASE_URL;
48+
49+
if (!baseUrl.startsWith("http")) {
50+
baseUrl = `https://${baseUrl}`;
51+
}
52+
53+
if (baseUrl.endsWith("/")) {
54+
baseUrl = baseUrl.slice(0, -1);
55+
}
56+
57+
console.log("[Proxy] ", path);
58+
console.log("[Base Url]", baseUrl);
59+
60+
const timeoutId = setTimeout(
61+
() => {
62+
controller.abort();
63+
},
64+
10 * 60 * 1000,
65+
);
66+
67+
const fetchUrl = `${baseUrl}${path}`;
68+
const fetchOptions: RequestInit = {
69+
headers: {
70+
"Content-Type": "application/json",
71+
Authorization: req.headers.get("Authorization") ?? "",
72+
},
73+
method: req.method,
74+
body: req.body,
75+
redirect: "manual",
76+
// @ts-ignore
77+
duplex: "half",
78+
signal: controller.signal,
79+
};
80+
81+
// #1815 try to refuse some request to some models
82+
if (serverConfig.customModels && req.body) {
83+
try {
84+
const clonedBody = await req.text();
85+
fetchOptions.body = clonedBody;
86+
87+
const jsonBody = JSON.parse(clonedBody) as { model?: string };
88+
89+
// not undefined and is false
90+
if (
91+
isModelAvailableInServer(
92+
serverConfig.customModels,
93+
jsonBody?.model as string,
94+
ServiceProvider.XAI as string,
95+
)
96+
) {
97+
return NextResponse.json(
98+
{
99+
error: true,
100+
message: `you are not allowed to use ${jsonBody?.model} model`,
101+
},
102+
{
103+
status: 403,
104+
},
105+
);
106+
}
107+
} catch (e) {
108+
console.error(`[XAI] filter`, e);
109+
}
110+
}
111+
try {
112+
const res = await fetch(fetchUrl, fetchOptions);
113+
114+
// to prevent browser prompt for credentials
115+
const newHeaders = new Headers(res.headers);
116+
newHeaders.delete("www-authenticate");
117+
// to disable nginx buffering
118+
newHeaders.set("X-Accel-Buffering", "no");
119+
120+
return new Response(res.body, {
121+
status: res.status,
122+
statusText: res.statusText,
123+
headers: newHeaders,
124+
});
125+
} finally {
126+
clearTimeout(timeoutId);
127+
}
128+
}

0 commit comments

Comments
 (0)