diff --git a/README.md b/README.md
index 2001d4d88..c72c791b2 100644
--- a/README.md
+++ b/README.md
@@ -12,15 +12,18 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。
+[![Saas][Saas-image]][saas-url]
[![Web][Web-image]][web-url]
[![Windows][Windows-image]][download-url]
[![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url]
-[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)
+[NextChatAI](https://nextchat.dev/chat) / [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)
-[网页版](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)
+[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)
+[saas-url]: https://nextchat.dev/chat
+[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
[web-url]: https://app.nextchat.dev/
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
diff --git a/README_CN.md b/README_CN.md
index 7831e2ee9..c5d02477c 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -8,7 +8,7 @@
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
-[企业版](#%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)
+[NextChatAI](https://nextchat.dev/chat) / [企业版](#%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)
[
](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) [
](https://zeabur.com/templates/ZBUEFA) [
](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
diff --git a/README_JA.md b/README_JA.md
index 1716089af..2b0a3ab78 100644
--- a/README_JA.md
+++ b/README_JA.md
@@ -5,7 +5,7 @@
ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。
-[企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
+[NextChatAI](https://nextchat.dev/chat) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
[
](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) [
](https://zeabur.com/templates/ZBUEFA) [
](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
diff --git a/app/api/google.ts b/app/api/google.ts
index e6ab47256..7d3f08be4 100644
--- a/app/api/google.ts
+++ b/app/api/google.ts
@@ -91,7 +91,7 @@ async function request(req: NextRequest, apiKey: string) {
},
10 * 60 * 1000,
);
- const fetchUrl = `${baseUrl}${path}?key=${apiKey}${
+ const fetchUrl = `${baseUrl}${path}${
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : ""
}`;
@@ -100,6 +100,9 @@ async function request(req: NextRequest, apiKey: string) {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
+ "x-google-api-key":
+ req.headers.get("x-google-api-key") ||
+ (req.headers.get("Authorization") ?? "").replace("Bearer ", ""),
},
method: req.method,
body: req.body,
diff --git a/app/client/api.ts b/app/client/api.ts
index 8285b4d9f..48bbde6bc 100644
--- a/app/client/api.ts
+++ b/app/client/api.ts
@@ -272,7 +272,13 @@ export function getHeaders(ignoreHeaders: boolean = false) {
}
function getAuthHeader(): string {
- return isAzure ? "api-key" : isAnthropic ? "x-api-key" : "Authorization";
+ return isAzure
+ ? "api-key"
+ : isAnthropic
+ ? "x-api-key"
+ : isGoogle
+ ? "x-goog-api-key"
+ : "Authorization";
}
const {
@@ -283,14 +289,15 @@ export function getHeaders(ignoreHeaders: boolean = false) {
apiKey,
isEnabledAccessControl,
} = getConfig();
- // when using google api in app, not set auth header
- if (isGoogle && clientConfig?.isApp) return headers;
// when using baidu api in app, not set auth header
if (isBaidu && clientConfig?.isApp) return headers;
const authHeader = getAuthHeader();
- const bearerToken = getBearerToken(apiKey, isAzure || isAnthropic);
+ const bearerToken = getBearerToken(
+ apiKey,
+ isAzure || isAnthropic || isGoogle,
+ );
if (bearerToken) {
headers[authHeader] = bearerToken;
diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts
index ecb5ce44b..3c2607271 100644
--- a/app/client/platforms/google.ts
+++ b/app/client/platforms/google.ts
@@ -48,10 +48,6 @@ export class GeminiProApi implements LLMApi {
let chatPath = [baseUrl, path].join("/");
chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse";
- // if chatPath.startsWith('http') then add key in query string
- if (chatPath.startsWith("http") && accessStore.googleApiKey) {
- chatPath += `&key=${accessStore.googleApiKey}`;
- }
return chatPath;
}
extractMessage(res: any) {
diff --git a/app/components/auth.module.scss b/app/components/auth.module.scss
index 6630c0613..fe143b428 100644
--- a/app/components/auth.module.scss
+++ b/app/components/auth.module.scss
@@ -1,12 +1,70 @@
.auth-page {
display: flex;
- justify-content: center;
+ justify-content: flex-start;
align-items: center;
height: 100%;
width: 100%;
flex-direction: column;
+ .top-banner {
+ position: relative;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 12px 64px;
+ box-sizing: border-box;
+ background: var(--second);
+ .top-banner-inner {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 14px;
+ line-height: 150%;
+ span {
+ gap: 8px;
+ a {
+ display: inline-flex;
+ align-items: center;
+ text-decoration: none;
+ margin-left: 8px;
+ color: var(--primary);
+ }
+ }
+ }
+ .top-banner-close {
+ cursor: pointer;
+ position: absolute;
+ top: 50%;
+ right: 48px;
+ transform: translateY(-50%);
+ }
+ }
+
+ @media (max-width: 600px) {
+ .top-banner {
+ padding: 12px 24px 12px 12px;
+ .top-banner-close {
+ right: 10px;
+ }
+ .top-banner-inner {
+ .top-banner-logo {
+ margin-right: 8px;
+ }
+ }
+ }
+ }
+
+ .auth-header {
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ animation: slide-in-from-top ease 0.3s;
+ }
.auth-logo {
+ margin-top: 10vh;
transform: scale(1.4);
}
@@ -14,6 +72,7 @@
font-size: 24px;
font-weight: bold;
line-height: 2;
+ margin-bottom: 1vh;
}
.auth-tips {
@@ -24,6 +83,10 @@
margin: 3vh 0;
}
+ .auth-input-second {
+ margin: 0 0 3vh 0;
+ }
+
.auth-actions {
display: flex;
justify-content: center;
diff --git a/app/components/auth.tsx b/app/components/auth.tsx
index 57118349b..e19512d87 100644
--- a/app/components/auth.tsx
+++ b/app/components/auth.tsx
@@ -1,21 +1,34 @@
import styles from "./auth.module.scss";
import { IconButton } from "./button";
-
+import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
-import { Path } from "../constant";
+import { Path, SAAS_CHAT_URL } from "../constant";
import { useAccessStore } from "../store";
import Locale from "../locales";
-
+import Delete from "../icons/close.svg";
+import Arrow from "../icons/arrow.svg";
+import Logo from "../icons/logo.svg";
+import { useMobileScreen } from "@/app/utils";
import BotIcon from "../icons/bot.svg";
-import { useEffect } from "react";
import { getClientConfig } from "../config/client";
+import LeftIcon from "@/app/icons/left.svg";
+import { safeLocalStorage } from "@/app/utils";
+import {
+ trackSettingsPageGuideToCPaymentClick,
+ trackAuthorizationPageButtonToCPaymentClick,
+} from "../utils/auth-settings-events";
+const storage = safeLocalStorage();
export function AuthPage() {
const navigate = useNavigate();
const accessStore = useAccessStore();
-
const goHome = () => navigate(Path.Home);
const goChat = () => navigate(Path.Chat);
+ const goSaas = () => {
+ trackAuthorizationPageButtonToCPaymentClick();
+ window.location.href = SAAS_CHAT_URL;
+ };
+
const resetAccessCode = () => {
accessStore.update((access) => {
access.openaiApiKey = "";
@@ -32,6 +45,14 @@ export function AuthPage() {
return (
+
+
+ }
+ text={Locale.Auth.Return}
+ onClick={() => navigate(Path.Home)}
+ >
+
@@ -65,7 +86,7 @@ export function AuthPage() {
}}
/>
{
- resetAccessCode();
- goHome();
+ goSaas();
}}
/>
);
}
+
+function TopBanner() {
+ const [isHovered, setIsHovered] = useState(false);
+ const [isVisible, setIsVisible] = useState(true);
+ const isMobile = useMobileScreen();
+ useEffect(() => {
+ // 检查 localStorage 中是否有标记
+ const bannerDismissed = storage.getItem("bannerDismissed");
+ // 如果标记不存在,存储默认值并显示横幅
+ if (!bannerDismissed) {
+ storage.setItem("bannerDismissed", "false");
+ setIsVisible(true); // 显示横幅
+ } else if (bannerDismissed === "true") {
+ // 如果标记为 "true",则隐藏横幅
+ setIsVisible(false);
+ }
+ }, []);
+
+ const handleMouseEnter = () => {
+ setIsHovered(true);
+ };
+
+ const handleMouseLeave = () => {
+ setIsHovered(false);
+ };
+
+ const handleClose = () => {
+ setIsVisible(false);
+ storage.setItem("bannerDismissed", "true");
+ };
+
+ if (!isVisible) {
+ return null;
+ }
+ return (
+
+
+ {(isHovered || isMobile) && (
+
+ )}
+
+ );
+}
diff --git a/app/components/button.module.scss b/app/components/button.module.scss
index e332df2d2..05248bee8 100644
--- a/app/components/button.module.scss
+++ b/app/components/button.module.scss
@@ -5,7 +5,6 @@
align-items: center;
justify-content: center;
padding: 10px;
-
cursor: pointer;
transition: all 0.3s ease;
overflow: hidden;
diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx
index 8936e0b5b..6e340d065 100644
--- a/app/components/markdown.tsx
+++ b/app/components/markdown.tsx
@@ -21,6 +21,7 @@ import {
} from "./artifacts";
import { useChatStore } from "../store";
import { IconButton } from "./button";
+
import { useAppConfig } from "../store/config";
export function Mermaid(props: { code: string }) {
diff --git a/app/components/model-config.module.scss b/app/components/model-config.module.scss
new file mode 100644
index 000000000..40ba03f86
--- /dev/null
+++ b/app/components/model-config.module.scss
@@ -0,0 +1,7 @@
+.select-compress-model {
+ width: 60%;
+ select {
+ max-width: 100%;
+ white-space: normal;
+ }
+}
diff --git a/app/components/model-config.tsx b/app/components/model-config.tsx
index 04cd3ff01..f2297e10b 100644
--- a/app/components/model-config.tsx
+++ b/app/components/model-config.tsx
@@ -6,6 +6,7 @@ import { InputRange } from "./input-range";
import { ListItem, Select } from "./ui-lib";
import { useAllModels } from "../utils/hooks";
import { groupBy } from "lodash-es";
+import styles from "./model-config.module.scss";
export function ModelConfigList(props: {
modelConfig: ModelConfig;
@@ -242,6 +243,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.CompressModel.SubTitle}
>