Skip to content

Commit

Permalink
Merge pull request ChatGPTNextWeb#1927 from Yidadaa/bugfix-0613
Browse files Browse the repository at this point in the history
feat: ChatGPTNextWeb#1000 ready to support client-side only
  • Loading branch information
Yidadaa authored Jun 13, 2023
2 parents e6b49a6 + 2a191aa commit 426ce7f
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 36 deletions.
23 changes: 22 additions & 1 deletion app/client/api.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -42,6 +42,27 @@ export abstract class LLMApi {
abstract usage(): Promise<LLMUsage>;
}

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;

Expand Down
5 changes: 5 additions & 0 deletions app/components/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { SideBar } from "./sidebar";
import { useAppConfig } from "../store/config";
import { AuthPage } from "./auth";
import { getClientConfig } from "../config/client";

export function Loading(props: { noLogo?: boolean }) {
return (
Expand Down Expand Up @@ -147,6 +148,10 @@ function Screen() {
export function Home() {
useSwitchTheme();

useEffect(() => {
console.log("[Config] got config from build time", getClientConfig());
}, []);

if (!useHasHydrated()) {
return <Loading />;
}
Expand Down
18 changes: 17 additions & 1 deletion app/components/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo, HTMLProps, useRef } from "react";
import { useState, useEffect, useMemo } from "react";

import styles from "./settings.module.scss";

Expand Down Expand Up @@ -45,6 +45,7 @@ import { ErrorBoundary } from "./error";
import { InputRange } from "./input-range";
import { useNavigate } from "react-router-dom";
import { Avatar, AvatarPicker } from "./emoji";
import { getClientConfig } from "../config/client";

function EditPromptModal(props: { id: number; onClose: () => void }) {
const promptStore = usePromptStore();
Expand Down Expand Up @@ -541,6 +542,21 @@ export function Settings() {
/>
)}
</ListItem>

{!accessStore.hideUserApiKey ? (
<ListItem
title={Locale.Settings.Endpoint.Title}
subTitle={Locale.Settings.Endpoint.SubTitle}
>
<input
type="text"
value={accessStore.openaiUrl}
onChange={(e) =>
accessStore.updateOpenAiUrl(e.currentTarget.value)
}
></input>
</ListItem>
) : null}
</List>

<List>
Expand Down
29 changes: 16 additions & 13 deletions app/config/build.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
const COMMIT_ID: string = (() => {
try {
const childProcess = require("child_process");
return childProcess
.execSync('git log -1 --format="%at000" --date=unix')
.toString()
.trim();
} catch (e) {
console.error("[Build Config] No git or not from git repo.");
return "unknown";
}
})();

export const getBuildConfig = () => {
if (typeof process === "undefined") {
throw Error(
"[Server Config] you are importing a nodejs-only module outside of nodejs",
);
}

const COMMIT_ID: string = (() => {
try {
const childProcess = require("child_process");
return childProcess
.execSync('git log -1 --format="%at000" --date=unix')
.toString()
.trim();
} catch (e) {
console.error("[Build Config] No git or not from git repo.");
return "unknown";
}
})();

return {
commitId: COMMIT_ID,
buildMode: process.env.BUILD_MODE ?? "standalone",
};
};

export type BuildConfig = ReturnType<typeof getBuildConfig>;
27 changes: 27 additions & 0 deletions app/config/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BuildConfig, getBuildConfig } from "./build";

export function getClientConfig() {
if (typeof document !== "undefined") {
// client side
return JSON.parse(queryMeta("config")) as BuildConfig;
}

if (typeof process !== "undefined") {
// server side
return getBuildConfig();
}
}

function queryMeta(key: string, defaultValue?: string): string {
let ret: string;
if (document) {
const meta = document.head.querySelector(
`meta[name='${key}']`,
) as HTMLMetaElement;
ret = meta?.content ?? "";
} else {
ret = defaultValue ?? "";
}

return ret;
}
1 change: 1 addition & 0 deletions app/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare global {
VERCEL?: string;
HIDE_USER_API_KEY?: string; // disable user's api key input
DISABLE_GPT4?: string; // allow user to use gpt-4 or not
BUILD_MODE?: "standalone" | "export";
}
}
}
Expand Down
1 change: 1 addition & 0 deletions app/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const UPDATE_URL = `${REPO_URL}#keep-updated`;
export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
export const DEFAULT_API_HOST = "https://chatgpt.nextweb.fun/api/proxy";

