diff --git a/README_ES.md b/README_ES.md
new file mode 100644
index 000000000..cdd835908
--- /dev/null
+++ b/README_ES.md
@@ -0,0 +1,171 @@
+
+
+
+
ChatGPT Next Web
+
+Implemente su aplicación web privada ChatGPT de forma gratuita con un solo clic.
+
+[Demo demo](https://chat-gpt-next-web.vercel.app/) / [Problemas de comentarios](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Únete a Discord](https://discord.gg/zrhvHCr79N) / [Grupo QQ](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [Desarrolladores de consejos](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Donar](#捐赠-donate-usdt)
+
+[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web\&env=OPENAI_API_KEY\&env=CODE\&project-name=chatgpt-next-web\&repository-name=ChatGPT-Next-Web)
+
+[](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
+
+
+
+
+
+## Comenzar
+
+1. Prepara el tuyo [Clave API OpenAI](https://platform.openai.com/account/api-keys);
+2. Haga clic en el botón de la derecha para iniciar la implementación:
+ [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web\&env=OPENAI_API_KEY\&env=CODE\&project-name=chatgpt-next-web\&repository-name=ChatGPT-Next-Web), inicie sesión directamente con su cuenta de Github y recuerde completar la clave API y la suma en la página de variables de entorno[Contraseña de acceso a la página](#配置页面访问密码) CÓDIGO;
+3. Una vez implementado, puede comenzar;
+4. (Opcional)[Enlazar un nombre de dominio personalizado](https://vercel.com/docs/concepts/projects/domains/add-a-domain): El nombre de dominio DNS asignado por Vercel está contaminado en algunas regiones y puede conectarse directamente enlazando un nombre de dominio personalizado.
+
+## Manténgase actualizado
+
+Si sigue los pasos anteriores para implementar su proyecto con un solo clic, es posible que siempre diga "La actualización existe" porque Vercel creará un nuevo proyecto para usted de forma predeterminada en lugar de bifurcar el proyecto, lo que evitará que la actualización se detecte correctamente.
+Le recomendamos que siga estos pasos para volver a implementar:
+
+* Eliminar el repositorio original;
+* Utilice el botón de bifurcación en la esquina superior derecha de la página para bifurcar este proyecto;
+* En Vercel, vuelva a seleccionar e implementar,[Echa un vistazo al tutorial detallado](./docs/vercel-cn.md#如何新建项目)。
+
+### Activar actualizaciones automáticas
+
+> Si encuentra un error de ejecución de Upstream Sync, ¡Sync Fork manualmente una vez!
+
+Cuando bifurca el proyecto, debido a las limitaciones de Github, debe ir manualmente a la página Acciones de su proyecto bifurcado para habilitar Flujos de trabajo y habilitar Upstream Sync Action, después de habilitarlo, puede activar las actualizaciones automáticas cada hora:
+
+
+
+
+
+### Actualizar el código manualmente
+
+Si desea que el manual se actualice inmediatamente, puede consultarlo [Documentación para Github](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) Aprenda a sincronizar un proyecto bifurcado con código ascendente.
+
+Puede destacar / ver este proyecto o seguir al autor para recibir notificaciones de nuevas actualizaciones de funciones.
+
+## Configurar la contraseña de acceso a la página
+
+> Después de configurar la contraseña, el usuario debe completar manualmente el código de acceso en la página de configuración para chatear normalmente, de lo contrario, se solicitará el estado no autorizado a través de un mensaje.
+
+> **advertir**: Asegúrese de establecer el número de dígitos de la contraseña lo suficientemente largo, preferiblemente más de 7 dígitos, de lo contrario[Será volado](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518)。
+
+Este proyecto proporciona control de permisos limitado, agregue el nombre al nombre en la página Variables de entorno del Panel de control del proyecto Vercel `CODE` Variables de entorno con valores para contraseñas personalizadas separadas por comas:
+
+ code1,code2,code3
+
+Después de agregar o modificar la variable de entorno, por favor**Redesplegar**proyecto para poner en vigor los cambios.
+
+## Variable de entorno
+
+> La mayoría de los elementos de configuración de este proyecto se establecen a través de variables de entorno, tutorial:[Cómo modificar las variables de entorno de Vercel](./docs/vercel-cn.md)。
+
+### `OPENAI_API_KEY` (Requerido)
+
+OpanAI key, la clave API que solicita en la página de su cuenta openai.
+
+### `CODE` (Opcional)
+
+Las contraseñas de acceso, opcionalmente, se pueden separar por comas.
+
+**advertir**: Si no completa este campo, cualquiera puede usar directamente su sitio web implementado, lo que puede hacer que su token se consuma rápidamente, se recomienda completar esta opción.
+
+### `BASE_URL` (Opcional)
+
+> Predeterminado: `https://api.openai.com`
+
+> Ejemplos: `http://your-openai-proxy.com`
+
+URL del proxy de interfaz OpenAI, complete esta opción si configuró manualmente el proxy de interfaz openAI.
+
+> Si encuentra problemas con el certificado SSL, establezca el `BASE_URL` El protocolo se establece en http.
+
+### `OPENAI_ORG_ID` (Opcional)
+
+Especifica el identificador de la organización en OpenAI.
+
+### `HIDE_USER_API_KEY` (Opcional)
+
+Si no desea que los usuarios rellenen la clave de API ellos mismos, establezca esta variable de entorno en 1.
+
+### `DISABLE_GPT4` (Opcional)
+
+Si no desea que los usuarios utilicen GPT-4, establezca esta variable de entorno en 1.
+
+## explotación
+
+> No se recomienda encarecidamente desarrollar o implementar localmente, debido a algunas razones técnicas, es difícil configurar el agente API de OpenAI localmente, a menos que pueda asegurarse de que puede conectarse directamente al servidor OpenAI.
+
+Haga clic en el botón de abajo para iniciar el desarrollo secundario:
+
+[](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
+
+Antes de empezar a escribir código, debe crear uno nuevo en la raíz del proyecto `.env.local` archivo, lleno de variables de entorno:
+
+ OPENAI_API_KEY=
+
+### Desarrollo local
+
+1. Instale nodejs 18 e hilo, pregunte a ChatGPT para obtener más detalles;
+2. ejecutar `yarn install && yarn dev` Enlatar. ⚠️ Nota: Este comando es solo para desarrollo local, no para implementación.
+3. Úselo si desea implementar localmente `yarn install && yarn start` comando, puede cooperar con pm2 a daemon para evitar ser asesinado, pregunte a ChatGPT para obtener más detalles.
+
+## desplegar
+
+### Implementación de contenedores (recomendado)
+
+> La versión de Docker debe ser 20 o posterior, de lo contrario se indicará que no se puede encontrar la imagen.
+
+> ⚠️ Nota: Las versiones de Docker están de 1 a 2 días por detrás de la última versión la mayor parte del tiempo, por lo que es normal que sigas diciendo "La actualización existe" después de la implementación.
+
+```shell
+docker pull yidadaa/chatgpt-next-web
+
+docker run -d -p 3000:3000 \
+ -e OPENAI_API_KEY="sk-xxxx" \
+ -e CODE="页面访问密码" \
+ yidadaa/chatgpt-next-web
+```
+
+También puede especificar proxy:
+
+```shell
+docker run -d -p 3000:3000 \
+ -e OPENAI_API_KEY="sk-xxxx" \
+ -e CODE="页面访问密码" \
+ --net=host \
+ -e PROXY_URL="http://127.0.0.1:7890" \
+ yidadaa/chatgpt-next-web
+```
+
+Si necesita especificar otras variables de entorno, agréguelas usted mismo en el comando anterior `-e 环境变量=环境变量值` para especificar.
+
+### Implementación local
+
+Ejecute el siguiente comando en la consola:
+
+```shell
+bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
+```
+
+⚠️ Nota: Si tiene problemas durante la instalación, utilice la implementación de Docker.
+
+## Reconocimiento
+
+### donante
+
+> Ver versión en inglés.
+
+### Colaboradores
+
+[Ver la lista de colaboradores del proyecto](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
+
+## Licencia de código abierto
+
+> Contra 996, empezando por mí.
+
+[Licencia Anti 996](https://github.com/kattgu7/Anti-996-License/blob/master/LICENSE_CN_EN)
diff --git a/app/api/common.ts b/app/api/common.ts
index 03247539a..ccfb99e26 100644
--- a/app/api/common.ts
+++ b/app/api/common.ts
@@ -1,9 +1,10 @@
-import { NextRequest } from "next/server";
+import { NextRequest, NextResponse } from "next/server";
export const OPENAI_URL = "api.openai.com";
const DEFAULT_PROTOCOL = "https";
const PROTOCOL = process.env.PROTOCOL ?? DEFAULT_PROTOCOL;
const BASE_URL = process.env.BASE_URL ?? OPENAI_URL;
+const DISABLE_GPT4 = !!process.env.DISABLE_GPT4;
export async function requestOpenai(req: NextRequest) {
const controller = new AbortController();
@@ -45,21 +46,45 @@ export async function requestOpenai(req: NextRequest) {
signal: controller.signal,
};
+ // #1815 try to refuse gpt4 request
+ if (DISABLE_GPT4 && req.body) {
+ try {
+ const clonedBody = await req.text();
+ fetchOptions.body = clonedBody;
+
+ const jsonBody = JSON.parse(clonedBody);
+
+ if ((jsonBody?.model ?? "").includes("gpt-4")) {
+ return NextResponse.json(
+ {
+ error: true,
+ message: "you are not allowed to use gpt-4 model",
+ },
+ {
+ status: 403,
+ },
+ );
+ }
+ } catch (e) {
+ console.error("[OpenAI] gpt4 filter", e);
+ }
+ }
+
try {
const res = await fetch(fetchUrl, fetchOptions);
- if (res.status === 401) {
- // to prevent browser prompt for credentials
- const newHeaders = new Headers(res.headers);
- newHeaders.delete("www-authenticate");
- return new Response(res.body, {
- status: res.status,
- statusText: res.statusText,
- headers: newHeaders,
- });
- }
+ // to prevent browser prompt for credentials
+ const newHeaders = new Headers(res.headers);
+ newHeaders.delete("www-authenticate");
- return res;
+ // to disbale ngnix buffering
+ newHeaders.set("X-Accel-Buffering", "no");
+
+ return new Response(res.body, {
+ status: res.status,
+ statusText: res.statusText,
+ headers: newHeaders,
+ });
} finally {
clearTimeout(timeoutId);
}
diff --git a/app/api/openai/[...path]/route.ts b/app/api/openai/[...path]/route.ts
index 981749e7e..04f3b6da8 100644
--- a/app/api/openai/[...path]/route.ts
+++ b/app/api/openai/[...path]/route.ts
@@ -1,14 +1,32 @@
+import { OpenaiPath } from "@/app/constant";
import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth";
import { requestOpenai } from "../../common";
+const ALLOWD_PATH = new Set(Object.values(OpenaiPath));
+
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[OpenAI Route] params ", params);
+ const subpath = params.path.join("/");
+
+ if (!ALLOWD_PATH.has(subpath)) {
+ console.log("[OpenAI Route] forbidden path ", subpath);
+ return NextResponse.json(
+ {
+ error: true,
+ msg: "you are not allowed to request " + subpath,
+ },
+ {
+ status: 403,
+ },
+ );
+ }
+
const authResult = auth(req);
if (authResult.error) {
return NextResponse.json(authResult, {
diff --git a/app/client/api.ts b/app/client/api.ts
index fb829f97a..8897d7a66 100644
--- a/app/client/api.ts
+++ b/app/client/api.ts
@@ -1,5 +1,5 @@
import { ACCESS_CODE_PREFIX } from "../constant";
-import { ChatMessage, ModelConfig, ModelType, useAccessStore } from "../store";
+import { ChatMessage, ModelType, useAccessStore } from "../store";
import { ChatGPTApi } from "./platforms/openai";
export const ROLES = ["system", "user", "assistant"] as const;
@@ -42,6 +42,27 @@ export abstract class LLMApi {
abstract usage(): Promise;
}
+type ProviderName = "openai" | "azure" | "claude" | "palm";
+
+interface Model {
+ name: string;
+ provider: ProviderName;
+ ctxlen: number;
+}
+
+interface ChatProvider {
+ name: ProviderName;
+ apiConfig: {
+ baseUrl: string;
+ apiKey: string;
+ summaryModel: Model;
+ };
+ models: Model[];
+
+ chat: () => void;
+ usage: () => void;
+}
+
export class ClientApi {
public llm: LLMApi;
diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts
index 84c4a2df0..fd4c33655 100644
--- a/app/client/platforms/openai.ts
+++ b/app/client/platforms/openai.ts
@@ -1,4 +1,4 @@
-import { REQUEST_TIMEOUT_MS } from "@/app/constant";
+import { OpenaiPath, REQUEST_TIMEOUT_MS } from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { ChatOptions, getHeaders, LLMApi, LLMUsage } from "../api";
@@ -10,10 +10,6 @@ import {
import { prettyObject } from "@/app/utils/format";
export class ChatGPTApi implements LLMApi {
- public ChatPath = "v1/chat/completions";
- public UsagePath = "dashboard/billing/usage";
- public SubsPath = "dashboard/billing/subscription";
-
path(path: string): string {
let openaiUrl = useAccessStore.getState().openaiUrl;
if (openaiUrl.endsWith("/")) {
@@ -55,7 +51,7 @@ export class ChatGPTApi implements LLMApi {
options.onController?.(controller);
try {
- const chatPath = this.path(this.ChatPath);
+ const chatPath = this.path(OpenaiPath.ChatPath);
const chatPayload = {
method: "POST",
body: JSON.stringify(requestPayload),
@@ -177,14 +173,14 @@ export class ChatGPTApi implements LLMApi {
const [used, subs] = await Promise.all([
fetch(
this.path(
- `${this.UsagePath}?start_date=${startDate}&end_date=${endDate}`,
+ `${OpenaiPath.UsagePath}?start_date=${startDate}&end_date=${endDate}`,
),
{
method: "GET",
headers: getHeaders(),
},
),
- fetch(this.path(this.SubsPath), {
+ fetch(this.path(OpenaiPath.SubsPath), {
method: "GET",
headers: getHeaders(),
}),
@@ -228,3 +224,4 @@ export class ChatGPTApi implements LLMApi {
} as LLMUsage;
}
}
+export { OpenaiPath };
diff --git a/app/components/auth.module.scss b/app/components/auth.module.scss
new file mode 100644
index 000000000..6630c0613
--- /dev/null
+++ b/app/components/auth.module.scss
@@ -0,0 +1,36 @@
+.auth-page {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ width: 100%;
+ flex-direction: column;
+
+ .auth-logo {
+ transform: scale(1.4);
+ }
+
+ .auth-title {
+ font-size: 24px;
+ font-weight: bold;
+ line-height: 2;
+ }
+
+ .auth-tips {
+ font-size: 14px;
+ }
+
+ .auth-input {
+ margin: 3vh 0;
+ }
+
+ .auth-actions {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+
+ button:not(:last-child) {
+ margin-bottom: 10px;
+ }
+ }
+}
diff --git a/app/components/auth.tsx b/app/components/auth.tsx
new file mode 100644
index 000000000..de0df4542
--- /dev/null
+++ b/app/components/auth.tsx
@@ -0,0 +1,46 @@
+import styles from "./auth.module.scss";
+import { IconButton } from "./button";
+
+import { useNavigate } from "react-router-dom";
+import { Path } from "../constant";
+import { useAccessStore } from "../store";
+import Locale from "../locales";
+
+import BotIcon from "../icons/bot.svg";
+
+export function AuthPage() {
+ const navigate = useNavigate();
+ const access = useAccessStore();
+
+ const goHome = () => navigate(Path.Home);
+
+ return (
+