This commit is contained in:
GH Action - Upstream Sync 2023-04-17 00:59:02 +00:00
commit b3a0b252ad
21 changed files with 410 additions and 131 deletions

View File

@ -19,6 +19,7 @@ import LightIcon from "../icons/light.svg";
import DarkIcon from "../icons/dark.svg"; import DarkIcon from "../icons/dark.svg";
import AutoIcon from "../icons/auto.svg"; import AutoIcon from "../icons/auto.svg";
import BottomIcon from "../icons/bottom.svg"; import BottomIcon from "../icons/bottom.svg";
import StopIcon from "../icons/pause.svg";
import { import {
Message, Message,
@ -38,7 +39,6 @@ import {
isMobileScreen, isMobileScreen,
selectOrCopy, selectOrCopy,
autoGrowTextArea, autoGrowTextArea,
getCSSVar,
} from "../utils"; } from "../utils";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
@ -355,8 +355,8 @@ export function ChatActions(props: {
}) { }) {
const chatStore = useChatStore(); const chatStore = useChatStore();
// switch themes
const theme = chatStore.config.theme; const theme = chatStore.config.theme;
function nextTheme() { function nextTheme() {
const themes = [Theme.Auto, Theme.Light, Theme.Dark]; const themes = [Theme.Auto, Theme.Light, Theme.Dark];
const themeIndex = themes.indexOf(theme); const themeIndex = themes.indexOf(theme);
@ -365,8 +365,20 @@ export function ChatActions(props: {
chatStore.updateConfig((config) => (config.theme = nextTheme)); chatStore.updateConfig((config) => (config.theme = nextTheme));
} }
// stop all responses
const couldStop = ControllerPool.hasPending();
const stopAll = () => ControllerPool.stopAll();
return ( return (
<div className={chatStyle["chat-input-actions"]}> <div className={chatStyle["chat-input-actions"]}>
{couldStop && (
<div
className={`${chatStyle["chat-input-action"]} clickable`}
onClick={stopAll}
>
<StopIcon />
</div>
)}
{!props.hitBottom && ( {!props.hitBottom && (
<div <div
className={`${chatStyle["chat-input-action"]} clickable`} className={`${chatStyle["chat-input-action"]} clickable`}
@ -524,21 +536,45 @@ export function Chat(props: {
} }
}; };
const onResend = (botIndex: number) => { const findLastUesrIndex = (messageId: number) => {
// find last user input message and resend // find last user input message and resend
for (let i = botIndex; i >= 0; i -= 1) { let lastUserMessageIndex: number | null = null;
if (messages[i].role === "user") { for (let i = 0; i < session.messages.length; i += 1) {
setIsLoading(true); const message = session.messages[i];
chatStore if (message.id === messageId) {
.onUserInput(messages[i].content) break;
.then(() => setIsLoading(false)); }
chatStore.updateCurrentSession((session) => if (message.role === "user") {
session.messages.splice(i, 2), lastUserMessageIndex = i;
);
inputRef.current?.focus();
return;
} }
} }
return lastUserMessageIndex;
};
const deleteMessage = (userIndex: number) => {
chatStore.updateCurrentSession((session) =>
session.messages.splice(userIndex, 2),
);
};
const onDelete = (botMessageId: number) => {
const userIndex = findLastUesrIndex(botMessageId);
if (userIndex === null) return;
deleteMessage(userIndex);
};
const onResend = (botMessageId: number) => {
// find last user input message and resend
const userIndex = findLastUesrIndex(botMessageId);
if (userIndex === null) return;
setIsLoading(true);
chatStore
.onUserInput(session.messages[userIndex].content)
.then(() => setIsLoading(false));
deleteMessage(userIndex);
inputRef.current?.focus();
}; };
const config = useChatStore((state) => state.config); const config = useChatStore((state) => state.config);
@ -710,12 +746,20 @@ export function Chat(props: {
{Locale.Chat.Actions.Stop} {Locale.Chat.Actions.Stop}
</div> </div>
) : ( ) : (
<div <>
className={styles["chat-message-top-action"]} <div
onClick={() => onResend(i)} className={styles["chat-message-top-action"]}
> onClick={() => onDelete(message.id ?? i)}
{Locale.Chat.Actions.Retry} >
</div> {Locale.Chat.Actions.Delete}
</div>
<div
className={styles["chat-message-top-action"]}
onClick={() => onResend(message.id ?? i)}
>
{Locale.Chat.Actions.Retry}
</div>
</>
)} )}
<div <div

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(8.002766666666666 2) rotate(0 0 4.649916666666667)" d="M0,9.3L0,0 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 7.333333333333333) rotate(0 4 2)" d="M8,0L4,4L0,0 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 14) rotate(0 4 0)" d="M8,0L0,0 " /></g></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 4) rotate(0 4 2)" d="M8,0L4,4L0,0 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 8) rotate(0 4 2)" d="M8,0L4,4L0,0 " /></g></g></svg>

Before

Width:  |  Height:  |  Size: 958 B

After

Width:  |  Height:  |  Size: 736 B

1
app/icons/pause.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.666666666666666 6.666666666666666)" d="M13.33,6.67C13.33,2.98 10.35,0 6.67,0C2.98,0 0,2.98 0,6.67C0,10.35 2.98,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67Z " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(6.333333333333333 6) rotate(0 0 2)" d="M0,0L0,4 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.666666666666666 6) rotate(0 0 2)" d="M0,0L0,4 " /></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -17,6 +17,7 @@ const cn = {
Copy: "复制", Copy: "复制",
Stop: "停止", Stop: "停止",
Retry: "重试", Retry: "重试",
Delete: "删除",
}, },
Rename: "重命名对话", Rename: "重命名对话",
Typing: "正在输入…", Typing: "正在输入…",
@ -74,6 +75,7 @@ const cn = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "头像", Avatar: "头像",

184
app/locales/de.ts Normal file
View File

@ -0,0 +1,184 @@
import { SubmitKey } from "../store/app";
import type { LocaleType } from "./index";
const de: LocaleType = {
WIP: "In Bearbeitung...",
Error: {
Unauthorized:
"Unbefugter Zugriff, bitte geben Sie den Zugangscode auf der Einstellungsseite ein.",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} Nachrichten`,
},
Chat: {
SubTitle: (count: number) => `${count} Nachrichten mit ChatGPT`,
Actions: {
ChatList: "Zur Chat-Liste gehen",
CompressedHistory: "Komprimierter Gedächtnis-Prompt",
Export: "Alle Nachrichten als Markdown exportieren",
Copy: "Kopieren",
Stop: "Stop",
Retry: "Wiederholen",
Delete: "Delete",
},
Rename: "Chat umbenennen",
Typing: "Tippen...",
Input: (submitKey: string) => {
var inputHints = `${submitKey} um zu Senden`;
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Umschalt + Eingabe für Zeilenumbruch";
}
return inputHints + ", / zum Durchsuchen von Prompts";
},
Send: "Senden",
},
Export: {
Title: "Alle Nachrichten",
Copy: "Alles kopieren",
Download: "Herunterladen",
MessageFromYou: "Deine Nachricht",
MessageFromChatGPT: "Nachricht von ChatGPT",
},
Memory: {
Title: "Gedächtnis-Prompt",
EmptyContent: "Noch nichts.",
Send: "Gedächtnis senden",
Copy: "Gedächtnis kopieren",
Reset: "Sitzung zurücksetzen",
ResetConfirm:
"Das Zurücksetzen löscht den aktuellen Gesprächsverlauf und das Langzeit-Gedächtnis. Möchten Sie wirklich zurücksetzen?",
},
Home: {
NewChat: "Neuer Chat",
DeleteChat: "Bestätigen Sie, um das ausgewählte Gespräch zu löschen?",
DeleteToast: "Chat gelöscht",
Revert: "Zurücksetzen",
},
Settings: {
Title: "Einstellungen",
SubTitle: "Alle Einstellungen",
Actions: {
ClearAll: "Alle Daten löschen",
ResetAll: "Alle Einstellungen zurücksetzen",
Close: "Schließen",
ConfirmResetAll: {
Confirm: "Möchten Sie wirklich alle Konfigurationen zurücksetzen?",
},
ConfirmClearAll: {
Confirm: "Möchten Sie wirklich alle Chats zurücksetzen?",
},
},
Lang: {
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
Options: {
cn: "简体中文",
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
tr: "Türkçe",
jp: "日本語",
de: "Deutsch",
},
},
Avatar: "Avatar",
FontSize: {
Title: "Schriftgröße",
SubTitle: "Schriftgröße des Chat-Inhalts anpassen",
},
Update: {
Version: (x: string) => `Version: ${x}`,
IsLatest: "Neueste Version",
CheckUpdate: "Update prüfen",
IsChecking: "Update wird geprüft...",
FoundUpdate: (x: string) => `Neue Version gefunden: ${x}`,
GoToUpdate: "Aktualisieren",
},
SendKey: "Senden-Taste",
Theme: "Erscheinungsbild",
TightBorder: "Enger Rahmen",
SendPreviewBubble: "Vorschau-Bubble senden",
Prompt: {
Disable: {
Title: "Autovervollständigung deaktivieren",
SubTitle: "Autovervollständigung mit / starten",
},
List: "Prompt-Liste",
ListCount: (builtin: number, custom: number) =>
`${builtin} integriert, ${custom} benutzerdefiniert`,
Edit: "Bearbeiten",
},
HistoryCount: {
Title: "Anzahl der angehängten Nachrichten",
SubTitle: "Anzahl der pro Anfrage angehängten gesendeten Nachrichten",
},
CompressThreshold: {
Title: "Schwellenwert für Verlaufskomprimierung",
SubTitle:
"Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet",
},
Token: {
Title: "API-Schlüssel",
SubTitle:
"Verwenden Sie Ihren Schlüssel, um das Zugangscode-Limit zu ignorieren",
Placeholder: "OpenAI API-Schlüssel",
},
Usage: {
Title: "Kontostand",
SubTitle(used: any, total: any) {
return `Diesen Monat ausgegeben $${used}, Abonnement $${total}`;
},
IsChecking: "Wird überprüft...",
Check: "Erneut prüfen",
NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen",
},
AccessCode: {
Title: "Zugangscode",
SubTitle: "Zugangskontrolle aktiviert",
Placeholder: "Zugangscode erforderlich",
},
Model: "Modell",
Temperature: {
Title: "Temperature", //Temperatur
SubTitle: "Ein größerer Wert führt zu zufälligeren Antworten",
},
MaxTokens: {
Title: "Max Tokens", //Maximale Token
SubTitle: "Maximale Anzahl der Anfrage- plus Antwort-Token",
},
PresencePenlty: {
Title: "Presence Penalty", //Anwesenheitsstrafe
SubTitle:
"Ein größerer Wert erhöht die Wahrscheinlichkeit, dass über neue Themen gesprochen wird",
},
},
Store: {
DefaultTopic: "Neues Gespräch",
BotHello: "Hallo! Wie kann ich Ihnen heute helfen?",
Error:
"Etwas ist schief gelaufen, bitte versuchen Sie es später noch einmal.",
Prompt: {
History: (content: string) =>
"Dies ist eine Zusammenfassung des Chatverlaufs zwischen dem KI und dem Benutzer als Rückblick: " +
content,
Topic:
"Bitte erstellen Sie einen vier- bis fünfwörtigen Titel, der unser Gespräch zusammenfasst, ohne Einleitung, Zeichensetzung, Anführungszeichen, Punkte, Symbole oder zusätzlichen Text. Entfernen Sie Anführungszeichen.",
Summarize:
"Fassen Sie unsere Diskussion kurz in 200 Wörtern oder weniger zusammen, um sie als Pronpt für zukünftige Gespräche zu verwenden.",
},
ConfirmClearAll:
"Bestätigen Sie, um alle Chat- und Einstellungsdaten zu löschen?",
},
Copy: {
Success: "In die Zwischenablage kopiert",
Failed:
"Kopieren fehlgeschlagen, bitte geben Sie die Berechtigung zum Zugriff auf die Zwischenablage frei",
},
Context: {
Toast: (x: any) => `Mit ${x} Kontext-Prompts`,
Edit: "Kontext- und Gedächtnis-Prompts",
Add: "Hinzufügen",
},
};
export default de;

View File

@ -19,6 +19,7 @@ const en: LocaleType = {
Copy: "Copy", Copy: "Copy",
Stop: "Stop", Stop: "Stop",
Retry: "Retry", Retry: "Retry",
Delete: "Delete",
}, },
Rename: "Rename Chat", Rename: "Rename Chat",
Typing: "Typing…", Typing: "Typing…",
@ -77,6 +78,7 @@ const en: LocaleType = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "Avatar", Avatar: "Avatar",

View File

@ -19,6 +19,7 @@ const es: LocaleType = {
Copy: "Copiar", Copy: "Copiar",
Stop: "Detener", Stop: "Detener",
Retry: "Reintentar", Retry: "Reintentar",
Delete: "Delete",
}, },
Rename: "Renombrar chat", Rename: "Renombrar chat",
Typing: "Escribiendo...", Typing: "Escribiendo...",
@ -77,6 +78,7 @@ const es: LocaleType = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "Avatar", Avatar: "Avatar",

View File

@ -5,10 +5,20 @@ import ES from "./es";
import IT from "./it"; import IT from "./it";
import TR from "./tr"; import TR from "./tr";
import JP from "./jp"; import JP from "./jp";
import DE from "./de";
export type { LocaleType } from "./cn"; export type { LocaleType } from "./cn";
export const AllLangs = ["en", "cn", "tw", "es", "it", "tr", "jp"] as const; export const AllLangs = [
"en",
"cn",
"tw",
"es",
"it",
"tr",
"jp",
"de",
] as const;
type Lang = (typeof AllLangs)[number]; type Lang = (typeof AllLangs)[number];
const LANG_KEY = "lang"; const LANG_KEY = "lang";
@ -44,21 +54,13 @@ export function getLang(): Lang {
const lang = getLanguage(); const lang = getLanguage();
if (lang.includes("zh") || lang.includes("cn")) { for (const option of AllLangs) {
return "cn"; if (lang.includes(option)) {
} else if (lang.includes("tw")) { return option;
return "tw"; }
} else if (lang.includes("es")) {
return "es";
} else if (lang.includes("it")) {
return "it";
} else if (lang.includes("tr")) {
return "tr";
} else if (lang.includes("jp")) {
return "jp";
} else {
return "en";
} }
return "en";
} }
export function changeLang(lang: Lang) { export function changeLang(lang: Lang) {
@ -66,6 +68,13 @@ export function changeLang(lang: Lang) {
location.reload(); location.reload();
} }
export default { en: EN, cn: CN, tw: TW, es: ES, it: IT, tr: TR, jp: JP }[ export default {
getLang() en: EN,
]; cn: CN,
tw: TW,
es: ES,
it: IT,
tr: TR,
jp: JP,
de: DE,
}[getLang()] as typeof CN;

View File

@ -19,6 +19,7 @@ const it: LocaleType = {
Copy: "Copia", Copy: "Copia",
Stop: "Stop", Stop: "Stop",
Retry: "Riprova", Retry: "Riprova",
Delete: "Delete",
}, },
Rename: "Rinomina Chat", Rename: "Rinomina Chat",
Typing: "Typing…", Typing: "Typing…",
@ -77,6 +78,7 @@ const it: LocaleType = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "Avatar", Avatar: "Avatar",

View File

@ -18,6 +18,7 @@ const jp = {
Copy: "コピー", Copy: "コピー",
Stop: "停止", Stop: "停止",
Retry: "リトライ", Retry: "リトライ",
Delete: "Delete",
}, },
Rename: "チャットの名前を変更", Rename: "チャットの名前を変更",
Typing: "入力中…", Typing: "入力中…",
@ -76,6 +77,7 @@ const jp = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "アバター", Avatar: "アバター",
@ -177,6 +179,4 @@ const jp = {
}, },
}; };
export type LocaleType = typeof jp;
export default jp; export default jp;

View File

@ -19,6 +19,7 @@ const tr: LocaleType = {
Copy: "Kopyala", Copy: "Kopyala",
Stop: "Durdur", Stop: "Durdur",
Retry: "Tekrar Dene", Retry: "Tekrar Dene",
Delete: "Delete",
}, },
Rename: "Sohbeti Yeniden Adlandır", Rename: "Sohbeti Yeniden Adlandır",
Typing: "Yazıyor…", Typing: "Yazıyor…",
@ -77,6 +78,7 @@ const tr: LocaleType = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "Avatar", Avatar: "Avatar",

View File

@ -18,6 +18,7 @@ const tw: LocaleType = {
Copy: "複製", Copy: "複製",
Stop: "停止", Stop: "停止",
Retry: "重試", Retry: "重試",
Delete: "刪除",
}, },
Rename: "重命名對話", Rename: "重命名對話",
Typing: "正在輸入…", Typing: "正在輸入…",
@ -75,6 +76,7 @@ const tw: LocaleType = {
it: "Italiano", it: "Italiano",
tr: "Türkçe", tr: "Türkçe",
jp: "日本語", jp: "日本語",
de: "Deutsch",
}, },
}, },
Avatar: "大頭貼", Avatar: "大頭貼",
@ -152,7 +154,8 @@ const tw: LocaleType = {
Prompt: { Prompt: {
History: (content: string) => History: (content: string) =>
"這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content, "這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content,
Topic: "Summarise the conversation in a short and concise eye-catching title that instantly conveys the main topic. Use as few words as possible. Use the language used in the enquiry, e.g. use English for English enquiry, use zh-hant for traditional chinese enquiry. Don't use quotation marks at the beginning and the end.", Topic:
"Summarise the conversation in a short and concise eye-catching title that instantly conveys the main topic. Use as few words as possible. Use the language used in the enquiry, e.g. use English for English enquiry, use zh-hant for traditional chinese enquiry. Don't use quotation marks at the beginning and the end.",
Summarize: Summarize:
"Summarise the conversation in at most 250 tokens for continuing the conversation in future. Use the language used in the conversation, e.g. use English for English conversation, use zh-hant for traditional chinese conversation.", "Summarise the conversation in at most 250 tokens for continuing the conversation in future. Use the language used in the conversation, e.g. use English for English conversation, use zh-hant for traditional chinese conversation.",
}, },

View File

@ -2,7 +2,7 @@ import type { ChatRequest, ChatResponse } from "./api/openai/typing";
import { Message, ModelConfig, useAccessStore, useChatStore } from "./store"; import { Message, ModelConfig, useAccessStore, useChatStore } from "./store";
import { showToast } from "./components/ui-lib"; import { showToast } from "./components/ui-lib";
const TIME_OUT_MS = 30000; const TIME_OUT_MS = 60000;
const makeRequestParam = ( const makeRequestParam = (
messages: Message[], messages: Message[],
@ -167,7 +167,6 @@ export async function requestChatStream(
options?.onController?.(controller); options?.onController?.(controller);
while (true) { while (true) {
// handle time out, will stop if no response in 10 secs
const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS); const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS);
const content = await reader?.read(); const content = await reader?.read();
clearTimeout(resTimeoutId); clearTimeout(resTimeoutId);
@ -235,6 +234,14 @@ export const ControllerPool = {
controller?.abort(); controller?.abort();
}, },
stopAll() {
Object.values(this.controllers).forEach((v) => v.abort());
},
hasPending() {
return Object.values(this.controllers).length > 0;
},
remove(sessionIndex: number, messageId: number) { remove(sessionIndex: number, messageId: number) {
const key = this.key(sessionIndex, messageId); const key = this.key(sessionIndex, messageId);
delete this.controllers[key]; delete this.controllers[key];

View File

@ -386,6 +386,7 @@ export const useChatStore = create<ChatStore>()(
const botMessage: Message = createMessage({ const botMessage: Message = createMessage({
role: "assistant", role: "assistant",
streaming: true, streaming: true,
id: userMessage.id! + 1,
}); });
// get recent messages // get recent messages
@ -421,7 +422,7 @@ export const useChatStore = create<ChatStore>()(
onError(error, statusCode) { onError(error, statusCode) {
if (statusCode === 401) { if (statusCode === 401) {
botMessage.content = Locale.Error.Unauthorized; botMessage.content = Locale.Error.Unauthorized;
} else { } else if (!error.message.includes("aborted")) {
botMessage.content += "\n\n" + Locale.Store.Error; botMessage.content += "\n\n" + Locale.Store.Error;
} }
botMessage.streaming = false; botMessage.streaming = false;

View File

@ -116,10 +116,9 @@ export const usePromptStore = create<PromptStore>()(
}) })
.concat([...(state?.prompts?.values() ?? [])]); .concat([...(state?.prompts?.values() ?? [])]);
const allPromptsForSearch = builtinPrompts.reduce( const allPromptsForSearch = builtinPrompts
(pre, cur) => pre.concat(cur), .reduce((pre, cur) => pre.concat(cur), [])
[], .filter((v) => !!v.title && !!v.content);
);
SearchService.count.builtin = res.en.length + res.cn.length; SearchService.count.builtin = res.en.length + res.cn.length;
SearchService.init(allPromptsForSearch); SearchService.init(allPromptsForSearch);
}); });

View File

@ -8,6 +8,17 @@ export const config = {
const serverConfig = getServerSideConfig(); const serverConfig = getServerSideConfig();
function getIP(req: NextRequest) {
let ip = req.ip ?? req.headers.get("x-real-ip");
const forwardedFor = req.headers.get("x-forwarded-for");
if (!ip && forwardedFor) {
ip = forwardedFor.split(",").at(0) ?? "";
}
return ip;
}
export function middleware(req: NextRequest) { export function middleware(req: NextRequest) {
const accessCode = req.headers.get("access-code"); const accessCode = req.headers.get("access-code");
const token = req.headers.get("token"); const token = req.headers.get("token");
@ -16,6 +27,8 @@ export function middleware(req: NextRequest) {
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]); console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
console.log("[Auth] got access code:", accessCode); console.log("[Auth] got access code:", accessCode);
console.log("[Auth] hashed access code:", hashedCode); console.log("[Auth] hashed access code:", hashedCode);
console.log("[User IP] ", getIP(req));
console.log("[Time] ", new Date().toLocaleString());
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) { if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
return NextResponse.json( return NextResponse.json(

View File

@ -9,7 +9,8 @@
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"fetch": "node ./scripts/fetch-prompts.mjs", "fetch": "node ./scripts/fetch-prompts.mjs",
"prepare": "husky install" "prepare": "husky install",
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
}, },
"dependencies": { "dependencies": {
"@hello-pangea/dnd": "^16.2.0", "@hello-pangea/dnd": "^16.2.0",
@ -18,7 +19,7 @@
"emoji-picker-react": "^4.4.7", "emoji-picker-react": "^4.4.7",
"eventsource-parser": "^0.1.0", "eventsource-parser": "^0.1.0",
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"next": "^13.2.3", "next": "^13.3.0",
"node-fetch": "^3.3.1", "node-fetch": "^3.3.1",
"openai": "^3.2.1", "openai": "^3.2.1",
"react": "^18.2.0", "react": "^18.2.0",

1
scripts/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
proxychains.conf

5
scripts/init-proxy.sh Normal file
View File

@ -0,0 +1,5 @@
dir="$(dirname "$0")"
config=$dir/proxychains.conf
host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //')
cp $dir/proxychains.template.conf $config
sed -i "\$s/.*/http $host_ip 7890/" $config

View File

@ -0,0 +1,12 @@
strict_chain
proxy_dns
remote_dns_subnet 224
tcp_read_time_out 15000
tcp_connect_time_out 8000
localnet 127.0.0.0/255.0.0.0
[ProxyList]
socks4 127.0.0.1 9050

145
yarn.lock
View File

@ -1099,10 +1099,10 @@
"@jridgewell/resolve-uri" "3.1.0" "@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14" "@jridgewell/sourcemap-codec" "1.4.14"
"@next/env@13.2.4": "@next/env@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.4.tgz#8b763700262b2445140a44a8c8d088cef676dbae" resolved "https://registry.npmmirror.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba"
integrity sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA== integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==
"@next/eslint-plugin-next@13.2.3": "@next/eslint-plugin-next@13.2.3":
version "13.2.3" version "13.2.3"
@ -1111,70 +1111,50 @@
dependencies: dependencies:
glob "7.1.7" glob "7.1.7"
"@next/swc-android-arm-eabi@13.2.4": "@next/swc-darwin-arm64@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz#758d0403771e549f9cee71cbabc0cb16a6c947c0" resolved "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2"
integrity sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw== integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w==
"@next/swc-android-arm64@13.2.4": "@next/swc-darwin-x64@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz#834d586523045110d5602e0c8aae9028835ac427" resolved "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb"
integrity sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg== integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg==
"@next/swc-darwin-arm64@13.2.4": "@next/swc-linux-arm64-gnu@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.4.tgz#5006fca179a36ef3a24d293abadec7438dbb48c6" resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5"
integrity sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A== integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw==
"@next/swc-darwin-x64@13.2.4": "@next/swc-linux-arm64-musl@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.4.tgz#6549c7c04322766acc3264ccdb3e1b43fcaf7946" resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2"
integrity sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw== integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ==
"@next/swc-freebsd-x64@13.2.4": "@next/swc-linux-x64-gnu@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz#0bbe28979e3e868debc2cc06e45e186ce195b7f4" resolved "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086"
integrity sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ== integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA==
"@next/swc-linux-arm-gnueabihf@13.2.4": "@next/swc-linux-x64-musl@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz#1d28d2203f5a7427d6e7119d7bcb5fc40959fb3e" resolved "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b"
integrity sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg== integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw==
"@next/swc-linux-arm64-gnu@13.2.4": "@next/swc-win32-arm64-msvc@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.4.tgz#eb26448190948cdf4c44b8f34110a3ecea32f1d0" resolved "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093"
integrity sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg== integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA==
"@next/swc-linux-arm64-musl@13.2.4": "@next/swc-win32-ia32-msvc@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.4.tgz#c4227c0acd94a420bb14924820710e6284d234d3" resolved "https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd"
integrity sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw== integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w==
"@next/swc-linux-x64-gnu@13.2.4": "@next/swc-win32-x64-msvc@13.3.0":
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.4.tgz#6bcb540944ee9b0209b33bfc23b240c2044dfc3e" resolved "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770"
integrity sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ== integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ==
"@next/swc-linux-x64-musl@13.2.4":
version "13.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.4.tgz#ce21e43251eaf09a09df39372b2c3e38028c30ff"
integrity sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA==
"@next/swc-win32-arm64-msvc@13.2.4":
version "13.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.4.tgz#68220063d8e5e082f5465498675640dedb670ff1"
integrity sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw==
"@next/swc-win32-ia32-msvc@13.2.4":
version "13.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.4.tgz#7c120ab54a081be9566df310bed834f168252990"
integrity sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw==
"@next/swc-win32-x64-msvc@13.2.4":
version "13.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.4.tgz#5abda92fe12b9829bf7951c4a221282c56041144"
integrity sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw==
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
version "2.1.5" version "2.1.5"
@ -1730,6 +1710,13 @@ browserslist@^4.21.3, browserslist@^4.21.5:
node-releases "^2.0.8" node-releases "^2.0.8"
update-browserslist-db "^1.0.10" update-browserslist-db "^1.0.10"
busboy@1.6.0:
version "1.6.0"
resolved "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
dependencies:
streamsearch "^1.1.0"
call-bind@^1.0.0, call-bind@^1.0.2: call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@ -3937,30 +3924,27 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
next@^13.2.3: next@^13.3.0:
version "13.2.4" version "13.3.0"
resolved "https://registry.yarnpkg.com/next/-/next-13.2.4.tgz#2363330392b0f7da02ab41301f60857ffa7f67d6" resolved "https://registry.npmmirror.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1"
integrity sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw== integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA==
dependencies: dependencies:
"@next/env" "13.2.4" "@next/env" "13.3.0"
"@swc/helpers" "0.4.14" "@swc/helpers" "0.4.14"
busboy "1.6.0"
caniuse-lite "^1.0.30001406" caniuse-lite "^1.0.30001406"
postcss "8.4.14" postcss "8.4.14"
styled-jsx "5.1.1" styled-jsx "5.1.1"
optionalDependencies: optionalDependencies:
"@next/swc-android-arm-eabi" "13.2.4" "@next/swc-darwin-arm64" "13.3.0"
"@next/swc-android-arm64" "13.2.4" "@next/swc-darwin-x64" "13.3.0"
"@next/swc-darwin-arm64" "13.2.4" "@next/swc-linux-arm64-gnu" "13.3.0"
"@next/swc-darwin-x64" "13.2.4" "@next/swc-linux-arm64-musl" "13.3.0"
"@next/swc-freebsd-x64" "13.2.4" "@next/swc-linux-x64-gnu" "13.3.0"
"@next/swc-linux-arm-gnueabihf" "13.2.4" "@next/swc-linux-x64-musl" "13.3.0"
"@next/swc-linux-arm64-gnu" "13.2.4" "@next/swc-win32-arm64-msvc" "13.3.0"
"@next/swc-linux-arm64-musl" "13.2.4" "@next/swc-win32-ia32-msvc" "13.3.0"
"@next/swc-linux-x64-gnu" "13.2.4" "@next/swc-win32-x64-msvc" "13.3.0"
"@next/swc-linux-x64-musl" "13.2.4"
"@next/swc-win32-arm64-msvc" "13.2.4"
"@next/swc-win32-ia32-msvc" "13.2.4"
"@next/swc-win32-x64-msvc" "13.2.4"
node-domexception@^1.0.0: node-domexception@^1.0.0:
version "1.0.0" version "1.0.0"
@ -4668,6 +4652,11 @@ stop-iteration-iterator@^1.0.0:
dependencies: dependencies:
internal-slot "^1.0.4" internal-slot "^1.0.4"
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
string-argv@^0.3.1: string-argv@^0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"