Merge pull request #1 from Yidadaa/main

1.5
This commit is contained in:
btxbtxbtx 2023-03-28 18:02:59 +08:00 committed by GitHub
commit 0c672ecaee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 300 additions and 62 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
public/serviceWorker.js

33
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: Publish Docker image
on:
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: yidadaa/chatgpt-next-web
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

44
Dockerfile Normal file
View File

@ -0,0 +1,44 @@
FROM node:18-alpine AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder
RUN apk update && apk add --no-cache git
ENV OPENAI_API_KEY=""
ENV CODE=""
ARG DOCKER=true
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
FROM base AS runner
WORKDIR /app
ENV OPENAI_API_KEY=""
ENV CODE=""
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/.next/server ./.next/server
EXPOSE 3000
CMD ["node","server.js"]

View File

@ -7,7 +7,7 @@
One-Click to deploy your own ChatGPT web UI. One-Click to deploy your own ChatGPT web UI.
[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [微信群](https://user-images.githubusercontent.com/16968934/227772522-b3ba3713-9206-4c8d-a81f-22300b7c313a.jpg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
@ -116,31 +116,17 @@ OPENAI_API_KEY=<your api key here>
2. 执行 `yarn install && yarn dev` 即可。 2. 执行 `yarn install && yarn dev` 即可。
### 本地部署 Local Deployment ### 本地部署 Local Deployment
```shell
请直接询问 ChatGPT使用下列 Prompt bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
如何使用 pm2 和 yarn 部署 nextjs 项目到 ubuntu 服务器上,项目编译命令为 yarn build启动命令为 yarn start启动时需要设置环境变量为 OPENAI_API_KEY端口为 3000使用 ngnix 做反向代理
``` ```
Please ask ChatGPT with prompt:
``` ### 容器部署 Docker Deployment
how to deploy nextjs project with pm2 and yarn on my ubuntu server, the build command is `yarn build`, the start command is `yarn start`, the project must start with env var named `OPENAI_API_KEY`, the port is 3000, use ngnix
```
### Docker Deployment ```shell
docker pull yidadaa/chatgpt-next-web
请直接询问 ChatGPT使用下列 Prompt docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-web
```
如何使用 docker 部署 nextjs 项目到 ubuntu 服务器上,项目编译命令为 yarn build启动命令为 yarn start启动时需要设置环境变量为 OPENAI_API_KEY端口为 3000使用 ngnix 做反向代理
```
Please ask ChatGPT with prompt:
```
how to deploy nextjs project with docker on my ubuntu server, the build command is `yarn build`, the start command is `yarn start`, the project must start with env var named `OPENAI_API_KEY`, the port is 3000, use ngnix
``` ```
## 截图 Screenshots ## 截图 Screenshots
@ -165,6 +151,14 @@ If you would like to contribute your API key, you can email it to the author and
[@mushan0x0](https://github.com/mushan0x0) [@mushan0x0](https://github.com/mushan0x0)
[@ClarenceDan](https://github.com/ClarenceDan) [@ClarenceDan](https://github.com/ClarenceDan)
[@zhangjia](https://github.com/zhangjia)
### 贡献者 Contributor
[@AprilNEA](https://github.com/AprilNEA)
[@iSource](https://github.com/iSource)
[@iFwu](https://github.com/iFwu)
[@xiaotianxt](https://github.com/xiaotianxt)
## LICENSE ## LICENSE

View File

@ -1,4 +1,3 @@
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";

View File

@ -131,10 +131,12 @@ function useSubmitHandler() {
(config.submitKey === SubmitKey.AltEnter && e.altKey) || (config.submitKey === SubmitKey.AltEnter && e.altKey) ||
(config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) || (config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
(config.submitKey === SubmitKey.ShiftEnter && e.shiftKey) || (config.submitKey === SubmitKey.ShiftEnter && e.shiftKey) ||
(config.submitKey === SubmitKey.MetaEnter && e.metaKey) ||
(config.submitKey === SubmitKey.Enter && (config.submitKey === SubmitKey.Enter &&
!e.altKey && !e.altKey &&
!e.ctrlKey && !e.ctrlKey &&
!e.shiftKey) !e.shiftKey &&
!e.metaKey)
); );
}; };
@ -163,6 +165,7 @@ export function Chat(props: { showSideBar?: () => void }) {
setIsLoading(true); setIsLoading(true);
onUserInput(userInput).then(() => setIsLoading(false)); onUserInput(userInput).then(() => setIsLoading(false));
setUserInput(""); setUserInput("");
inputRef.current?.focus();
}; };
// stop response // stop response
@ -203,6 +206,7 @@ export function Chat(props: { showSideBar?: () => void }) {
// for auto-scroll // for auto-scroll
const latestMessageRef = useRef<HTMLDivElement>(null); const latestMessageRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
// wont scroll while hovering messages // wont scroll while hovering messages
const [autoScroll, setAutoScroll] = useState(false); const [autoScroll, setAutoScroll] = useState(false);
@ -371,6 +375,7 @@ export function Chat(props: { showSideBar?: () => void }) {
<div className={styles["chat-input-panel"]}> <div className={styles["chat-input-panel"]}>
<div className={styles["chat-input-panel-inner"]}> <div className={styles["chat-input-panel-inner"]}>
<textarea <textarea
ref={inputRef}
className={styles["chat-input"]} className={styles["chat-input"]}
placeholder={Locale.Chat.Input(submitKey)} placeholder={Locale.Chat.Input(submitKey)}
rows={3} rows={3}
@ -399,11 +404,16 @@ function useSwitchTheme() {
useEffect(() => { useEffect(() => {
document.body.classList.remove("light"); document.body.classList.remove("light");
document.body.classList.remove("dark"); document.body.classList.remove("dark");
if (config.theme === "dark") { if (config.theme === "dark") {
document.body.classList.add("dark"); document.body.classList.add("dark");
} else if (config.theme === "light") { } else if (config.theme === "light") {
document.body.classList.add("light"); document.body.classList.add("light");
} }
const themeColor = getComputedStyle(document.body).getPropertyValue("--theme-color").trim();
const metaDescription = document.querySelector('meta[name="theme-color"]');
metaDescription?.setAttribute('content', themeColor);
}, [config.theme]); }, [config.theme]);
} }
@ -465,6 +475,16 @@ function showMemoryPrompt(session: ChatSession) {
}); });
} }
const useHasHydrated = () => {
const [hasHydrated, setHasHydrated] = useState<boolean>(false);
useEffect(() => {
setHasHydrated(true);
}, []);
return hasHydrated;
};
export function Home() { export function Home() {
const [createNewSession, currentIndex, removeSession] = useChatStore( const [createNewSession, currentIndex, removeSession] = useChatStore(
(state) => [ (state) => [
@ -473,7 +493,7 @@ export function Home() {
state.removeSession, state.removeSession,
] ]
); );
const loading = !useChatStore?.persist?.hasHydrated(); const loading = !useHasHydrated();
const [showSideBar, setShowSideBar] = useState(true); const [showSideBar, setShowSideBar] = useState(true);
// setting // setting
@ -546,7 +566,10 @@ export function Home() {
<IconButton <IconButton
icon={<AddIcon />} icon={<AddIcon />}
text={Locale.Home.NewChat} text={Locale.Home.NewChat}
onClick={createNewSession} onClick={()=>{
createNewSession();
setShowSideBar(false);
}}
/> />
</div> </div>
</div> </div>

View File

@ -354,7 +354,7 @@ export function Settings(props: { closeSettings: () => void }) {
<input <input
type="number" type="number"
min={100} min={100}
max={4000} max={4096}
value={config.modelConfig.max_tokens} value={config.modelConfig.max_tokens}
onChange={(e) => onChange={(e) =>
updateConfig( updateConfig(

View File

@ -5,19 +5,29 @@ import "./styles/prism.scss";
import process from "child_process"; import process from "child_process";
import { ACCESS_CODES } from "./api/access"; import { ACCESS_CODES } from "./api/access";
const COMMIT_ID = process let COMMIT_ID: string | undefined;
try {
COMMIT_ID = process
.execSync("git rev-parse --short HEAD") .execSync("git rev-parse --short HEAD")
.toString() .toString()
.trim(); .trim();
} catch (e) {
console.error("No git or not from git repo.")
}
export const metadata = { export const metadata = {
title: "ChatGPT Next Web", title: "ChatGPT Next Web",
description: "Your personal ChatGPT Chat Bot.", description: "Your personal ChatGPT Chat Bot.",
appleWebApp: {
title: "ChatGPT Next Web",
statusBarStyle: "black-translucent",
},
themeColor: "#fafafa"
}; };
function Meta() { function Meta() {
const metas = { const metas = {
version: COMMIT_ID, version: COMMIT_ID ?? "unknown",
access: ACCESS_CODES.size > 0 ? "enabled" : "disabled", access: ACCESS_CODES.size > 0 ? "enabled" : "disabled",
}; };
@ -50,6 +60,7 @@ export default function RootLayout({
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap"
rel="stylesheet" rel="stylesheet"
></link> ></link>
<script src="/serviceWorkerRegister.js" defer></script>
</head> </head>
<body>{children}</body> <body>{children}</body>
</html> </html>

View File

@ -21,6 +21,7 @@ export enum SubmitKey {
CtrlEnter = "Ctrl + Enter", CtrlEnter = "Ctrl + Enter",
ShiftEnter = "Shift + Enter", ShiftEnter = "Shift + Enter",
AltEnter = "Alt + Enter", AltEnter = "Alt + Enter",
MetaEnter = "Meta + Enter",
} }
export enum Theme { export enum Theme {

View File

@ -7,6 +7,7 @@
--second: rgb(231, 248, 255); --second: rgb(231, 248, 255);
--hover-color: #f3f3f3; --hover-color: #f3f3f3;
--bar-color: rgba(0, 0, 0, 0.1); --bar-color: rgba(0, 0, 0, 0.1);
--theme-color: var(--gray);
/* shadow */ /* shadow */
--shadow: 50px 50px 100px 10px rgb(0, 0, 0, 0.1); --shadow: 50px 50px 100px 10px rgb(0, 0, 0, 0.1);
@ -28,6 +29,8 @@
--bar-color: rgba(255, 255, 255, 0.1); --bar-color: rgba(255, 255, 255, 0.1);
--border-in-light: 1px solid rgba(255, 255, 255, 0.192); --border-in-light: 1px solid rgba(255, 255, 255, 0.192);
--theme-color: var(--gray);
} }
.light { .light {
@ -85,6 +88,10 @@ body {
user-select: none; user-select: none;
font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons", font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons",
"PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif; "PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
@media only screen and (max-width: 600px) {
background-color: var(--second);
}
} }
::-webkit-scrollbar { ::-webkit-scrollbar {

View File

@ -839,21 +839,20 @@
.markdown-body .highlight pre, .markdown-body .highlight pre,
.markdown-body pre { .markdown-body pre {
padding: 16px; padding: 16px 16px 8px 16px;
overflow: auto; overflow: auto;
font-size: 85%; font-size: 85%;
line-height: 1.45; line-height: 1.45;
background-color: var(--color-canvas-subtle);
border-radius: 6px; border-radius: 6px;
} }
.markdown-body pre code, .markdown-body pre code,
.markdown-body pre tt { .markdown-body pre tt {
display: inline; display: inline-block;
max-width: auto; max-width: 100%;
padding: 0; padding: 0;
margin: 0; margin: 0;
overflow: visible; overflow-x: scroll;
line-height: inherit; line-height: inherit;
word-wrap: normal; word-wrap: normal;
background-color: transparent; background-color: transparent;

View File

@ -121,32 +121,32 @@
} }
} }
// @mixin light { @mixin light {
// .markdown-body pre[class*="language-"] { .markdown-body pre {
// filter: invert(1) hue-rotate(50deg) brightness(1.3); filter: invert(1) hue-rotate(90deg) brightness(1.3);
// } }
// } }
// @mixin dark { @mixin dark {
// .markdown-body pre[class*="language-"] { .markdown-body pre {
// filter: none; filter: none;
// } }
// } }
// :root { :root {
// @include light(); @include light();
// } }
// .light { .light {
// @include light(); @include light();
// } }
// .dark { .dark {
// @include dark(); @include dark();
// } }
// @media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
// :root { :root {
// @include dark(); @include dark();
// } }
// } }

View File

@ -14,4 +14,8 @@ const nextConfig = {
} }
}; };
if (process.env.DOCKER) {
nextConfig.output = 'standalone'
}
module.exports = nextConfig; module.exports = nextConfig;

View File

@ -2,6 +2,7 @@
"name": "chatgpt-next-web", "name": "chatgpt-next-web",
"version": "1.1", "version": "1.1",
"private": false, "private": false,
"license": "Anti 996",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",

4
public/robots.txt Normal file
View File

@ -0,0 +1,4 @@
User-agent: *
Disallow: /
User-agent: vitals.vercel-insights.com
Allow: /

24
public/serviceWorker.js Normal file
View File

@ -0,0 +1,24 @@
const CHATGPT_NEXT_WEB_CACHE = "chatgpt-next-web-cache";
self.addEventListener('activate', function (event) {
console.log('ServiceWorker activated.');
});
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(CHATGPT_NEXT_WEB_CACHE)
.then(function (cache) {
return cache.addAll([
]);
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function (response) {
return response || fetch(event.request);
})
);
});

View File

@ -0,0 +1,9 @@
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/serviceWorker.js').then(function (registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
console.error('ServiceWorker registration failed: ', err);
});
});
}

View File

@ -1 +1,21 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} {
"name": "ChatGPT Next Web",
"short_name": "ChatGPT",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

64
scripts/setup.sh Normal file
View File

@ -0,0 +1,64 @@
#!/bin/bash
# Check if running on a supported system
case "$(uname -s)" in
Linux)
if [[ -f "/etc/lsb-release" ]]; then
. /etc/lsb-release
if [[ "$DISTRIB_ID" != "Ubuntu" ]]; then
echo "This script only works on Ubuntu, not $DISTRIB_ID."
exit 1
fi
else
if [[ ! "$(cat /etc/*-release | grep '^ID=')" =~ ^(ID=\"ubuntu\")|(ID=\"centos\")|(ID=\"arch\")$ ]]; then
echo "Unsupported Linux distribution."
exit 1
fi
fi
;;
Darwin)
echo "Running on MacOS."
;;
*)
echo "Unsupported operating system."
exit 1
;;
esac
# Check if needed dependencies are installed and install if necessary
if ! command -v node >/dev/null || ! command -v git >/dev/null || ! command -v yarn >/dev/null; then
case "$(uname -s)" in
Linux)
if [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=\"ubuntu\"" ]]; then
sudo apt-get update
sudo apt-get -y install nodejs git yarn
elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=\"centos\"" ]]; then
sudo yum -y install epel-release
sudo yum -y install nodejs git yarn
elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=\"arch\"" ]]; then
sudo pacman -Syu -y
sudo pacman -S -y nodejs git yarn
else
echo "Unsupported Linux distribution"
exit 1
fi
;;
Darwin)
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install node git yarn
;;
esac
fi
# Clone the repository and install dependencies
git clone https://github.com/Yidadaa/ChatGPT-Next-Web
cd ChatGPT-Next-Web
yarn install
# Prompt user for environment variables
read -p "Enter OPENAI_API_KEY: " OPENAI_API_KEY
read -p "Enter CODE: " CODE
read -p "Enter PORT: " PORT
# Build and run the project using the environment variables
OPENAI_API_KEY=$OPENAI_API_KEY CODE=$CODE PORT=$PORT yarn build && OPENAI_API_KEY=$OPENAI_API_KEY CODE=$CODE PORT=$PORT yarn start