export enum Path {
Home = "/",
Expand Down
5 changes: 2 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import "./styles/globals.scss";
import "./styles/markdown.scss";
import "./styles/highlight.scss";
import { getBuildConfig } from "./config/build";

const buildConfig = getBuildConfig();
import { getClientConfig } from "./config/client";

export const metadata = {
title: "ChatGPT Next Web",
Expand Down Expand Up @@ -32,7 +31,7 @@ export default function RootLayout({
return (
<html lang="en">
<head>
<meta name="version" content={buildConfig.commitId} />
<meta name="config" content={JSON.stringify(getClientConfig())} />
<link rel="manifest" href="/site.webmanifest"></link>
<script src="/serviceWorkerRegister.js" defer></script>
</head>
Expand Down
4 changes: 4 additions & 0 deletions app/locales/cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ const cn = {
SubTitle: "管理员已开启加密访问",
Placeholder: "请输入访问密码",
},
Endpoint: {
Title: "接口地址",
SubTitle: "除默认地址外,必须包含 http(s)://",
},
Model: "模型 (model)",
Temperature: {
Title: "随机性 (temperature)",
Expand Down
4 changes: 4 additions & 0 deletions app/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ const en: RequiredLocaleType = {
SubTitle: "Access control enabled",
Placeholder: "Need Access Code",
},
Endpoint: {
Title: "Endpoint",
SubTitle: "Custom endpoint must start with http(s)://",
},
Model: "Model",
Temperature: {
Title: "Temperature",
Expand Down
13 changes: 11 additions & 2 deletions app/store/access.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { StoreKey } from "../constant";
import { DEFAULT_API_HOST, StoreKey } from "../constant";
import { getHeaders } from "../client/api";
import { BOT_HELLO } from "./chat";
import { ALL_MODELS } from "./config";
import { getClientConfig } from "../config/client";

export interface AccessControlStore {
accessCode: string;
Expand All @@ -15,21 +16,26 @@ export interface AccessControlStore {

updateToken: (_: string) => void;
updateCode: (_: string) => void;
updateOpenAiUrl: (_: string) => void;
enabledAccessControl: () => boolean;
isAuthorized: () => boolean;
fetch: () => void;
}

let fetchState = 0; // 0 not fetch, 1 fetching, 2 done

const DEFAULT_OPENAI_URL =
getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : "/api/openai/";
console.log("[API] default openai url", DEFAULT_OPENAI_URL);

export const useAccessStore = create<AccessControlStore>()(
persist(
(set, get) => ({
token: "",
accessCode: "",
needCode: true,
hideUserApiKey: false,
openaiUrl: "/api/openai/",
openaiUrl: DEFAULT_OPENAI_URL,

enabledAccessControl() {
get().fetch();
Expand All @@ -42,6 +48,9 @@ export const useAccessStore = create<AccessControlStore>()(
updateToken(token: string) {
set(() => ({ token }));
},
updateOpenAiUrl(url: string) {
set(() => ({ openaiUrl: url }));
},
isAuthorized() {
get().fetch();

Expand Down
18 changes: 2 additions & 16 deletions app/store/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
import { FETCH_COMMIT_URL, StoreKey } from "../constant";
import { api } from "../client/api";
import { showToast } from "../components/ui-lib";
import { getClientConfig } from "../config/client";

export interface UpdateStore {
lastUpdate: number;
Expand All @@ -17,20 +17,6 @@ export interface UpdateStore {
updateUsage: (force?: boolean) => Promise<void>;
}

function queryMeta(key: string, defaultValue?: string): string {
let ret: string;
if (document) {
const meta = document.head.querySelector(
`meta[name='${key}']`,
) as HTMLMetaElement;
ret = meta?.content ?? "";
} else {
ret = defaultValue ?? "";
}

return ret;
}

const ONE_MINUTE = 60 * 1000;

export const useUpdateStore = create<UpdateStore>()(
Expand All @@ -44,7 +30,7 @@ export const useUpdateStore = create<UpdateStore>()(
version: "unknown",

async getLatestVersion(force = false) {
set(() => ({ version: queryMeta("version") ?? "unknown" }));
set(() => ({ version: getClientConfig()?.commitId ?? "unknown" }));

const overTenMins = Date.now() - get().lastUpdate > 10 * ONE_MINUTE;
if (!force && !overTenMins) return;
Expand Down
21 changes: 21 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@ const nextConfig = {
};

if (mode !== "export") {
nextConfig.headers = async () => {
return [
{
source: "/:path*",
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
{
key: "Access-Control-Allow-Methods",
value: "GET,OPTIONS,PATCH,DELETE,POST,PUT",
},
{
key: "Access-Control-Allow-Headers",
value:
"X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version",
},
],
},
];
};

nextConfig.rewrites = async () => {
const ret = [
{
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"export": "BUILD_MODE=export yarn build",
"prompts": "node ./scripts/fetch-prompts.mjs",
"prepare": "husky install",
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
Expand Down

0 comments on commit 426ce7f

Please sign in to comment.