From d7edcadec776d283d038cf98649ca79bd91fa247 Mon Sep 17 00:00:00 2001 From: Muhammet Demirel Date: Mon, 10 Apr 2023 12:04:30 +0300 Subject: [PATCH 01/13] Added new language, Turkish. --- app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/locales/es.ts | 1 + app/locales/index.ts | 7 +- app/locales/it.ts | 1 + app/locales/tr.ts | 177 +++++++++++++++++++++++++++++++++++++++++++ app/locales/tw.ts | 1 + 7 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 app/locales/tr.ts diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 9bd4b0a3f..d3df950b5 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -72,6 +72,7 @@ const cn = { tw: "繁體中文", es: "Español", it: "Italiano", + tr: "Türkçe", }, }, Avatar: "头像", diff --git a/app/locales/en.ts b/app/locales/en.ts index 731309034..6b63f21be 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -75,6 +75,7 @@ const en: LocaleType = { tw: "繁體中文", es: "Español", it: "Italiano", + tr: "Türkçe", }, }, Avatar: "Avatar", diff --git a/app/locales/es.ts b/app/locales/es.ts index 06277f6b5..eac504dd8 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -75,6 +75,7 @@ const es: LocaleType = { tw: "繁體中文", es: "Español", it: "Italiano", + tr: "Türkçe", }, }, Avatar: "Avatar", diff --git a/app/locales/index.ts b/app/locales/index.ts index 5c41eeb77..d7a1aefe5 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -3,10 +3,11 @@ import EN from "./en"; import TW from "./tw"; import ES from "./es"; import IT from "./it"; +import TR from "./tr"; export type { LocaleType } from "./cn"; -export const AllLangs = ["en", "cn", "tw", "es", "it"] as const; +export const AllLangs = ["en", "cn", "tw", "es", "it", "tr"] as const; type Lang = (typeof AllLangs)[number]; const LANG_KEY = "lang"; @@ -50,6 +51,8 @@ export function getLang(): Lang { return "es"; } else if (lang.includes("it")) { return "it"; + } else if (lang.includes("tr")) { + return "tr"; } else { return "en"; } @@ -60,4 +63,4 @@ export function changeLang(lang: Lang) { location.reload(); } -export default { en: EN, cn: CN, tw: TW, es: ES, it: IT }[getLang()]; +export default { en: EN, cn: CN, tw: TW, es: ES, it: IT, tr: TR }[getLang()]; diff --git a/app/locales/it.ts b/app/locales/it.ts index 70967d966..3c11e708d 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -75,6 +75,7 @@ const it: LocaleType = { tw: "繁體中文", es: "Español", it: "Italiano", + tr: "Türkçe", }, }, Avatar: "Avatar", diff --git a/app/locales/tr.ts b/app/locales/tr.ts new file mode 100644 index 000000000..354786818 --- /dev/null +++ b/app/locales/tr.ts @@ -0,0 +1,177 @@ +import { SubmitKey } from "../store/app"; +import type { LocaleType } from "./index"; + +const tr: LocaleType = { + WIP: "Çalışma devam ediyor...", + Error: { + Unauthorized: + "Yetkisiz erişim, lütfen erişim kodunu ayarlar sayfasından giriniz.", + }, + ChatItem: { + ChatItemCount: (count: number) => `${count} mesaj`, + }, + Chat: { + SubTitle: (count: number) => `ChatGPT tarafından ${count} mesaj`, + Actions: { + ChatList: "Sohbet Listesine Git", + CompressedHistory: "Sıkıştırılmış Geçmiş Bellek Komutu", + Export: "Tüm Mesajları Markdown Olarak Dışa Aktar", + Copy: "Kopyala", + Stop: "Durdur", + Retry: "Tekrar Dene", + }, + Rename: "Sohbeti Yeniden Adlandır", + Typing: "Yazıyor…", + Input: (submitKey: string) => { + var inputHints = `Göndermek için ${submitKey}`; + if (submitKey === String(SubmitKey.Enter)) { + inputHints += ", kaydırmak için Shift + Enter"; + } + return inputHints + ", komutları aramak için / (eğik çizgi)"; + }, + Send: "Gönder", + }, + Export: { + Title: "Tüm Mesajlar", + Copy: "Tümünü Kopyala", + Download: "İndir", + MessageFromYou: "Sizin Mesajınız", + MessageFromChatGPT: "ChatGPT'nin Mesajı", + }, + Memory: { + Title: "Bellek Komutları", + EmptyContent: "Henüz değil.", + Send: "Belleği Gönder", + Copy: "Belleği Kopyala", + Reset: "Oturumu Sıfırla", + ResetConfirm: + "Sıfırlama, geçerli görüşme geçmişini ve geçmiş belleği siler. Sıfırlamak istediğinizden emin misiniz?", + }, + Home: { + NewChat: "Yeni Sohbet", + DeleteChat: "Seçili sohbeti silmeyi onaylıyor musunuz?", + DeleteToast: "Sohbet Silindi", + Revert: "Geri Al", + }, + Settings: { + Title: "Ayarlar", + SubTitle: "Tüm Ayarlar", + Actions: { + ClearAll: "Tüm Verileri Temizle", + ResetAll: "Tüm Ayarları Sıfırla", + Close: "Kapat", + ConfirmResetAll: { + Confirm: "Tüm ayarları sıfırlamak istediğinizden emin misiniz?", + }, + ConfirmClearAll: { + Confirm: "Tüm sohbeti sıfırlamak istediğinizden emin misiniz?", + }, + }, + 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", + }, + }, + Avatar: "Avatar", + FontSize: { + Title: "Yazı Boyutu", + SubTitle: "Sohbet içeriğinin yazı boyutunu ayarlayın", + }, + Update: { + Version: (x: string) => `Sürüm: ${x}`, + IsLatest: "En son sürüm", + CheckUpdate: "Güncellemeyi Kontrol Et", + IsChecking: "Güncelleme kontrol ediliyor...", + FoundUpdate: (x: string) => `Yeni sürüm bulundu: ${x}`, + GoToUpdate: "Güncelle", + }, + SendKey: "Gönder Tuşu", + Theme: "Tema", + TightBorder: "Tam Ekran", + SendPreviewBubble: "Mesaj Önizleme Balonu", + Prompt: { + Disable: { + Title: "Otomatik tamamlamayı devre dışı bırak", + SubTitle: "Otomatik tamamlamayı kullanmak için / (eğik çizgi) girin", + }, + List: "Komut Listesi", + ListCount: (builtin: number, custom: number) => + `${builtin} yerleşik, ${custom} kullanıcı tanımlı`, + Edit: "Düzenle", + }, + HistoryCount: { + Title: "Ekli Mesaj Sayısı", + SubTitle: "İstek başına ekli gönderilen mesaj sayısı", + }, + CompressThreshold: { + Title: "Geçmiş Sıkıştırma Eşiği", + SubTitle: + "Sıkıştırılmamış mesajların uzunluğu bu değeri aşarsa sıkıştırılır", + }, + Token: { + Title: "API Anahtarı", + SubTitle: "Erişim kodu sınırını yoksaymak için anahtarınızı kullanın", + Placeholder: "OpenAI API Anahtarı", + }, + Usage: { + Title: "Hesap Bakiyesi", + SubTitle(used: any, total: any) { + return `Bu ay kullanılan $${used}, abonelik $${total}`; + }, + IsChecking: "Kontrol ediliyor...", + Check: "Tekrar Kontrol Et", + NoAccess: "Bakiyeyi kontrol etmek için API anahtarını girin", + }, + AccessCode: { + Title: "Erişim Kodu", + SubTitle: "Erişim kontrolü etkinleştirme", + Placeholder: "Erişim Kodu Gerekiyor", + }, + Model: "Model", + Temperature: { + Title: "Gerçeklik", + SubTitle: "Daha büyük bir değer girildiğinde gerçeklik oranı düşer ve daha rastgele çıktılar üretir", + }, + MaxTokens: { + Title: "Maksimum Belirteç", + SubTitle: "Girdi belirteçlerinin ve oluşturulan belirteçlerin maksimum uzunluğu", + }, + PresencePenlty: { + Title: "Varlık Cezası", + SubTitle: + "Daha büyük bir değer, yeni konular hakkında konuşma olasılığını artırır", + }, + }, + Store: { + DefaultTopic: "Yeni Konuşma", + BotHello: "Merhaba! Size bugün nasıl yardımcı olabilirim?", + Error: "Bir şeyler yanlış gitti. Lütfen daha sonra tekrar deneyiniz.", + Prompt: { + History: (content: string) => + "Bu, yapay zeka ile kullanıcı arasındaki sohbet geçmişinin bir özetidir: " + + content, + Topic: + "Lütfen herhangi bir giriş, noktalama işareti, tırnak işareti, nokta, sembol veya ek metin olmadan konuşmamızı özetleyen dört ila beş kelimelik bir başlık oluşturun. Çevreleyen tırnak işaretlerini kaldırın.", + Summarize: + "Gelecekteki bağlam için bir bilgi istemi olarak kullanmak üzere tartışmamızı en fazla 200 kelimeyle özetleyin.", + }, + ConfirmClearAll: "Tüm sohbet ve ayar verilerini temizlemeyi onaylıyor musunuz?", + }, + Copy: { + Success: "Panoya kopyalandı", + Failed: "Kopyalama başarısız oldu, lütfen panoya erişim izni verin", + }, + Context: { + Toast: (x: any) => `${x} bağlamsal bellek komutu`, + Edit: "Bağlamsal ve Bellek Komutları", + Add: "Yeni Ekle", + }, +}; + +export default tr; diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 3861a671c..89924b726 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -73,6 +73,7 @@ const tw: LocaleType = { tw: "繁體中文", es: "Español", it: "Italiano", + tr: "Türkçe", }, }, Avatar: "大頭貼", From 2ec99bbb70744f4e72a8513a96bad5d1b4b5dc66 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 10 Apr 2023 17:39:21 +0800 Subject: [PATCH 02/13] Update docker.yml --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a7a29644d..8ac96f193 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -43,7 +43,7 @@ jobs: uses: docker/build-push-action@v4 with: context: . - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From 7670b8b73825300ba792d893c3a8b7f442bac8f5 Mon Sep 17 00:00:00 2001 From: aooyoo Date: Mon, 10 Apr 2023 14:01:54 +0000 Subject: [PATCH 03/13] add new language, Japanese --- app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/locales/es.ts | 1 + app/locales/index.ts | 9 ++- app/locales/it.ts | 1 + app/locales/jp.ts | 182 +++++++++++++++++++++++++++++++++++++++++++ app/locales/tr.ts | 10 ++- app/locales/tw.ts | 1 + 8 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 app/locales/jp.ts diff --git a/app/locales/cn.ts b/app/locales/cn.ts index d3df950b5..fbb418471 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -73,6 +73,7 @@ const cn = { es: "Español", it: "Italiano", tr: "Türkçe", + jr: "日本語", }, }, Avatar: "头像", diff --git a/app/locales/en.ts b/app/locales/en.ts index 6b63f21be..8ffc07816 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -76,6 +76,7 @@ const en: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", + jr: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/es.ts b/app/locales/es.ts index eac504dd8..0939cc7a0 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -76,6 +76,7 @@ const es: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", + jr: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/index.ts b/app/locales/index.ts index d7a1aefe5..dff1e6614 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -4,10 +4,11 @@ import TW from "./tw"; import ES from "./es"; import IT from "./it"; import TR from "./tr"; +import JP from "./jp"; export type { LocaleType } from "./cn"; -export const AllLangs = ["en", "cn", "tw", "es", "it", "tr"] as const; +export const AllLangs = ["en", "cn", "tw", "es", "it", "tr", "jp"] as const; type Lang = (typeof AllLangs)[number]; const LANG_KEY = "lang"; @@ -53,6 +54,8 @@ export function getLang(): Lang { return "it"; } else if (lang.includes("tr")) { return "tr"; + } else if (lang.includes("jp")) { + return "jp"; } else { return "en"; } @@ -63,4 +66,6 @@ export function changeLang(lang: Lang) { location.reload(); } -export default { en: EN, cn: CN, tw: TW, es: ES, it: IT, tr: TR }[getLang()]; +export default { en: EN, cn: CN, tw: TW, es: ES, it: IT, tr: TR, jp: JP }[ + getLang() +]; diff --git a/app/locales/it.ts b/app/locales/it.ts index 3c11e708d..0a3cf18d9 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -76,6 +76,7 @@ const it: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", + jr: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/jp.ts b/app/locales/jp.ts new file mode 100644 index 000000000..98a62fb0c --- /dev/null +++ b/app/locales/jp.ts @@ -0,0 +1,182 @@ +import { SubmitKey } from "../store/app"; + +const jp = { + WIP: "この機能は開発中です……", + Error: { + Unauthorized: + "現在は未承認状態です。左下の設定ボタンをクリックし、アクセスパスワードを入力してください。", + }, + ChatItem: { + ChatItemCount: (count: number) => `${count} 通のチャット`, + }, + Chat: { + SubTitle: (count: number) => `ChatGPTとの ${count} 通のチャット`, + Actions: { + ChatList: "メッセージリストを表示", + CompressedHistory: "圧縮された履歴プロンプトを表示", + Export: "チャット履歴をエクスポート", + Copy: "コピー", + Stop: "停止", + Retry: "リトライ", + }, + Rename: "チャットの名前を変更", + Typing: "入力中…", + Input: (submitKey: string) => { + var inputHints = `${submitKey} で送信`; + if (submitKey === String(SubmitKey.Enter)) { + inputHints += ",Shift + Enter で改行"; + } + return inputHints + ",/ で自動補完をトリガー"; + }, + Send: "送信", + }, + Export: { + Title: "チャット履歴をMarkdown形式でエクスポート", + Copy: "すべてコピー", + Download: "ファイルをダウンロード", + MessageFromYou: "あなたからのメッセージ", + MessageFromChatGPT: "ChatGPTからのメッセージ", + }, + Memory: { + Title: "履歴メモリ", + EmptyContent: "まだ記憶されていません", + Send: "メモリを送信", + Copy: "メモリをコピー", + Reset: "チャットをリセット", + ResetConfirm: + "リセット後、現在のチャット履歴と過去のメモリがクリアされます。リセットしてもよろしいですか?", + }, + Home: { + NewChat: "新しいチャット", + DeleteChat: "選択したチャットを削除してもよろしいですか?", + DeleteToast: "チャットが削除されました", + Revert: "元に戻す", + }, + Settings: { + Title: "設定", + SubTitle: "設定オプション", + Actions: { + ClearAll: "すべてのデータをクリア", + ResetAll: "すべてのオプションをリセット", + Close: "閉じる", + ConfirmResetAll: { + Confirm: "すべての設定をリセットしてもよろしいですか?", + }, + ConfirmClearAll: { + Confirm: "すべてのチャットをリセットしてもよろしいですか?", + }, + }, + Lang: { + Name: "Language", + Options: { + cn: "简体中文", + en: "English", + tw: "繁體中文", + es: "Español", + it: "Italiano", + tr: "Türkçe", + jr: "日本語", + }, + }, + Avatar: "アバター", + FontSize: { + Title: "フォントサイズ", + SubTitle: "チャット内容のフォントサイズ", + }, + + Update: { + Version: (x: string) => `現在のバージョン:${x}`, + IsLatest: "最新バージョンです", + CheckUpdate: "アップデートを確認", + IsChecking: "アップデートを確認しています...", + FoundUpdate: (x: string) => `新しいバージョンが見つかりました:${x}`, + GoToUpdate: "更新する", + }, + SendKey: "送信キー", + Theme: "テーマ", + TightBorder: "ボーダーレスモード", + SendPreviewBubble: "プレビューバブルの送信", + Prompt: { + Disable: { + Title: "プロンプトの自動補完を無効にする", + SubTitle: + "入力フィールドの先頭に / を入力すると、自動補完がトリガーされます。", + }, + List: "カスタムプロンプトリスト", + ListCount: (builtin: number, custom: number) => + `組み込み ${builtin} 件、ユーザー定義 ${custom} 件`, + Edit: "編集", + }, + HistoryCount: { + Title: "履歴メッセージ数を添付", + SubTitle: "リクエストごとに添付する履歴メッセージ数", + }, + CompressThreshold: { + Title: "履歴メッセージの長さ圧縮しきい値", + SubTitle: + "圧縮されていない履歴メッセージがこの値を超えた場合、圧縮が行われます。", + }, + Token: { + Title: "APIキー", + SubTitle: "自分のキーを使用してパスワードアクセス制限を迂回する", + Placeholder: "OpenAI APIキー", + }, + Usage: { + Title: "残高照会", + SubTitle(used: any, total: any) { + return `今月は $${used} を使用しました。総額は $${total} です。`; + }, + IsChecking: "確認中...", + Check: "再確認", + NoAccess: "APIキーまたはアクセスパスワードを入力して残高を表示", + }, + AccessCode: { + Title: "アクセスパスワード", + SubTitle: "暗号化アクセスが有効になっています", + Placeholder: "アクセスパスワードを入力してください", + }, + Model: "モデル (model)", + Temperature: { + Title: "ランダム性 (temperature)", + SubTitle: + "値が大きいほど、回答がランダムになります。1以上の値には文字化けが含まれる可能性があります。", + }, + MaxTokens: { + Title: "シングルレスポンス制限 (max_tokens)", + SubTitle: "1回のインタラクションで使用される最大トークン数", + }, + PresencePenlty: { + Title: "トピックの新鮮度 (presence_penalty)", + SubTitle: "値が大きいほど、新しいトピックへの展開が可能になります。", + }, + }, + Store: { + DefaultTopic: "新しいチャット", + BotHello: "何かお手伝いできることはありますか", + Error: "エラーが発生しました。しばらくしてからやり直してください。", + Prompt: { + History: (content: string) => + "これは、AI とユーザの過去のチャットを要約した前提となるストーリーです:" + + content, + Topic: + "4~5文字でこの文章の簡潔な主題を返してください。説明、句読点、感嘆詞、余分なテキストは無しで。もし主題がない場合は、「おしゃべり」を返してください", + Summarize: + "あなたとユーザの会話を簡潔にまとめて、後続のコンテキストプロンプトとして使ってください。200字以内に抑えてください。", + }, + ConfirmClearAll: + "すべてのチャット、設定データをクリアしてもよろしいですか?", + }, + Copy: { + Success: "クリップボードに書き込みました", + Failed: "コピーに失敗しました。クリップボード許可を与えてください。", + }, + Context: { + Toast: (x: any) => `前置コンテキストが ${x} 件設定されました`, + Edit: "前置コンテキストと履歴メモリ", + Add: "新規追加", + }, +}; + +export type LocaleType = typeof jp; + +export default jp; diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 354786818..d5ac93807 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -76,6 +76,7 @@ const tr: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", + jr: "日本語", }, }, Avatar: "Avatar", @@ -136,11 +137,13 @@ const tr: LocaleType = { Model: "Model", Temperature: { Title: "Gerçeklik", - SubTitle: "Daha büyük bir değer girildiğinde gerçeklik oranı düşer ve daha rastgele çıktılar üretir", + SubTitle: + "Daha büyük bir değer girildiğinde gerçeklik oranı düşer ve daha rastgele çıktılar üretir", }, MaxTokens: { Title: "Maksimum Belirteç", - SubTitle: "Girdi belirteçlerinin ve oluşturulan belirteçlerin maksimum uzunluğu", + SubTitle: + "Girdi belirteçlerinin ve oluşturulan belirteçlerin maksimum uzunluğu", }, PresencePenlty: { Title: "Varlık Cezası", @@ -161,7 +164,8 @@ const tr: LocaleType = { Summarize: "Gelecekteki bağlam için bir bilgi istemi olarak kullanmak üzere tartışmamızı en fazla 200 kelimeyle özetleyin.", }, - ConfirmClearAll: "Tüm sohbet ve ayar verilerini temizlemeyi onaylıyor musunuz?", + ConfirmClearAll: + "Tüm sohbet ve ayar verilerini temizlemeyi onaylıyor musunuz?", }, Copy: { Success: "Panoya kopyalandı", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 89924b726..eadfa275a 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -74,6 +74,7 @@ const tw: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", + jr: "日本語", }, }, Avatar: "大頭貼", From 042e989ebbefebb28c987114d52d8bc0e6ea7f73 Mon Sep 17 00:00:00 2001 From: aooyoo Date: Mon, 10 Apr 2023 14:12:31 +0000 Subject: [PATCH 04/13] fix spelling errors --- app/locales/cn.ts | 2 +- app/locales/en.ts | 2 +- app/locales/es.ts | 2 +- app/locales/it.ts | 2 +- app/locales/jp.ts | 2 +- app/locales/tr.ts | 2 +- app/locales/tw.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index fbb418471..9973a3c68 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -73,7 +73,7 @@ const cn = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "头像", diff --git a/app/locales/en.ts b/app/locales/en.ts index 8ffc07816..bd417aa84 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -76,7 +76,7 @@ const en: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/es.ts b/app/locales/es.ts index 0939cc7a0..88bcd2012 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -76,7 +76,7 @@ const es: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/it.ts b/app/locales/it.ts index 0a3cf18d9..3cd768fed 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -76,7 +76,7 @@ const it: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/jp.ts b/app/locales/jp.ts index 98a62fb0c..50ac21609 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -75,7 +75,7 @@ const jp = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "アバター", diff --git a/app/locales/tr.ts b/app/locales/tr.ts index d5ac93807..708d2d7d5 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -76,7 +76,7 @@ const tr: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "Avatar", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index eadfa275a..77975b896 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -74,7 +74,7 @@ const tw: LocaleType = { es: "Español", it: "Italiano", tr: "Türkçe", - jr: "日本語", + jp: "日本語", }, }, Avatar: "大頭貼", From 8df8ee8936505f19bfbb59e550df5dca47253f49 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 10 Apr 2023 22:46:58 +0800 Subject: [PATCH 05/13] fix: #676 docker override old proxy files --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 018396ef3..6aa1bfb4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,7 +46,7 @@ CMD if [ -n "$PROXY_URL" ]; then \ host=$(echo $PROXY_URL | cut -d/ -f3 | cut -d: -f1); \ port=$(echo $PROXY_URL | cut -d: -f3); \ conf=/etc/proxychains.conf; \ - echo "strict_chain" >> $conf; \ + echo "strict_chain" > $conf; \ echo "proxy_dns" >> $conf; \ echo "remote_dns_subnet 224" >> $conf; \ echo "tcp_read_time_out 15000" >> $conf; \ From 7aee53ea05494ef55412a1e2745a8a9ee8d497d8 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 10 Apr 2023 23:13:20 +0800 Subject: [PATCH 06/13] fix: #507 break cjk chars in stream mode --- app/api/chat-stream/route.ts | 2 +- app/requests.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/api/chat-stream/route.ts b/app/api/chat-stream/route.ts index 526623ce1..41f135495 100644 --- a/app/api/chat-stream/route.ts +++ b/app/api/chat-stream/route.ts @@ -40,7 +40,7 @@ async function createStream(req: NextRequest) { const parser = createParser(onParse); for await (const chunk of res.body as any) { - parser.feed(decoder.decode(chunk)); + parser.feed(decoder.decode(chunk, { stream: true })); } }, }); diff --git a/app/requests.ts b/app/requests.ts index ee3498c11..6254c7c78 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -171,7 +171,7 @@ export async function requestChatStream( const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS); const content = await reader?.read(); clearTimeout(resTimeoutId); - const text = decoder.decode(content?.value); + const text = decoder.decode(content?.value, { stream: true }); responseText += text; const done = !content || content.done; From 9b61cb1335b33fc889cb1db2c8a3624144772d86 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 11 Apr 2023 01:21:34 +0800 Subject: [PATCH 07/13] refactor: build/runtime/client configs --- Dockerfile | 1 - app/api/access.ts | 17 --------------- app/components/settings.tsx | 9 ++++---- app/config/build.ts | 27 ++++++++++++++++++++++++ app/config/client.ts | 42 +++++++++++++++++++++++++++++++++++++ app/config/server.ts | 42 +++++++++++++++++++++++++++++++++++++ app/constant.ts | 1 + app/layout.tsx | 31 +++------------------------ app/page.tsx | 19 ++++++++++++++++- app/store/access.ts | 8 ++++--- app/store/update.ts | 6 +++--- app/utils.ts | 25 ---------------------- middleware.ts | 8 ++++--- next.config.js | 9 +++----- 14 files changed, 154 insertions(+), 91 deletions(-) delete mode 100644 app/api/access.ts create mode 100644 app/config/build.ts create mode 100644 app/config/client.ts create mode 100644 app/config/server.ts diff --git a/Dockerfile b/Dockerfile index 6aa1bfb4b..7ed7bc155 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,6 @@ 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 diff --git a/app/api/access.ts b/app/api/access.ts deleted file mode 100644 index d3e4c9cf9..000000000 --- a/app/api/access.ts +++ /dev/null @@ -1,17 +0,0 @@ -import md5 from "spark-md5"; - -export function getAccessCodes(): Set { - const code = process.env.CODE; - - try { - const codes = (code?.split(",") ?? []) - .filter((v) => !!v) - .map((v) => md5.hash(v.trim())); - return new Set(codes); - } catch (e) { - return new Set(); - } -} - -export const ACCESS_CODES = getAccessCodes(); -export const IS_IN_DOCKER = process.env.DOCKER; diff --git a/app/components/settings.tsx b/app/components/settings.tsx index bbb28b46e..d39985966 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -26,13 +26,14 @@ import { import { Avatar } from "./chat"; import Locale, { AllLangs, changeLang, getLang } from "../locales"; -import { getCurrentVersion, getEmojiUrl } from "../utils"; +import { getEmojiUrl } from "../utils"; import Link from "next/link"; import { UPDATE_URL } from "../constant"; import { SearchService, usePromptStore } from "../store/prompt"; import { requestUsage } from "../requests"; import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; +import { getClientSideConfig } from "../config/client"; function SettingItem(props: { title: string; @@ -88,9 +89,9 @@ export function Settings(props: { closeSettings: () => void }) { const updateStore = useUpdateStore(); const [checkingUpdate, setCheckingUpdate] = useState(false); - const currentId = getCurrentVersion(); + const currentVersion = getClientSideConfig()?.version; const remoteId = updateStore.remoteId; - const hasNewVersion = currentId !== remoteId; + const hasNewVersion = currentVersion !== remoteId; function checkUpdate(force = false) { setCheckingUpdate(true); @@ -224,7 +225,7 @@ export function Settings(props: { closeSettings: () => void }) { { + try { + const childProcess = require("child_process"); + return ( + childProcess + // .execSync("git describe --tags --abbrev=0") + .execSync("git rev-parse --short HEAD") + .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", + ); + } + + return { + commitId: COMMIT_ID, + }; +}; diff --git a/app/config/client.ts b/app/config/client.ts new file mode 100644 index 000000000..0e9c50d0b --- /dev/null +++ b/app/config/client.ts @@ -0,0 +1,42 @@ +import { RUNTIME_CONFIG_DOM } from "../constant"; + +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; +} + +export function getClientSideConfig() { + if (typeof window === "undefined") { + throw Error( + "[Client Config] you are importing a browser-only module outside of browser", + ); + } + + const dom = document.getElementById(RUNTIME_CONFIG_DOM); + + if (!dom) { + throw Error("[Config] Dont get config before page loading!"); + } + + try { + const fromServerConfig = JSON.parse(dom.innerText) as DangerConfig; + const fromBuildConfig = { + version: queryMeta("version"), + }; + return { + ...fromServerConfig, + ...fromBuildConfig, + }; + } catch (e) { + console.error("[Config] failed to parse client config"); + } +} diff --git a/app/config/server.ts b/app/config/server.ts new file mode 100644 index 000000000..798177e59 --- /dev/null +++ b/app/config/server.ts @@ -0,0 +1,42 @@ +import md5 from "spark-md5"; + +declare global { + namespace NodeJS { + interface ProcessEnv { + OPENAI_API_KEY?: string; + CODE?: string; + PROXY_URL?: string; + VERCEL?: string; + } + } +} + +const ACCESS_CODES = (function getAccessCodes(): Set { + const code = process.env.CODE; + + try { + const codes = (code?.split(",") ?? []) + .filter((v) => !!v) + .map((v) => md5.hash(v.trim())); + return new Set(codes); + } catch (e) { + return new Set(); + } +})(); + +export const getServerSideConfig = () => { + if (typeof process === "undefined") { + throw Error( + "[Server Config] you are importing a nodejs-only module outside of nodejs", + ); + } + + return { + apiKey: process.env.OPENAI_API_KEY, + code: process.env.CODE, + codes: ACCESS_CODES, + needCode: ACCESS_CODES.size > 0, + proxyUrl: process.env.PROXY_URL, + isVercel: !!process.env.VERCEL, + }; +}; diff --git a/app/constant.ts b/app/constant.ts index 8a519d44e..6f08ad756 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -5,3 +5,4 @@ export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`; 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"; diff --git a/app/layout.tsx b/app/layout.tsx index 49a6d644d..38748ef37 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,19 +2,9 @@ import "./styles/globals.scss"; import "./styles/markdown.scss"; import "./styles/highlight.scss"; -import process from "child_process"; -import { ACCESS_CODES, IS_IN_DOCKER } from "./api/access"; +import { getBuildConfig } from "./config/build"; -let COMMIT_ID: string | undefined; -try { - COMMIT_ID = process - // .execSync("git describe --tags --abbrev=0") - .execSync("git rev-parse --short HEAD") - .toString() - .trim(); -} catch (e) { - console.error("No git or not from git repo."); -} +const buildConfig = getBuildConfig(); export const metadata = { title: "ChatGPT Next Web", @@ -26,21 +16,6 @@ export const metadata = { themeColor: "#fafafa", }; -function Meta() { - const metas = { - version: COMMIT_ID ?? "unknown", - access: ACCESS_CODES.size > 0 || IS_IN_DOCKER ? "enabled" : "disabled", - }; - - return ( - <> - {Object.entries(metas).map(([k, v]) => ( - - ))} - - ); -} - export default function RootLayout({ children, }: { @@ -58,7 +33,7 @@ export default function RootLayout({ content="#151515" media="(prefers-color-scheme: dark)" /> - + diff --git a/app/page.tsx b/app/page.tsx index 1d1da2227..484e2c205 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,12 +1,29 @@ import { Analytics } from "@vercel/analytics/react"; import { Home } from "./components/home"; +import { getServerSideConfig } from "./config/server"; +import { RUNTIME_CONFIG_DOM } from "./constant"; + +const serverConfig = getServerSideConfig(); + +// Danger! Don not write any secret value here! +// 警告!不要在这里写入任何敏感信息! +const DANGER_CONFIG = { + needCode: serverConfig?.needCode, +}; + +declare global { + type DangerConfig = typeof DANGER_CONFIG; +} export default function App() { return ( <> +
+ {JSON.stringify(DANGER_CONFIG)} +
- + {serverConfig?.isVercel && } ); } diff --git a/app/store/access.ts b/app/store/access.ts index 5d43e6327..9eafc3a45 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,6 +1,6 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import { queryMeta } from "../utils"; +import { getClientSideConfig } from "../config/client"; export interface AccessControlStore { accessCode: string; @@ -20,7 +20,7 @@ export const useAccessStore = create()( token: "", accessCode: "", enabledAccessControl() { - return queryMeta("access") === "enabled"; + return !!getClientSideConfig()?.needCode; }, updateCode(code: string) { set((state) => ({ accessCode: code })); @@ -30,7 +30,9 @@ export const useAccessStore = create()( }, isAuthorized() { // has token or has code or disabled access control - return !!get().token || !!get().accessCode || !get().enabledAccessControl(); + return ( + !!get().token || !!get().accessCode || !get().enabledAccessControl() + ); }, }), { diff --git a/app/store/update.ts b/app/store/update.ts index 97fb343c3..78001f91c 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -1,7 +1,7 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; +import { getClientSideConfig } from "../config/client"; import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant"; -import { getCurrentVersion } from "../utils"; export interface UpdateStore { lastUpdate: number; @@ -22,7 +22,7 @@ export const useUpdateStore = create()( const overTenMins = Date.now() - get().lastUpdate > 10 * 60 * 1000; const shouldFetch = force || overTenMins; if (!shouldFetch) { - return getCurrentVersion(); + return getClientSideConfig()?.version ?? ""; } try { @@ -38,7 +38,7 @@ export const useUpdateStore = create()( return remoteId; } catch (error) { console.error("[Fetch Upstream Commit Id]", error); - return getCurrentVersion(); + return getClientSideConfig()?.version ?? ""; } }, }), diff --git a/app/utils.ts b/app/utils.ts index 9a792fd52..5c2b06975 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -69,31 +69,6 @@ export function selectOrCopy(el: HTMLElement, content: string) { return true; } -export 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; -} - -let currentId: string; -export function getCurrentVersion() { - if (currentId) { - return currentId; - } - - currentId = queryMeta("version"); - - return currentId; -} - export function getEmojiUrl(unified: string, style: EmojiStyle) { return `https://cdn.staticfile.org/emoji-datasource-apple/14.0.0/img/${style}/64/${unified}.png`; } diff --git a/middleware.ts b/middleware.ts index 9338a2c6b..31a417a2f 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,21 +1,23 @@ import { NextRequest, NextResponse } from "next/server"; -import { ACCESS_CODES } from "./app/api/access"; +import { getServerSideConfig } from "./app/config/server"; import md5 from "spark-md5"; export const config = { matcher: ["/api/openai", "/api/chat-stream"], }; +const serverConfig = getServerSideConfig(); + export function middleware(req: NextRequest) { const accessCode = req.headers.get("access-code"); const token = req.headers.get("token"); const hashedCode = md5.hash(accessCode ?? "").trim(); - console.log("[Auth] allowed hashed codes: ", [...ACCESS_CODES]); + console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]); console.log("[Auth] got access code:", accessCode); console.log("[Auth] hashed access code:", hashedCode); - if (ACCESS_CODES.size > 0 && !ACCESS_CODES.has(hashedCode) && !token) { + if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) { return NextResponse.json( { error: true, diff --git a/next.config.js b/next.config.js index fc164db9c..f7d5ff086 100644 --- a/next.config.js +++ b/next.config.js @@ -8,14 +8,11 @@ const nextConfig = { config.module.rules.push({ test: /\.svg$/, use: ["@svgr/webpack"], - }); // 针对 SVG 的处理规则 + }); return config; - } + }, + output: "standalone", }; -if (process.env.DOCKER) { - nextConfig.output = 'standalone' -} - module.exports = nextConfig; From d6e6dd09f06ed2797bfe5b9ea4803213179bed97 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 11 Apr 2023 02:54:31 +0800 Subject: [PATCH 08/13] feat: dynamic config --- app/api/config/route.ts | 21 +++++ app/components/home.tsx | 161 ++++++++++++++++++------------------ app/components/settings.tsx | 7 +- app/config/client.ts | 42 ---------- app/page.tsx | 17 +--- app/store/access.ts | 30 ++++++- app/store/update.ts | 34 ++++++-- middleware.ts | 2 +- 8 files changed, 160 insertions(+), 154 deletions(-) create mode 100644 app/api/config/route.ts delete mode 100644 app/config/client.ts diff --git a/app/api/config/route.ts b/app/api/config/route.ts new file mode 100644 index 000000000..e04e22a0c --- /dev/null +++ b/app/api/config/route.ts @@ -0,0 +1,21 @@ +import { NextRequest, NextResponse } from "next/server"; + +import { getServerSideConfig } from "../../config/server"; + +const serverConfig = getServerSideConfig(); + +// Danger! Don not write any secret value here! +// 警告!不要在这里写入任何敏感信息! +const DANGER_CONFIG = { + needCode: serverConfig.needCode, +}; + +declare global { + type DangerConfig = typeof DANGER_CONFIG; +} + +export async function POST(req: NextRequest) { + return NextResponse.json({ + needCode: serverConfig.needCode, + }); +} diff --git a/app/components/home.tsx b/app/components/home.tsx index e73b80417..3055b6885 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -2,13 +2,7 @@ require("../polyfill"); -import { - useState, - useEffect, - useRef, - useCallback, - MouseEventHandler, -} from "react"; +import { useState, useEffect, useRef } from "react"; import { IconButton } from "./button"; import styles from "./home.module.scss"; @@ -30,7 +24,6 @@ import { Chat } from "./chat"; import dynamic from "next/dynamic"; import { REPO_URL } from "../constant"; import { ErrorBoundary } from "./error"; -import { useDebounce } from "use-debounce"; export function Loading(props: { noLogo?: boolean }) { return ( @@ -165,96 +158,100 @@ function _Home() { } return ( -
+ <>
-
-
ChatGPT Next
-
- Build your own AI assistant. -
-
- -
-
-
{ - setOpenSettings(false); - setShowSideBar(false); - }} + className={ + styles.sidebar + ` ${showSideBar && styles["sidebar-show"]}` + } > - -
- -
-
-
- } - onClick={chatStore.deleteSession} - /> +
+
ChatGPT Next
+
+ Build your own AI assistant.
-
+
+ +
+
+ +
{ + setOpenSettings(false); + setShowSideBar(false); + }} + > + +
+ +
+
+
+ } + onClick={chatStore.deleteSession} + /> +
+
+ } + onClick={() => { + setOpenSettings(true); + setShowSideBar(false); + }} + shadow + /> +
+ +
+
} + icon={} + text={Locale.Home.NewChat} onClick={() => { - setOpenSettings(true); + createNewSession(); setShowSideBar(false); }} shadow />
- -
-
- } - text={Locale.Home.NewChat} - onClick={() => { - createNewSession(); - setShowSideBar(false); - }} - shadow - />
+ +
onDragMouseDown(e as any)} + >
-
onDragMouseDown(e as any)} - >
+
+ {openSettings ? ( + { + setOpenSettings(false); + setShowSideBar(true); + }} + /> + ) : ( + setShowSideBar(true)} + sideBarShowing={showSideBar} + /> + )} +
- -
- {openSettings ? ( - { - setOpenSettings(false); - setShowSideBar(true); - }} - /> - ) : ( - setShowSideBar(true)} - sideBarShowing={showSideBar} - /> - )} -
-
+ ); } diff --git a/app/components/settings.tsx b/app/components/settings.tsx index d39985966..e418d4843 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -33,7 +33,6 @@ import { SearchService, usePromptStore } from "../store/prompt"; import { requestUsage } from "../requests"; import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; -import { getClientSideConfig } from "../config/client"; function SettingItem(props: { title: string; @@ -89,13 +88,13 @@ export function Settings(props: { closeSettings: () => void }) { const updateStore = useUpdateStore(); const [checkingUpdate, setCheckingUpdate] = useState(false); - const currentVersion = getClientSideConfig()?.version; - const remoteId = updateStore.remoteId; + const currentVersion = updateStore.version; + const remoteId = updateStore.remoteVersion; const hasNewVersion = currentVersion !== remoteId; function checkUpdate(force = false) { setCheckingUpdate(true); - updateStore.getLatestCommitId(force).then(() => { + updateStore.getLatestVersion(force).then(() => { setCheckingUpdate(false); }); } diff --git a/app/config/client.ts b/app/config/client.ts deleted file mode 100644 index 0e9c50d0b..000000000 --- a/app/config/client.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { RUNTIME_CONFIG_DOM } from "../constant"; - -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; -} - -export function getClientSideConfig() { - if (typeof window === "undefined") { - throw Error( - "[Client Config] you are importing a browser-only module outside of browser", - ); - } - - const dom = document.getElementById(RUNTIME_CONFIG_DOM); - - if (!dom) { - throw Error("[Config] Dont get config before page loading!"); - } - - try { - const fromServerConfig = JSON.parse(dom.innerText) as DangerConfig; - const fromBuildConfig = { - version: queryMeta("version"), - }; - return { - ...fromServerConfig, - ...fromBuildConfig, - }; - } catch (e) { - console.error("[Config] failed to parse client config"); - } -} diff --git a/app/page.tsx b/app/page.tsx index 484e2c205..20b503174 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,27 +1,14 @@ import { Analytics } from "@vercel/analytics/react"; import { Home } from "./components/home"; + import { getServerSideConfig } from "./config/server"; -import { RUNTIME_CONFIG_DOM } from "./constant"; const serverConfig = getServerSideConfig(); -// Danger! Don not write any secret value here! -// 警告!不要在这里写入任何敏感信息! -const DANGER_CONFIG = { - needCode: serverConfig?.needCode, -}; - -declare global { - type DangerConfig = typeof DANGER_CONFIG; -} - -export default function App() { +export default async function App() { return ( <> -
- {JSON.stringify(DANGER_CONFIG)} -
{serverConfig?.isVercel && } diff --git a/app/store/access.ts b/app/store/access.ts index 9eafc3a45..aed131684 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,26 +1,33 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import { getClientSideConfig } from "../config/client"; export interface AccessControlStore { accessCode: string; token: string; + needCode: boolean; + updateToken: (_: string) => void; updateCode: (_: string) => void; enabledAccessControl: () => boolean; isAuthorized: () => boolean; + fetch: () => void; } export const ACCESS_KEY = "access-control"; +let fetchState = 0; // 0 not fetch, 1 fetching, 2 done + export const useAccessStore = create()( persist( (set, get) => ({ token: "", accessCode: "", + needCode: true, enabledAccessControl() { - return !!getClientSideConfig()?.needCode; + get().fetch(); + + return get().needCode; }, updateCode(code: string) { set((state) => ({ accessCode: code })); @@ -34,6 +41,25 @@ export const useAccessStore = create()( !!get().token || !!get().accessCode || !get().enabledAccessControl() ); }, + fetch() { + if (fetchState > 0) return; + fetchState = 1; + fetch("/api/config", { + method: "post", + body: null, + }) + .then((res) => res.json()) + .then((res: DangerConfig) => { + console.log("[Config] got config from server", res); + set(() => ({ ...res })); + }) + .catch(() => { + console.error("[Config] failed to fetch config"); + }) + .finally(() => { + fetchState = 2; + }); + }, }), { name: ACCESS_KEY, diff --git a/app/store/update.ts b/app/store/update.ts index 78001f91c..efcdc8a7b 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -1,28 +1,46 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import { getClientSideConfig } from "../config/client"; import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant"; export interface UpdateStore { lastUpdate: number; - remoteId: string; + remoteVersion: string; - getLatestCommitId: (force: boolean) => Promise; + version: string; + getLatestVersion: (force: boolean) => Promise; } export const UPDATE_KEY = "chat-update"; +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; +} + export const useUpdateStore = create()( persist( (set, get) => ({ lastUpdate: 0, - remoteId: "", + remoteVersion: "", + + version: "unknown", + + async getLatestVersion(force = false) { + set(() => ({ version: queryMeta("version") })); - async getLatestCommitId(force = false) { const overTenMins = Date.now() - get().lastUpdate > 10 * 60 * 1000; const shouldFetch = force || overTenMins; if (!shouldFetch) { - return getClientSideConfig()?.version ?? ""; + return get().version ?? "unknown"; } try { @@ -32,13 +50,13 @@ export const useUpdateStore = create()( const remoteId = (data[0].sha as string).substring(0, 7); set(() => ({ lastUpdate: Date.now(), - remoteId, + remoteVersion: remoteId, })); console.log("[Got Upstream] ", remoteId); return remoteId; } catch (error) { console.error("[Fetch Upstream Commit Id]", error); - return getClientSideConfig()?.version ?? ""; + return get().version ?? ""; } }, }), diff --git a/middleware.ts b/middleware.ts index 31a417a2f..c2e077706 100644 --- a/middleware.ts +++ b/middleware.ts @@ -32,7 +32,7 @@ export function middleware(req: NextRequest) { // inject api key if (!token) { - const apiKey = process.env.OPENAI_API_KEY; + const apiKey = serverConfig.apiKey; if (apiKey) { console.log("[Auth] set system token"); req.headers.set("token", apiKey); From 6841846613ecdea74cdf37291c5ffd907085191f Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 11 Apr 2023 02:56:48 +0800 Subject: [PATCH 09/13] fixup --- app/components/home.tsx | 150 +++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 77 deletions(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index 3055b6885..b9c6a8a3e 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -158,100 +158,96 @@ function _Home() { } return ( - <> +
+
+
ChatGPT Next
+
+ Build your own AI assistant. +
+
+ +
+
+
{ + setOpenSettings(false); + setShowSideBar(false); + }} > -
-
ChatGPT Next
-
- Build your own AI assistant. -
-
- -
-
+ +
-
{ - setOpenSettings(false); - setShowSideBar(false); - }} - > - -
- -
-
-
- } - onClick={chatStore.deleteSession} - /> -
-
- } - onClick={() => { - setOpenSettings(true); - setShowSideBar(false); - }} - shadow - /> -
- -
-
+
+
+
} - text={Locale.Home.NewChat} + icon={} + onClick={chatStore.deleteSession} + /> +
+
+ } onClick={() => { - createNewSession(); + setOpenSettings(true); setShowSideBar(false); }} shadow />
+
- -
onDragMouseDown(e as any)} - >
-
- -
- {openSettings ? ( - { - setOpenSettings(false); - setShowSideBar(true); +
+ } + text={Locale.Home.NewChat} + onClick={() => { + createNewSession(); + setShowSideBar(false); }} + shadow /> - ) : ( - setShowSideBar(true)} - sideBarShowing={showSideBar} - /> - )} +
+ +
onDragMouseDown(e as any)} + >
- + +
+ {openSettings ? ( + { + setOpenSettings(false); + setShowSideBar(true); + }} + /> + ) : ( + setShowSideBar(true)} + sideBarShowing={showSideBar} + /> + )} +
+
); } From f43d936ef0401ca375c144a4c61f8a451ae47a21 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 11 Apr 2023 11:15:24 +0800 Subject: [PATCH 10/13] Update faq-cn.md --- docs/faq-cn.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/faq-cn.md b/docs/faq-cn.md index 88293b9c6..7a71e75d8 100644 --- a/docs/faq-cn.md +++ b/docs/faq-cn.md @@ -1,7 +1,5 @@ # 常见问题 -> We are sorry that there is currently no English version of the FAQ. English users can use translation tools to access this document. We look forward to receiving your PR for an English version of the documentation. - ## 如何快速获得帮助? 1. 询问ChatGPT / Bing / 百度 / Google等。 2. 询问网友。请提供问题的背景信息和碰到问题的详细描述。高质量的提问容易获得有用的答案。 @@ -121,8 +119,9 @@ OpenAI只接受指定地区的信用卡(中国信用卡无法使用)。一 3. 网上找人代充 ## 如何使用GPT-4的API访问? -(4月6日更新)GPT-4的API访问需要单独申请。到以下地址填写你的信息进入申请队列waitlist(准备好你的OpenAI组织ID):https://openai.com/waitlist/gpt-4-api +- GPT-4的API访问需要单独申请。到以下地址填写你的信息进入申请队列waitlist(准备好你的OpenAI组织ID):https://openai.com/waitlist/gpt-4-api 之后等待邮件消息。 +- 开通 ChatGPT Plus 不代表有 GPT-4 权限,两者毫无关系。 ## 如何使用 Azure OpenAI 接口 请参考:[#371](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/371) From 9f92968b964a40b5d14e28606cc942f647a28679 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 11 Apr 2023 11:21:05 +0800 Subject: [PATCH 11/13] Update issue templates --- .github/ISSUE_TEMPLATE/反馈问题.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/反馈问题.md b/.github/ISSUE_TEMPLATE/反馈问题.md index 4545721e6..ea56aa6fa 100644 --- a/.github/ISSUE_TEMPLATE/反馈问题.md +++ b/.github/ISSUE_TEMPLATE/反馈问题.md @@ -7,6 +7,11 @@ assignees: '' --- +**反馈须知** +> 请在下方中括号内输入 x 来表示你已经知晓相关内容。 +- [ ] 我确认已经在 [常见问题](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/docs/faq-cn.md) 中搜索了此次反馈的问题,没有找到解答; +- [ ] 我确认已经在 [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) 列表(包括已经 Close 的)中搜索了此次反馈的问题,没有找到解答。 + **描述问题** 请在此描述你遇到了什么问题。 From a1c709bb58ac7d9ccb33637c06df3a097552bde6 Mon Sep 17 00:00:00 2001 From: peanut996 <849421294godw@gmail.com> Date: Tue, 11 Apr 2023 12:06:12 +0800 Subject: [PATCH 12/13] [refactor] rename ChatResponse. --- app/api/openai/typing.ts | 2 +- app/requests.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/api/openai/typing.ts b/app/api/openai/typing.ts index 8c4218467..b936530c3 100644 --- a/app/api/openai/typing.ts +++ b/app/api/openai/typing.ts @@ -4,4 +4,4 @@ import type { } from "openai"; export type ChatRequest = CreateChatCompletionRequest; -export type ChatReponse = CreateChatCompletionResponse; +export type ChatResponse = CreateChatCompletionResponse; diff --git a/app/requests.ts b/app/requests.ts index 6254c7c78..3d559e369 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -1,4 +1,4 @@ -import type { ChatRequest, ChatReponse } from "./api/openai/typing"; +import type { ChatRequest, ChatResponse } from "./api/openai/typing"; import { Message, ModelConfig, useAccessStore, useChatStore } from "./store"; import { showToast } from "./components/ui-lib"; @@ -67,7 +67,7 @@ export async function requestChat(messages: Message[]) { const res = await requestOpenaiClient("v1/chat/completions")(req); try { - const response = (await res.json()) as ChatReponse; + const response = (await res.json()) as ChatResponse; return response; } catch (error) { console.error("[Request Chat] ", error, res.body); From 8d2abe36a97e8a75e41e4640d35662f607a15e65 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 11 Apr 2023 16:04:36 +0800 Subject: [PATCH 13/13] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ba4a6dc49..b2d1e48ce 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. 一键免费部署你的私人 ChatGPT 网页应用。 -[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Join 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) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) +[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Join Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/231095592-330adc52-0337-4c13-8452-938ec169e367.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) [![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&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) @@ -20,6 +20,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. ## Features - **Deploy for free with one-click** on Vercel in under 1 minute +- Privacy first, all data stored locally in the browser - Responsive design, dark mode and PWA - Fast first screen loading speed (~100kb) - Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) @@ -45,6 +46,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - 在 1 分钟内使用 Vercel **免费一键部署** - 精心设计的 UI,响应式设计,支持深色模式,支持 PWA - 极快的首屏加载速度(~100kb) +- 隐私安全,所有数据保存在用户浏览器本地 - 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts) - 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话 - 一键导出聊天记录,完整的 Markdown 支持