From fa2ea2ada8dc8eed562374fe997f5e552fd2d6cd Mon Sep 17 00:00:00 2001 From: CorrectRoadH Date: Fri, 5 May 2023 22:54:38 +0800 Subject: [PATCH 001/185] chore: fix some error format in Chinese document (#79) --- README.zh-CN.md | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 5098b069..eb6c8f60 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -10,7 +10,7 @@ ## 介绍 -SQLChat是一个基于聊天的SQL客户端,使用自然语言向数据库提问和查询数据库。 +SQL Chat 是一个基于聊天的 SQL 客户端,使用自然语言向数据库提问和查询数据库。 ![Screenshot](https://raw.githubusercontent.com/sqlchat/sqlchat/main/public/screenshot1.webp) @@ -18,11 +18,11 @@ SQLChat是一个基于聊天的SQL客户端,使用自然语言向数据库提 ## Why -随着我们进入 [开发者工具2.0时代](https://www.sequoiacap.com/article/ai-powered-developer-tools/),使用基于聊天的界面重建现有工具的机会非常大。SQL Client也不例外。与在许多UI控件之间导航不同,基于聊天的界面更加直观。当然,前提是那可行,而我们的目标就是提供这种体验。 +随着我们进入 [开发者工具2.0时代](https://www.sequoiacap.com/article/ai-powered-developer-tools/),使用基于聊天的界面重建现有工具的机会非常大。SQL Client 也不例外。与在许多 UI 控件之间导航不同,基于聊天的界面更加直观。当然,前提是那可行,而我们的目标就是提供这种体验。 ## How -SQLChat是由 [Next.js](https://nextjs.org/) 构建的,它支持以下数据库,并将随着时间的推移支持更多: +SQL Chat 是由 [Next.js](https://nextjs.org/) 构建的,它支持以下数据库,并将随着时间的推移支持更多: - MySQL - PostgreSQL @@ -32,15 +32,15 @@ SQLChat是由 [Next.js](https://nextjs.org/) 构建的,它支持以下数据 - 所有数据库连接配置都本地存储在浏览器中,您也可以访问设置到清除数据。 -- 只有数据库模式提供给OpenAI API,表数据不会提供给OpenAI API。 +- 只有数据库模式提供给 OpenAI API,表数据不会提供给 OpenAI API。 - 如果使用 [sqlchat.ai](https://sqlchat.ai), 它会记录下这些匿名对话。 -## IP白名单 +## IP 白名单 -如果使用 [sqlchat.ai](https://sqlchat.ai) 连接数据库,则需要在数据库白名单IP中添加0.0.0.0(允许所有连接)。因为sqlchat。AI托管在 [Vercel](https://vercel.com/) 上 [使用动态IP](https://vercel.com/guides/how-to-allowlist-deployment-ip-address)。如果这是一个问题,请考虑下面的自主机选项。 +如果使用 [sqlchat.ai](https://sqlchat.ai) 连接数据库,则需要在数据库白名单 I P 中添加0.0.0.0(允许所有连接)。因为 sqlchat.ai 托管在 [Vercel](https://vercel.com/) 上 [使用动态 IP](https://vercel.com/guides/how-to-allowlist-deployment-ip-address)。如果这是一个问题,请考虑下面的自主机选项。 -## 使用Docker自托管 +## 使用 Docker 自托管 ```bash docker run --name sqlchat --platform linux/amd64 -p 3000:3000 sqlchat/sqlchat @@ -48,8 +48,8 @@ docker run --name sqlchat --platform linux/amd64 -p 3000:3000 sqlchat/sqlchat 您可以设置以下环境变量来定制部署: -- `OPENAI_API_KEY`: OpenAI API密钥,从 [here](https://beta.openai.com/docs/developer-quickstart/api-keys) 获取。 -- `OPENAI_API_ENDPOINT`: OpenAI API端点,默认为 `https://api.openai.com`。 +- `OPENAI_API_KEY`: OpenAI API 密钥,从 [here](https://beta.openai.com/docs/developer-quickstart/api-keys) 获取。 +- `OPENAI_API_ENDPOINT`: OpenAI API 端点,默认为 `https://api.openai.com`。 ```bash docker run --name sqlchat --platform linux/amd64 --env OPENAI_API_KEY=xxx --env OPENAI_API_ENDPOINT=yyy -p 3000:3000 sqlchat/sqlchat @@ -85,13 +85,19 @@ docker run --name sqlchat --platform linux/amd64 --env OPENAI_API_KEY=xxx --env pnpm prisma generate ``` -3. 添加初始的数据 +3. 迁移 schema + + ```bash + pnpm prisma migrate dev + ``` + +4. 添加初始的数据 ```bash pnpm prisma db seed ``` -## Star历史 +## Star 历史 [![Star History Chart](https://api.star-history.com/svg?repos=sqlchat/sqlchat&type=Date)](https://star-history.com/#sqlchat/sqlchat&Date) @@ -113,11 +119,11 @@ docker run --name sqlchat --platform linux/amd64 --env OPENAI_API_KEY=xxx --env ## 许可 -本项目使用BSL许可证。请参阅 [LICENSE](LICENSE) 文件以获取完整的许可文本。 +本项目使用 BSL 许可证。请参阅 [LICENSE](LICENSE) 文件以获取完整的许可文本。 ## 常见问题 -
如何自托管SQL聊天? +
如何自托管 SQL Chat?

- 您可以一键将 `SQLChat` 部署到 `Vercel` @@ -133,7 +139,7 @@ docker run --name sqlchat --platform linux/amd64 --env OPENAI_API_KEY=xxx --env

-
如何使用我的OpenAI API密钥? +
如何使用我的 OpenAI API 密钥?

- 您可以在环境变量中设置 `OPENAI_API_KEY`。 @@ -150,7 +156,7 @@ docker run --name sqlchat --platform linux/amd64 --env OPENAI_API_KEY=xxx --env

它总是说我有网络连接问题?

-请确保您有一个稳定的网络连接,可以访问OpenAI API端点。如果您无法访问OpenAI API端点,您可以尝试在UI或环境变量中设置 `OPENAI_API_ENDPOINT`。 +请确保您有一个稳定的网络连接,可以访问 OpenAI API 端点。如果您无法访问 OpenAI API 端点,您可以尝试在 UI 或环境变量中设置 `OPENAI_API_ENDPOINT`。

From a80b0d71ac14d1736a527d23bd6789e9a4abede2 Mon Sep 17 00:00:00 2001 From: Tianzhou Chen Date: Fri, 5 May 2023 22:58:14 +0800 Subject: [PATCH 002/185] chore: only keep english, chinese simplified and spanish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are iterating very fast, just don’t have bandwith to handle many languages at this point --- src/components/LocaleSelector.tsx | 16 ----- src/locales/de.json | 97 ---------------------------- src/locales/i18n.ts | 16 ----- src/locales/jp.json | 97 ---------------------------- src/locales/th.json | 97 ---------------------------- src/locales/zh-Hant.json | 104 ------------------------------ 6 files changed, 427 deletions(-) delete mode 100644 src/locales/de.json delete mode 100644 src/locales/jp.json delete mode 100644 src/locales/th.json delete mode 100644 src/locales/zh-Hant.json diff --git a/src/components/LocaleSelector.tsx b/src/components/LocaleSelector.tsx index 1441ced5..0e3b65fc 100644 --- a/src/components/LocaleSelector.tsx +++ b/src/components/LocaleSelector.tsx @@ -16,26 +16,10 @@ const localeItemList: LocaleItem[] = [ value: "zh", label: "简体中文", }, - { - value: "zhHant", - label: "繁體中文", - }, { value: "es", label: "Español", }, - { - value: "jp", - label: "日本語", - }, - { - value: "de", - label: "Deutsch", - }, - { - value: "th", - label: "ยาสุฟุมิ", - }, ]; const LocaleSelector = () => { diff --git a/src/locales/de.json b/src/locales/de.json deleted file mode 100644 index 1387649e..00000000 --- a/src/locales/de.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "common": { - "clear": "Löschen", - "close": "Schließen", - "confirm": "Bestätigen", - "save": "Speichern", - "edit": "Bearbeiten", - "loading": "Laden", - "setting": "Einstellung", - "copy": "Kopieren", - "delete": "Löschen", - "execute": "Ausführen", - "sign-in": "Anmelden", - "sign-out": "Austragen", - "back": "Zurückkehren", - "quota": "Quote" - }, - "conversation": { - "new-chat": "Neuer Chat", - "conversation-title": "Titel des Gesprächs", - "update": "Gespräch aktualisieren", - "title": "Titel" - }, - "connection": { - "self": "Verbindung", - "new": "Verbindung erstellen", - "edit": "Verbindung bearbeiten", - "select-database": "Wählen Sie Ihre Datenbank aus", - "database-type": "Datenbanktyp", - "title": "Titel", - "host": "Host", - "port": "Port", - "database-name": "Datenbankname", - "username": "Benutzername", - "password": "Passwort" - }, - "assistant": { - "self": "Bot", - "create-your-bot": "Sie finden den benötigten Bot nicht? Erstellen Sie einen" - }, - "execution": { - "title": "SQL ausführen", - "message": { - "executing": "Abfrage wird ausgeführt...", - "no-connection": "Keine Verbindung ausgewählt", - "no-data": "Keine Daten zurückgegeben" - } - }, - "editor": { - "placeholder": "Geben Sie hier Ihre Frage ein..." - }, - "setting": { - "self": "Einstellung", - "general": "Allgemein", - "plan": { - "free": "Frei", - "pro": "Profi", - "signup-for-more": "Melden Sie sich für mehr an", - "upgrade": "Aktualisierung" - }, - "billing": { - "self": "Abrechnung" - }, - "account": { - "self": "Konto" - }, - "basic": { - "self": "Grundlegend", - "language": "Sprache" - }, - "theme": { - "self": "Thema", - "system": "System folgen", - "light": "Hell", - "dark": "Dunkel" - }, - "openai-api-configuration": { - "self": "OpenAI API-Konfiguration" - }, - "data": { - "self": "Daten", - "clear-all-data": "Alle Daten löschen" - } - }, - "social": { - "join-discord-channel": "Discord-Kanal beitreten", - "join-wechat-group": "WeChat-Gruppe beitreten" - }, - "banner": { - "data-storage": "Verbindungseinstellungen werden in Ihrem lokalen Browser gespeichert", - "non-select-sql-warning": "Die aktuelle Anweisung kann eine nicht-SELECT SQL-Anweisung sein, die zu einer Änderung des Datenbankschemas oder der Daten führen wird. Stellen Sie sicher, dass Sie wissen, was Sie tun.", - "product-hunt": "🚀🚀🚀 Wir haben gerade auf Product Hunt gestartet, bitte geben Sie uns eine Stimme! 🚀🚀🚀" - }, - "payment": { - "self": "Zahlung" - } -} diff --git a/src/locales/i18n.ts b/src/locales/i18n.ts index 5bc9febb..3c4fd4bf 100644 --- a/src/locales/i18n.ts +++ b/src/locales/i18n.ts @@ -2,11 +2,7 @@ import i18n from "i18next"; import { initReactI18next } from "react-i18next"; import enLocale from "./en.json"; import zhLocale from "./zh.json"; -import zhHant from "./zh-Hant.json"; import esLocale from "./es.json"; -import jpLocale from "./jp.json"; -import deLocale from "./de.json"; -import thLocale from "./th.json"; i18n.use(initReactI18next).init({ resources: { @@ -16,21 +12,9 @@ i18n.use(initReactI18next).init({ zh: { translation: zhLocale, }, - zhHant: { - translation: zhHant, - }, es: { translation: esLocale, }, - jp: { - translation: jpLocale, - }, - de: { - translation: deLocale, - }, - th: { - translation: thLocale, - }, }, fallbackLng: "en", }); diff --git a/src/locales/jp.json b/src/locales/jp.json deleted file mode 100644 index 2ec08d83..00000000 --- a/src/locales/jp.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "common": { - "clear": "クリア", - "close": "閉じる", - "confirm": "確認", - "save": "保存", - "edit": "編集", - "loading": "読み込み中", - "setting": "設定", - "copy": "コピー", - "delete": "削除", - "execute": "実行", - "sign-in": "ログイン", - "sign-out": "サインアウト", - "back": "戻る", - "quota": "クォータ" - }, - "conversation": { - "new-chat": "新しいチャット", - "conversation-title": "会話タイトル", - "update": "会話を更新", - "title": "タイトル" - }, - "connection": { - "self": "接続", - "new": "接続を作成", - "edit": "接続を編集", - "select-database": "データベースを選択", - "database-type": "データベースタイプ", - "title": "タイトル", - "host": "ホスト", - "port": "ポート", - "database-name": "データベース名", - "username": "ユーザー名", - "password": "パスワード" - }, - "assistant": { - "self": "ボット", - "create-your-bot": "必要なボットが見つからない場合は、作成してください" - }, - "execution": { - "title": "SQLを実行", - "message": { - "executing": "クエリを実行中...", - "no-connection": "接続が選択されていません", - "no-data": "データが返されませんでした" - } - }, - "editor": { - "placeholder": "ここに質問を入力してください..." - }, - "setting": { - "self": "設定", - "general": "全般的", - "plan": { - "free": "無料版", - "pro": "プロ", - "signup-for-more": "サインアップしてもっと", - "upgrade": "アップグレード" - }, - "billing": { - "self": "請求する" - }, - "account": { - "self": "アカウント" - }, - "basic": { - "self": "基本", - "language": "言語" - }, - "theme": { - "self": "テーマ", - "system": "システムに従う", - "light": "ライト", - "dark": "ダーク" - }, - "openai-api-configuration": { - "self": "OpenAI APIの設定" - }, - "data": { - "self": "データ", - "clear-all-data": "すべてのデータをクリア" - } - }, - "social": { - "join-discord-channel": "Discordチャンネルに参加", - "join-wechat-group": "WeChatグループに参加" - }, - "banner": { - "data-storage": "接続設定はローカルブラウザに保存されます", - "non-select-sql-warning": "現在のステートメントは非SELECT SQLである可能性があり、データベースのスキーマまたはデータの変更につながる可能性があります。自分が何をしているか確認してください。", - "product-hunt": "🚀🚀🚀 Product Huntでローンチしました。投票してください! 🚀🚀🚀" - }, - "payment": { - "self": "支払い" - } -} diff --git a/src/locales/th.json b/src/locales/th.json deleted file mode 100644 index 9dd9abab..00000000 --- a/src/locales/th.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "common": { - "clear": "ล้าง", - "close": "ปิด", - "confirm": "ยืนยัน", - "save": "บันทึก", - "edit": "แก้ไข", - "loading": "กำลังโหลด", - "setting": "ตั้งค่า", - "copy": "คัดลอก", - "delete": "ลบ", - "execute": "ดำเนินการ", - "sign-in": "เข้าสู่ระบบ", - "sign-out": "ออกจากระบบ", - "back": "กลับ", - "quota": "โควต้า" - }, - "conversation": { - "new-chat": "แชทใหม่", - "conversation-title": "หัวข้อการสนทนา", - "update": "อัปเดตการสนทนา", - "title": "หัวข้อ" - }, - "connection": { - "self": "การเชื่อมต่อ", - "new": "สร้างการเชื่อมต่อ", - "edit": "แก้ไขการเชื่อมต่อ", - "select-database": "เลือกฐานข้อมูลของคุณ", - "database-type": "ประเภทฐานข้อมูล", - "title": "หัวข้อ", - "host": "โฮสต์", - "port": "พอร์ต", - "database-name": "ชื่อฐานข้อมูล", - "username": "ชื่อผู้ใช้", - "password": "รหัสผ่าน" - }, - "assistant": { - "self": "บอท", - "create-your-bot": "ไม่พบบอทที่คุณต้องการ? สร้างใหม่" - }, - "execution": { - "title": "ดำเนินการ SQL", - "message": { - "executing": "กำลังดำเนินการคำสั่ง...", - "no-connection": "ไม่ได้เลือกการเชื่อมต่อ", - "no-data": "ไม่มีข้อมูลที่ส่งกลับ" - } - }, - "editor": { - "placeholder": "กรอกคำถามของคุณที่นี่..." - }, - "setting": { - "self": "ตั้งค่า", - "general": "ทั่วไป", - "plan": { - "free": "รุ่นฟรี", - "pro": "รุ่นมืออาชีพ", - "signup-for-more": "ลงทะเบียนเพื่อรับข้อมูลเพิ่มเติม", - "upgrade": "อัปเกรด" - }, - "billing": { - "self": "การเรียกเก็บเงิน" - }, - "account": { - "self": "บัญชี" - }, - "basic": { - "self": "พื้นฐาน", - "language": "ภาษา" - }, - "theme": { - "self": "ธีม", - "system": "ตามระบบ", - "light": "สว่าง", - "dark": "เข้ม" - }, - "openai-api-configuration": { - "self": "การกำหนดค่า OpenAI API" - }, - "data": { - "self": "ข้อมูล", - "clear-all-data": "ลบข้อมูลทั้งหมด" - } - }, - "social": { - "join-discord-channel": "เข้าร่วมช่อง Discord", - "join-wechat-group": "เข้าร่วมกลุ่ม WeChat" - }, - "banner": { - "data-storage": "การตั้งค่าการเชื่อมต่อจะถูกเก็บไว้ในเบราว์เซอร์ของคุณ", - "non-select-sql-warning": "คำสั่งปัจจุบันอาจเป็นคำสั่ง SQL ที่ไม่ใช่ SELECT ซึ่งจะทำให้เกิดการเปลี่ยนแปลงโครงสร้างฐานข้อมูลหรือข้อมูล โปรดตรวจสอบว่าคุณรู้ว่าตัวเองกำลังทำอะไร", - "product-hunt": "🚀🚀🚀 เราเพิ่งเปิดตัวบน Product Hunt โปรดโหวตเรา! 🚀🚀🚀" - }, - "payment": { - "self": "การชำระเงิน" - } -} diff --git a/src/locales/zh-Hant.json b/src/locales/zh-Hant.json deleted file mode 100644 index 925d35fc..00000000 --- a/src/locales/zh-Hant.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "common": { - "clear": "清除", - "close": "關閉", - "confirm": "確認", - "save": "儲存", - "edit": "編輯", - "loading": "載入中", - "setting": "設定", - "copy": "複製", - "delete": "刪除", - "execute": "執行", - "sign-in": "登錄", - "sign-out": "登出", - "back": "返回", - "quota": "額度" - }, - "conversation": { - "new-chat": "新聊天", - "conversation-title": "對話標題", - "update": "更新對話", - "title": "標題" - }, - "connection": { - "self": "連線", - "new": "建立連線", - "edit": "編輯連線", - "select-database": "選擇您的資料庫", - "select-table": "選擇您的資料", - "all-tables":"全部表", - "database-type": "資料庫類型", - "title": "標題", - "host": "主機", - "port": "埠號", - "database-name": "資料庫名稱", - "username": "使用者名稱", - "password": "密碼" - }, - "assistant": { - "self": "機器人", - "create-your-bot": "找不到您需要的機器人?建立一個" - }, - "execution": { - "title": "執行 SQL", - "message": { - "executing": "正在執行查詢...", - "no-connection": "未選擇連線", - "no-data": "未返回任何資料" - } - }, - "editor": { - "placeholder": "在此輸入您的問題..." - }, - "setting": { - "self": "設定", - "general": "通用", - "plan": { - "free": "免費版", - "pro": "專業版", - "signup-for-more": "注册提升额度", - "upgrade": "升級", - "pro-question-per-month": "1000 次提問 / 月", - "early-bird-checkout": "早鳥優惠,5 折購買 1 年" - }, - "billing": { - "self": "计费" - }, - "account": { - "self": "賬戶" - }, - "basic": { - "self": "基本", - "language": "語言" - }, - "theme": { - "self": "主題", - "system": "跟隨系統", - "light": "亮色", - "dark": "暗色" - }, - "openai-api-configuration": { - "self": "OpenAI API 設定" - }, - "data": { - "self": "資料", - "clear-all-data": "清除所有資料" - } - }, - "social": { - "join-discord-channel": "加入 Discord 頻道", - "join-wechat-group": "加入微信群組" - }, - "banner": { - "data-storage": "連線設定儲存在您的本地瀏覽器中", - "non-select-sql-warning": "當前語句可能是非 SELECT SQL 語句,這將導致資料庫結構或資料的更改。請確保您知道自己在做什麼。", - "product-hunt": "🚀🚀🚀 我們剛在 Product Hunt 上推出,請給我們一個投票!🚀🚀🚀", - "quota-overflow": "我們的 OpenAI 額度已用完,請提供您自己的 OpenAI API key。", - "use-my-key": "提供我自己的 key" - }, - "payment": { - "self": "支付", - "login-to-buy": "登陸後購買" - } -} From 691edcca2a7e476ae814b6745574e301c072d58f Mon Sep 17 00:00:00 2001 From: CorrectRoadH Date: Fri, 5 May 2023 23:04:28 +0800 Subject: [PATCH 003/185] chore: delete unused import (#80) --- src/components/ConversationView/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ConversationView/index.tsx b/src/components/ConversationView/index.tsx index af6502ec..58000d5e 100644 --- a/src/components/ConversationView/index.tsx +++ b/src/components/ConversationView/index.tsx @@ -1,5 +1,5 @@ import axios from "axios"; -import { first, head, last } from "lodash-es"; +import { head, last } from "lodash-es"; import { useEffect, useRef, useState } from "react"; import { toast } from "react-hot-toast"; import { API_KEY } from "@/env"; From ab80b675215b95a764b6aa451f3a8a4bd4df3fc6 Mon Sep 17 00:00:00 2001 From: Tianzhou Chen Date: Fri, 5 May 2023 23:17:42 +0800 Subject: [PATCH 004/185] =?UTF-8?q?fix:=20show=20table=20list=20only=20if?= =?UTF-8?q?=20it=E2=80=99s=20bound=20with=20a=20connection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ConnectionSidebar.tsx | 63 ++++++++++++++-------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/components/ConnectionSidebar.tsx b/src/components/ConnectionSidebar.tsx index 61bf1f4b..5f777cfa 100644 --- a/src/components/ConnectionSidebar.tsx +++ b/src/components/ConnectionSidebar.tsx @@ -164,38 +164,39 @@ const ConnectionSidebar = () => { /> )} - {tableSchemaLoadingState.isLoading ? ( -
- {" "} - {t("common.loading")} -
- ) : ( - tableList.length > 0 && ( -
- { + return { + label: + table.name === "" + ? t("connection.all-tables") + : table.name, + value: table.name, + }; + })} + onValueChange={(tableName) => + handleTableNameSelect(tableName) + } + placeholder={t("connection.select-table") || ""} + /> +
+ ) + ))}
From 5bb8a232e2632286e32e2cce9c8faca5e912e918 Mon Sep 17 00:00:00 2001 From: Tianzhou Chen Date: Sat, 6 May 2023 00:07:51 +0800 Subject: [PATCH 005/185] chore: re-arrange addMessage to avoid returning partial error Strange I can only observe this in prod. Not in local env even using `next build && next start` --- src/components/ConversationView/index.tsx | 31 +++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/ConversationView/index.tsx b/src/components/ConversationView/index.tsx index 58000d5e..b0e4e5cf 100644 --- a/src/components/ConversationView/index.tsx +++ b/src/components/ConversationView/index.tsx @@ -150,18 +150,6 @@ const ConversationView = () => { }; messageStore.addMessage(userMessage); - // Add PENDING assistant message to the store. - const assistantMessage: Message = { - id: generateUUID(), - conversationId: currentConversation.id, - creatorId: currentConversation.assistantId, - creatorRole: CreatorRole.Assistant, - createdAt: Date.now(), - content: "", - status: "LOADING", - }; - messageStore.addMessage(assistantMessage); - // Construct the system prompt const messageList = messageStore .getState() @@ -264,13 +252,30 @@ const ConversationView = () => { } catch (error) { // do nth } - messageStore.updateMessage(assistantMessage.id, { + messageStore.addMessage({ + id: generateUUID(), + conversationId: currentConversation.id, + creatorId: currentConversation.assistantId, + creatorRole: CreatorRole.Assistant, + createdAt: Date.now(), content: errorMessage, status: "FAILED", }); return; } + // Add PENDING assistant message to the store. + const assistantMessage: Message = { + id: generateUUID(), + conversationId: currentConversation.id, + creatorId: currentConversation.assistantId, + creatorRole: CreatorRole.Assistant, + createdAt: Date.now(), + content: "", + status: "LOADING", + }; + messageStore.addMessage(assistantMessage); + const data = rawRes.body; if (!data) { toast.error("No data return"); From 8e3d220d0d26d3e0225ff16471d83d9e27f519eb Mon Sep 17 00:00:00 2001 From: Tianzhou Chen Date: Sat, 6 May 2023 01:44:55 +0800 Subject: [PATCH 006/185] chore: update doc to make DATABASE_URL required --- .env.example | 1 + README.md | 53 ++++++++++++++++++++++++++-------------------------- process.d.ts | 5 +++-- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/.env.example b/.env.example index ca9ba6e9..2840ecd4 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ # see process.d.ts +DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@localhost:5432/sqlchat?schema=sqlchat OPENAI_API_KEY=YOUR_OPENAI_API_KEY diff --git a/README.md b/README.md index d4f3bb80..1f128715 100644 --- a/README.md +++ b/README.md @@ -61,45 +61,46 @@ docker run --name sqlchat --platform linux/amd64 --env OPENAI_API_KEY=xxx --env ## Local Development -1. Make a copy of the example environment variables file; +1. Make a copy of the example environment variables file: - ```bash - cp .env.example .env - ``` + ```bash + cp .env.example .env + ``` -2. Add your [API key](https://platform.openai.com/account/api-keys) and OpenAI API Endpoint(optional) to the newly created `.env` file; +1. Add your [API key](https://platform.openai.com/account/api-keys) and OpenAI API Endpoint(optional) to the newly created `.env` file. -3. Install dependencies and start the dev server; +1. Start a Postgres instance. For mac, you can use [StackbBricks](https://stackbricks.app/), [DBngin](https://dbngin.com/) or [Postgres.app](https://postgresapp.com/). Create a database: - ```bash - pnpm i && pnpm dev - ``` - -### Database Setup + ```sql + CREATE DATABASE sqlchat; + ``` + In `.env` file, assign the connection string to `DATABASE_URL`. 1. Install dependencies - ```bash - pnpm i - ``` + ```bash + pnpm i + ``` + +1. Generate schema -1. Generate prisma client from the model + 1. Generate prisma client from the model - ```bash - pnpm prisma generate - ``` + ```bash + pnpm prisma generate + ``` -1. Migrate schema + 1. Migrate schema - ```bash - pnpm prisma migrate dev - ``` + ```bash + pnpm prisma migrate dev + ``` -1. Seed data + 1. (Optional) Seed data - ```bash - pnpm prisma db seed - ``` + ```bash + pnpm prisma db seed + ``` ## Star History diff --git a/process.d.ts b/process.d.ts index 5431bdba..81b50ada 100644 --- a/process.d.ts +++ b/process.d.ts @@ -2,12 +2,13 @@ declare namespace NodeJS { export interface ProcessEnv { // Required. Node environment. NODE_ENV: string; + // Required. Postgres database connection string to store the data. + // e.g. postgresql://postgres:YOUR_PASSWORD@localhost:5432/sqlchat?schema=sqlchat + DATABASE_URL: string; // Required. Do not share your OpenAI API key with anyone! It should remain a secret. OPENAI_API_KEY: string; // Optional. OpenAI API endpoint. Defaults to https://api.openai.com. OPENAI_API_ENDPOINT: string; - // Optional. Database connection string to store the data. - DATABASE_URL: string; // Optional. API key to protect the backend API endpoint. // This needs to be exposed to the frontend and must be prefixed with NEXT_PUBLIC_. NEXT_PUBLIC_API_KEY: string; From cfbdf5cced4c54a1c92e8161c43c28c3592e8278 Mon Sep 17 00:00:00 2001 From: Tianzhou Chen Date: Sat, 6 May 2023 01:44:55 +0800 Subject: [PATCH 007/185] chore: update doc to make DATABASE_URL required --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f128715..e277b66c 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,13 @@ to the database whitelist IP. Because sqlchat.AI is hosted on [Vercel](https://v docker run --name sqlchat --platform linux/amd64 -p 3000:3000 sqlchat/sqlchat ``` -You can set the following environment variables to customize the deployment: +Required environment variables: +- `DATABASE_URL`: Postgres connection string to store data. e.g. postgresql://postgres:YOUR_PASSWORD@localhost:5432/sqlchat?schema=sqlchat. - `OPENAI_API_KEY`: OpenAI API key. You can get one from [here](https://beta.openai.com/docs/developer-quickstart/api-keys). + +Optional environment variables: + - `OPENAI_API_ENDPOINT`: OpenAI API endpoint. Defaults to `https://api.openai.com`. ```bash From 698eff0978b31b4dc44fddea794c6261a4fc1a7d Mon Sep 17 00:00:00 2001 From: EvanLong <52912949+evanlong0926@users.noreply.github.com> Date: Sat, 6 May 2023 22:41:16 +0800 Subject: [PATCH 008/185] chore: fix document target jump (#81) --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index eb6c8f60..0db49526 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -5,7 +5,7 @@ vercel -

English | 中文

+

English | 中文

## 介绍 From cd265a5c474638b20899313febbc799b5013beb8 Mon Sep 17 00:00:00 2001 From: boojack Date: Mon, 8 May 2023 23:29:33 +0800 Subject: [PATCH 009/185] chore: add dark mode styles to setting page (#83) --- src/components/PricingView.tsx | 17 +++------ src/components/SettingView.tsx | 4 +-- src/pages/setting/index.tsx | 65 +++++++++------------------------- 3 files changed, 23 insertions(+), 63 deletions(-) diff --git a/src/components/PricingView.tsx b/src/components/PricingView.tsx index 2e5f41b7..27449617 100644 --- a/src/components/PricingView.tsx +++ b/src/components/PricingView.tsx @@ -4,12 +4,7 @@ import { useTranslation } from "react-i18next"; import getStripe from "../utils/get-stripejs"; import { fetchPostJSON } from "../utils/api-helpers"; -const includedFeatures = [ - "Private forum access", - "Member resources", - "Entry to annual conference", - "Official member t-shirt", -]; +const includedFeatures = ["Private forum access", "Member resources", "Entry to annual conference", "Official member t-shirt"]; const checkout = async () => { // Create a Checkout Session. @@ -39,19 +34,15 @@ const PricingView = () => { const { data: session, status } = useSession(); return ( -
+
-

- {t("setting.plan.pro-question-per-month")} -

+

{t("setting.plan.pro-question-per-month")}

diff --git a/src/components/SettingView.tsx b/src/components/SettingView.tsx index 04e483ad..fa4bcc6d 100644 --- a/src/components/SettingView.tsx +++ b/src/components/SettingView.tsx @@ -1,5 +1,6 @@ import React from "react"; import { useTranslation } from "react-i18next"; +import { HasFeature } from "../utils"; import Icon from "./Icon"; import AccountView from "./AccountView"; import PricingView from "./PricingView"; @@ -8,13 +9,12 @@ import ClearDataButton from "./ClearDataButton"; import LocaleSelector from "./LocaleSelector"; import ThemeSelector from "./ThemeSelector"; import OpenAIApiConfigView from "./OpenAIApiConfigView"; -import { HasFeature } from "../utils"; const SettingView = () => { const { t } = useTranslation(); return ( -
+
{ ``` */} -
+
- + { leaveTo="opacity-0" >
-
{/* Sidebar component, swap this element with another sidebar if you like */} -
+
@@ -116,17 +99,13 @@ const SettingPage: NextPage = () => {
{isEditing && ( diff --git a/src/components/EngineIcon.tsx b/src/components/EngineIcon.tsx index 5c031d31..d0390488 100644 --- a/src/components/EngineIcon.tsx +++ b/src/components/EngineIcon.tsx @@ -15,6 +15,8 @@ const EngineIcon = (props: Props) => { return ; } else if (engine === Engine.MSSQL) { return ; + } else if (engine === Engine.TiDBServerless) { + return ; } else { return ; } diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 5b2bb892..62ff4521 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -6,6 +6,12 @@ import * as Fi from "react-icons/fi"; import * as Gi from "react-icons/gi"; import * as Io from "react-icons/io"; import * as Io5 from "react-icons/io5"; +import {IconType, IconBaseProps} from "react-icons/lib"; +import {ReactSVG} from "react-svg"; + +const TiDBCloudIcon: IconType = (props: IconBaseProps) => ( + +); const Icon = { ...Ai, @@ -16,6 +22,7 @@ const Icon = { ...Gi, ...Io, ...Io5, + TiDBCloudIcon }; // Icon is a collection of all icons from react-icons. diff --git a/src/locales/en.json b/src/locales/en.json index b51aa073..874d90f2 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -34,7 +34,8 @@ "port": "Port", "database-name": "Database name", "username": "Username", - "password": "Password" + "password": "Password", + "tidb-serverless-ssl-hint": "SSL is required and configured" }, "assistant": { "self": "Bot", diff --git a/src/locales/es.json b/src/locales/es.json index f59fe1b9..9469a8c1 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -32,7 +32,8 @@ "port": "Puerto", "database-name": "Nombre de Base de Datos", "username": "Usuario", - "password": "Contraseña" + "password": "Contraseña", + "tidb-serverless-ssl-hint": "Se requiere SSL y ya está configurado" }, "assistant": { "self": "Bot", diff --git a/src/locales/zh.json b/src/locales/zh.json index 85220764..da71abf2 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -34,7 +34,8 @@ "port": "端口", "database-name": "数据库", "username": "用户名", - "password": "密码" + "password": "密码", + "tidb-serverless-ssl-hint": "必需SSL且已配置" }, "assistant": { "self": "机器人", diff --git a/src/pages/api/connection/db.ts b/src/pages/api/connection/db.ts index 84c5ee44..d744dfa0 100644 --- a/src/pages/api/connection/db.ts +++ b/src/pages/api/connection/db.ts @@ -1,6 +1,8 @@ import { NextApiRequest, NextApiResponse } from "next"; import { newConnector } from "@/lib/connectors"; import { Connection } from "@/types"; +import { changeTiDBConnectionToMySQL } from "@/utils"; +import { Engine } from "@/types/connection"; // POST /api/connection/db // req body: { connection: Connection } @@ -10,7 +12,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(405).json([]); } - const connection = req.body.connection as Connection; + let connection = req.body.connection as Connection; + if (connection.engineType === Engine.TiDBServerless) { + connection = changeTiDBConnectionToMySQL(connection); + } + try { const connector = newConnector(connection); const databaseNameList = await connector.getDatabases(); diff --git a/src/pages/api/connection/db_schema.ts b/src/pages/api/connection/db_schema.ts index 1c2388df..85644d88 100644 --- a/src/pages/api/connection/db_schema.ts +++ b/src/pages/api/connection/db_schema.ts @@ -1,6 +1,8 @@ import { NextApiRequest, NextApiResponse } from "next"; import { newConnector } from "@/lib/connectors"; import { Connection, Table } from "@/types"; +import { changeTiDBConnectionToMySQL } from "@/utils"; +import { Engine } from "@/types/connection"; // POST /api/connection/db_schema // req body: { connection: Connection, db: string } @@ -10,7 +12,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(405).json([]); } - const connection = req.body.connection as Connection; + let connection = req.body.connection as Connection; + if (connection.engineType === Engine.TiDBServerless) { + connection = changeTiDBConnectionToMySQL(connection); + } + const db = req.body.db as string; try { const connector = newConnector(connection); diff --git a/src/pages/api/connection/execute.ts b/src/pages/api/connection/execute.ts index 6ff613fe..e7e7099a 100644 --- a/src/pages/api/connection/execute.ts +++ b/src/pages/api/connection/execute.ts @@ -1,6 +1,8 @@ import { NextApiRequest, NextApiResponse } from "next"; import { newConnector } from "@/lib/connectors"; import { Connection } from "@/types"; +import { changeTiDBConnectionToMySQL } from "@/utils"; +import { Engine } from "@/types/connection"; // POST /api/connection/execute // req body: { connection: Connection, db: string, statement: string } @@ -9,7 +11,10 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(405).json(false); } - const connection = req.body.connection as Connection; + let connection = req.body.connection as Connection; + if (connection.engineType === Engine.TiDBServerless) { + connection = changeTiDBConnectionToMySQL(connection); + } const db = req.body.db as string; const statement = req.body.statement as string; diff --git a/src/pages/api/connection/test.ts b/src/pages/api/connection/test.ts index d9ec669c..8726d464 100644 --- a/src/pages/api/connection/test.ts +++ b/src/pages/api/connection/test.ts @@ -1,6 +1,8 @@ import { NextApiRequest, NextApiResponse } from "next"; import { newConnector } from "@/lib/connectors"; import { Connection } from "@/types"; +import { changeTiDBConnectionToMySQL } from "@/utils"; +import { Engine } from "@/types/connection"; // POST /api/connection/test // req body: { connection: Connection } @@ -10,7 +12,10 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(405).json(false); } - const connection = req.body.connection as Connection; + let connection = req.body.connection as Connection; + if (connection.engineType === Engine.TiDBServerless) { + connection = changeTiDBConnectionToMySQL(connection); + } try { const connector = newConnector(connection); await connector.testConnection(); diff --git a/src/types/connection.ts b/src/types/connection.ts index 522eba5f..14807c78 100644 --- a/src/types/connection.ts +++ b/src/types/connection.ts @@ -4,12 +4,15 @@ export enum Engine { MySQL = "MYSQL", PostgreSQL = "POSTGRESQL", MSSQL = "MSSQL", + TiDBServerless = "TiDBServerless", } export interface SSLOptions { ca?: string; cert?: string; key?: string; + minVersion?: string; + rejectUnauthorized?: boolean; } export interface Connection { diff --git a/src/utils/index.ts b/src/utils/index.ts index 9d730bfb..285d6586 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,3 +4,4 @@ export * from "./sql"; export * from "./execution"; export * from "./model"; export * from "./feature"; +export * from "./tidb"; \ No newline at end of file diff --git a/src/utils/tidb.ts b/src/utils/tidb.ts new file mode 100644 index 00000000..3a035854 --- /dev/null +++ b/src/utils/tidb.ts @@ -0,0 +1,16 @@ +import { Connection } from "@/types"; +import { Engine } from "@/types/connection"; + +export const changeTiDBConnectionToMySQL = (connection: Connection) => { + if (connection.engineType === Engine.TiDBServerless) { + return { + ...connection, + engineType: Engine.MySQL, + ssl: { + minVersion: 'TLSv1.2', + rejectUnauthorized: true + } + }; + } + return connection; +} \ No newline at end of file From d423ce7a062e4358c82dd702da5a80b7dd9f5080 Mon Sep 17 00:00:00 2001 From: tianzhou Date: Fri, 12 May 2023 11:43:35 +0800 Subject: [PATCH 014/185] fix: correct response message --- src/pages/api/chat.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/pages/api/chat.ts b/src/pages/api/chat.ts index 9d22e283..e6724518 100644 --- a/src/pages/api/chat.ts +++ b/src/pages/api/chat.ts @@ -1,4 +1,8 @@ -import { createParser, ParsedEvent, ReconnectInterval } from "eventsource-parser"; +import { + createParser, + ParsedEvent, + ReconnectInterval, +} from "eventsource-parser"; import { NextRequest } from "next/server"; import { API_KEY } from "@/env"; import { openAIApiEndpoint, openAIApiKey, gpt35 } from "@/utils"; @@ -17,9 +21,19 @@ const handler = async (req: NextRequest) => { if (API_KEY) { const auth = req.headers.get("Authorization"); if (!auth || auth !== `Bearer ${API_KEY}`) { - return new Response("Unauthorized", { - status: 401, - }); + return new Response( + JSON.stringify({ + error: { + message: "Unauthorized.", + }, + }), + { + headers: { + "Content-Type": "application/json", + }, + status: 401, + } + ); } } @@ -31,7 +45,8 @@ const handler = async (req: NextRequest) => { return new Response( JSON.stringify({ error: { - message: "OpenAI API Key is missing. You can supply your own key via Settings.", + message: + "OpenAI API Key is missing. You can supply your own key via Settings.", }, }), { @@ -43,7 +58,9 @@ const handler = async (req: NextRequest) => { ); } - const apiEndpoint = getApiEndpoint(openAIApiConfig?.endpoint || openAIApiEndpoint); + const apiEndpoint = getApiEndpoint( + openAIApiConfig?.endpoint || openAIApiEndpoint + ); const res = await fetch(apiEndpoint, { headers: { "Content-Type": "application/json", From 79d02af91cd60d056ca5f18bf8d1f401174ad29f Mon Sep 17 00:00:00 2001 From: CorrectRoadH Date: Sat, 13 May 2023 15:01:42 +0800 Subject: [PATCH 015/185] feat: support multiple select table (#82) --- package.json | 1 + pnpm-lock.yaml | 345 ++++++++++++---------- src/components/ConnectionSidebar.tsx | 58 ++-- src/components/ConversationView/index.tsx | 24 +- src/components/kit/MultipleSelect.tsx | 66 +++++ src/locales/en.json | 4 +- src/locales/es.json | 2 + src/locales/zh.json | 4 +- src/store/conversation.ts | 6 +- src/types/conversation.ts | 2 +- tailwind.config.js | 5 +- 11 files changed, 331 insertions(+), 186 deletions(-) create mode 100644 src/components/kit/MultipleSelect.tsx diff --git a/package.json b/package.json index b9b60c95..3180798f 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "stripe": "^12.3.0", "styled-components": "^5.3.9", "swr": "^2.1.5", + "tailwind-scrollbar-hide": "^1.1.7", "uuid": "^9.0.0", "zustand": "^4.3.6" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91a57c3c..a900b18d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,6 +139,9 @@ dependencies: swr: specifier: ^2.1.5 version: 2.1.5(react@18.2.0) + tailwind-scrollbar-hide: + specifier: ^1.1.7 + version: registry.npmmirror.com/tailwind-scrollbar-hide@1.1.7 uuid: specifier: ^9.0.0 version: 9.0.0 @@ -965,123 +968,6 @@ packages: glob: 7.1.7 dev: true - /@next/swc-android-arm-eabi@13.2.4: - resolution: {integrity: sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==} - engines: {node: '>= 10'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: false - optional: true - - /@next/swc-android-arm64@13.2.4: - resolution: {integrity: sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: false - optional: true - - /@next/swc-darwin-arm64@13.2.4: - resolution: {integrity: sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@next/swc-darwin-x64@13.2.4: - resolution: {integrity: sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@next/swc-freebsd-x64@13.2.4: - resolution: {integrity: sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm-gnueabihf@13.2.4: - resolution: {integrity: sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm64-gnu@13.2.4: - resolution: {integrity: sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm64-musl@13.2.4: - resolution: {integrity: sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-x64-gnu@13.2.4: - resolution: {integrity: sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-x64-musl@13.2.4: - resolution: {integrity: sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-arm64-msvc@13.2.4: - resolution: {integrity: sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-ia32-msvc@13.2.4: - resolution: {integrity: sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-x64-msvc@13.2.4: - resolution: {integrity: sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2206,7 +2092,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: registry.npmmirror.com/fsevents@2.3.2 dev: true /client-only@0.0.1: @@ -3065,14 +2951,6 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true - optional: true - /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} @@ -4356,19 +4234,19 @@ packages: react-dom: 18.2.0(react@18.2.0) styled-jsx: 5.1.1(react@18.2.0) optionalDependencies: - '@next/swc-android-arm-eabi': 13.2.4 - '@next/swc-android-arm64': 13.2.4 - '@next/swc-darwin-arm64': 13.2.4 - '@next/swc-darwin-x64': 13.2.4 - '@next/swc-freebsd-x64': 13.2.4 - '@next/swc-linux-arm-gnueabihf': 13.2.4 - '@next/swc-linux-arm64-gnu': 13.2.4 - '@next/swc-linux-arm64-musl': 13.2.4 - '@next/swc-linux-x64-gnu': 13.2.4 - '@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 + '@next/swc-android-arm-eabi': registry.npmmirror.com/@next/swc-android-arm-eabi@13.2.4 + '@next/swc-android-arm64': registry.npmmirror.com/@next/swc-android-arm64@13.2.4 + '@next/swc-darwin-arm64': registry.npmmirror.com/@next/swc-darwin-arm64@13.2.4 + '@next/swc-darwin-x64': registry.npmmirror.com/@next/swc-darwin-x64@13.2.4 + '@next/swc-freebsd-x64': registry.npmmirror.com/@next/swc-freebsd-x64@13.2.4 + '@next/swc-linux-arm-gnueabihf': registry.npmmirror.com/@next/swc-linux-arm-gnueabihf@13.2.4 + '@next/swc-linux-arm64-gnu': registry.npmmirror.com/@next/swc-linux-arm64-gnu@13.2.4 + '@next/swc-linux-arm64-musl': registry.npmmirror.com/@next/swc-linux-arm64-musl@13.2.4 + '@next/swc-linux-x64-gnu': registry.npmmirror.com/@next/swc-linux-x64-gnu@13.2.4 + '@next/swc-linux-x64-musl': registry.npmmirror.com/@next/swc-linux-x64-musl@13.2.4 + '@next/swc-win32-arm64-msvc': registry.npmmirror.com/@next/swc-win32-arm64-msvc@13.2.4 + '@next/swc-win32-ia32-msvc': registry.npmmirror.com/@next/swc-win32-ia32-msvc@13.2.4 + '@next/swc-win32-x64-msvc': registry.npmmirror.com/@next/swc-win32-x64-msvc@13.2.4 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -6032,7 +5910,7 @@ packages: dev: false registry.npmmirror.com/@babel/runtime@7.21.0: - resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz} + resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz} name: '@babel/runtime' version: 7.21.0 engines: {node: '>=6.9.0'} @@ -6040,8 +5918,155 @@ packages: regenerator-runtime: registry.npmmirror.com/regenerator-runtime@0.13.11 dev: false + registry.npmmirror.com/@next/swc-android-arm-eabi@13.2.4: + resolution: {integrity: sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz} + name: '@next/swc-android-arm-eabi' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-android-arm64@13.2.4: + resolution: {integrity: sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz} + name: '@next/swc-android-arm64' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-darwin-arm64@13.2.4: + resolution: {integrity: sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.4.tgz} + name: '@next/swc-darwin-arm64' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-darwin-x64@13.2.4: + resolution: {integrity: sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.4.tgz} + name: '@next/swc-darwin-x64' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-freebsd-x64@13.2.4: + resolution: {integrity: sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz} + name: '@next/swc-freebsd-x64' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-linux-arm-gnueabihf@13.2.4: + resolution: {integrity: sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz} + name: '@next/swc-linux-arm-gnueabihf' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-linux-arm64-gnu@13.2.4: + resolution: {integrity: sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.4.tgz} + name: '@next/swc-linux-arm64-gnu' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-linux-arm64-musl@13.2.4: + resolution: {integrity: sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.4.tgz} + name: '@next/swc-linux-arm64-musl' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-linux-x64-gnu@13.2.4: + resolution: {integrity: sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.4.tgz} + name: '@next/swc-linux-x64-gnu' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-linux-x64-musl@13.2.4: + resolution: {integrity: sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.4.tgz} + name: '@next/swc-linux-x64-musl' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-win32-arm64-msvc@13.2.4: + resolution: {integrity: sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.4.tgz} + name: '@next/swc-win32-arm64-msvc' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-win32-ia32-msvc@13.2.4: + resolution: {integrity: sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.4.tgz} + name: '@next/swc-win32-ia32-msvc' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + registry.npmmirror.com/@next/swc-win32-x64-msvc@13.2.4: + resolution: {integrity: sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.4.tgz} + name: '@next/swc-win32-x64-msvc' + version: 13.2.4 + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + registry.npmmirror.com/@radix-ui/number@1.0.0: - resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/number/-/number-1.0.0.tgz} + resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/number/-/number-1.0.0.tgz} name: '@radix-ui/number' version: 1.0.0 dependencies: @@ -6049,7 +6074,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/primitive@1.0.0: - resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.0.0.tgz} + resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.0.0.tgz} name: '@radix-ui/primitive' version: 1.0.0 dependencies: @@ -6057,7 +6082,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-compose-refs@1.0.0(react@18.2.0): - resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz} + resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz} id: registry.npmmirror.com/@radix-ui/react-compose-refs/1.0.0 name: '@radix-ui/react-compose-refs' version: 1.0.0 @@ -6069,7 +6094,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-context@1.0.0(react@18.2.0): - resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.0.0.tgz} + resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.0.0.tgz} id: registry.npmmirror.com/@radix-ui/react-context/1.0.0 name: '@radix-ui/react-context' version: 1.0.0 @@ -6081,7 +6106,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-direction@1.0.0(react@18.2.0): - resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-direction/-/react-direction-1.0.0.tgz} + resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-direction/-/react-direction-1.0.0.tgz} id: registry.npmmirror.com/@radix-ui/react-direction/1.0.0 name: '@radix-ui/react-direction' version: 1.0.0 @@ -6093,7 +6118,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz} + resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz} id: registry.npmmirror.com/@radix-ui/react-presence/1.0.0 name: '@radix-ui/react-presence' version: 1.0.0 @@ -6109,7 +6134,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-primitive@1.0.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz} + resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz} id: registry.npmmirror.com/@radix-ui/react-primitive/1.0.2 name: '@radix-ui/react-primitive' version: 1.0.2 @@ -6124,7 +6149,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-scroll-area@1.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sBX9j8Q+0/jReNObEAveKIGXJtk3xUoSIx4cMKygGtO128QJyVDn01XNOFsyvihKDCTcu7SINzQ2jPAZEhIQtw==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.0.3.tgz} + resolution: {integrity: sha512-sBX9j8Q+0/jReNObEAveKIGXJtk3xUoSIx4cMKygGtO128QJyVDn01XNOFsyvihKDCTcu7SINzQ2jPAZEhIQtw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.0.3.tgz} id: registry.npmmirror.com/@radix-ui/react-scroll-area/1.0.3 name: '@radix-ui/react-scroll-area' version: 1.0.3 @@ -6147,7 +6172,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-slot@1.0.1(react@18.2.0): - resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.0.1.tgz} + resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.0.1.tgz} id: registry.npmmirror.com/@radix-ui/react-slot/1.0.1 name: '@radix-ui/react-slot' version: 1.0.1 @@ -6160,7 +6185,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0): - resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz} + resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz} id: registry.npmmirror.com/@radix-ui/react-use-callback-ref/1.0.0 name: '@radix-ui/react-use-callback-ref' version: 1.0.0 @@ -6172,7 +6197,7 @@ packages: dev: false registry.npmmirror.com/@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0): - resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz} + resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz} id: registry.npmmirror.com/@radix-ui/react-use-layout-effect/1.0.0 name: '@radix-ui/react-use-layout-effect' version: 1.0.0 @@ -6183,8 +6208,24 @@ packages: react: 18.2.0 dev: false + registry.npmmirror.com/fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz} + name: fsevents + version: 2.3.2 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + registry.npmmirror.com/regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz} + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz} name: regenerator-runtime version: 0.13.11 dev: false + + registry.npmmirror.com/tailwind-scrollbar-hide@1.1.7: + resolution: {integrity: sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tailwind-scrollbar-hide/-/tailwind-scrollbar-hide-1.1.7.tgz} + name: tailwind-scrollbar-hide + version: 1.1.7 + dev: false diff --git a/src/components/ConnectionSidebar.tsx b/src/components/ConnectionSidebar.tsx index 5f777cfa..bb3b5ca5 100644 --- a/src/components/ConnectionSidebar.tsx +++ b/src/components/ConnectionSidebar.tsx @@ -16,6 +16,7 @@ import ConversationList from "./Sidebar/ConversationList"; import ConnectionList from "./Sidebar/ConnectionList"; import QuotaWidget from "./QuotaWidget"; import { HasFeature } from "../utils"; +import MultipleSelect from "./kit/MultipleSelect"; interface State {} @@ -31,6 +32,10 @@ const ConnectionSidebar = () => { (database) => database.connectionId === currentConnectionCtx?.connection.id ); const [tableList, updateTableList] = useState([]); + const selectedTablesName: string[] = + conversationStore.getConversationById( + conversationStore.currentConversationId + )?.selectedTablesName || []; const tableSchemaLoadingState = useLoading(); useEffect(() => { @@ -84,13 +89,7 @@ const ConnectionSidebar = () => { database.name === currentConnectionCtx?.database?.name )?.tableList || []; - updateTableList([ - { - name: "", - structure: "", - } as Table, - ...tableList, - ]); + updateTableList(tableList); }, [connectionStore, currentConnectionCtx]); const handleDatabaseNameSelect = async (databaseName: string) => { @@ -116,8 +115,18 @@ const ConnectionSidebar = () => { } }; - const handleTableNameSelect = async (tableName: string) => { - conversationStore.updateTableName(tableName); + const handleTableNameSelect = async (selectedTablesName: string[]) => { + conversationStore.updateSelectedTablesName(selectedTablesName); + }; + + const handleAllSelect = async () => { + conversationStore.updateSelectedTablesName( + tableList.map((table) => table.name) + ); + }; + + const handleEmptySelect = async () => { + conversationStore.updateSelectedTablesName([]); }; return ( <> @@ -173,13 +182,9 @@ const ConnectionSidebar = () => { ) : ( tableList.length > 0 && (
- { { value: Engine.MSSQL, label: "MSSQL" }, { value: Engine.TiDBServerless, label: "TiDB Serverless Tier" }, ]} - onValueChange={(value) => - setPartialConnection({ engineType: value as Engine }) - } + onValueChange={(value) => setPartialConnection({ engineType: value as Engine })} />
- - setPartialConnection({ title: value })} - /> + + setPartialConnection({ title: value })} />
- - setPartialConnection({ host: value })} - /> + + setPartialConnection({ host: value })} />
- - setPartialConnection({ port: value })} - /> + + setPartialConnection({ port: value })} />
{showDatabaseField && (
- + {
)}
- + { />
- + {
{connection.engineType === Engine.TiDBServerless ? (
- +
) : (
- +
{SSLTypeOptions.map((option) => ( -
- +
@@ -433,10 +365,7 @@ const CreateConnectionModal = (props: Props) => {
{isEditing && ( - )} @@ -445,14 +374,8 @@ const CreateConnectionModal = (props: Props) => { -
diff --git a/src/components/DataStorageBanner.tsx b/src/components/DataStorageBanner.tsx index af8e3775..16288310 100644 --- a/src/components/DataStorageBanner.tsx +++ b/src/components/DataStorageBanner.tsx @@ -10,10 +10,7 @@ interface Props { const DataStorageBanner = (props: Props) => { const { className, alwaysShow } = props; const { t } = useTranslation(); - const [hideBanner, setHideBanner] = useLocalStorage( - "hide-local-storage-banner", - false - ); + const [hideBanner, setHideBanner] = useLocalStorage("hide-local-storage-banner", false); const show = alwaysShow || !hideBanner; return ( @@ -27,10 +24,7 @@ const DataStorageBanner = (props: Props) => { {t("banner.data-storage")} {!alwaysShow && ( - )} diff --git a/src/components/EmptyView.tsx b/src/components/EmptyView.tsx index e6ec95f4..7a50154a 100644 --- a/src/components/EmptyView.tsx +++ b/src/components/EmptyView.tsx @@ -1,18 +1,9 @@ -import { - useConversationStore, - useConnectionStore, - useMessageStore, -} from "@/store"; -import { CreatorRole } from "@/types"; -import { generateUUID } from "@/utils"; +import { useConversationStore, useConnectionStore, useMessageStore } from "@/store"; import useDarkMode from "@/hooks/useDarkmode"; import Icon from "./Icon"; // examples are used to show some examples to the user. -const examples = [ - "Give me an example schema about employee", - "How to create a view in MySQL?", -]; +const examples = ["Give me an example schema about employee", "How to create a view in MySQL?"]; interface Props { className?: string; @@ -27,38 +18,22 @@ const EmptyView = (props: Props) => { const isDarkMode = useDarkMode(); const handleExampleClick = async (content: string) => { - let conversation = conversationStore.getConversationById( - conversationStore.currentConversationId - ); + let conversation = conversationStore.getConversationById(conversationStore.currentConversationId); if (!conversation) { const currentConnectionCtx = connectionStore.currentConnectionCtx; if (!currentConnectionCtx) { conversation = conversationStore.createConversation(); } else { - conversation = conversationStore.createConversation( - currentConnectionCtx.connection.id, - currentConnectionCtx.database?.name - ); + conversation = conversationStore.createConversation(currentConnectionCtx.connection.id, currentConnectionCtx.database?.name); } } await sendMessage(content); }; return ( -
+
- sql-chat-logo + sql-chat-logo
diff --git a/src/components/EngineIcon.tsx b/src/components/EngineIcon.tsx index d0390488..ed3b6c0d 100644 --- a/src/components/EngineIcon.tsx +++ b/src/components/EngineIcon.tsx @@ -16,7 +16,7 @@ const EngineIcon = (props: Props) => { } else if (engine === Engine.MSSQL) { return ; } else if (engine === Engine.TiDBServerless) { - return ; + return ; } else { return ; } diff --git a/src/components/ExecutionView/DataTableView.tsx b/src/components/ExecutionView/DataTableView.tsx index 754cd653..4e5f6c14 100644 --- a/src/components/ExecutionView/DataTableView.tsx +++ b/src/components/ExecutionView/DataTableView.tsx @@ -22,9 +22,7 @@ const DataTableView = (props: Props) => { return rawResults.length === 0 ? (
- - {t("execution.message.no-data")} - + {t("execution.message.no-data")}
) : ( { const { t } = useTranslation(); return ( -
+
{t("banner.non-select-sql-warning")} diff --git a/src/components/ExecutionView/NotificationView.tsx b/src/components/ExecutionView/NotificationView.tsx index c2e58dbd..b1c28539 100644 --- a/src/components/ExecutionView/NotificationView.tsx +++ b/src/components/ExecutionView/NotificationView.tsx @@ -6,13 +6,7 @@ interface Props { const NotificationView = (props: Props) => { const { message, style } = props; const additionalStyle = style === "error" ? "text-red-500" : "text-gray-500"; - return ( -

- {message} -

- ); + return

{message}

; }; export default NotificationView; diff --git a/src/components/GitHubStarBadge.tsx b/src/components/GitHubStarBadge.tsx index 11f57d4c..f641938b 100644 --- a/src/components/GitHubStarBadge.tsx +++ b/src/components/GitHubStarBadge.tsx @@ -15,15 +15,12 @@ const GitHubStarBadge = (props: Props) => { const getRepoStarCount = async () => { let starCount = 0; try { - const { data } = await axios.get( - `https://api.github.com/repos/sqlchat/sqlchat`, - { - headers: { - Accept: "application/vnd.github.v3.star+json", - Authorization: "", - }, - } - ); + const { data } = await axios.get(`https://api.github.com/repos/sqlchat/sqlchat`, { + headers: { + Accept: "application/vnd.github.v3.star+json", + Authorization: "", + }, + }); starCount = data.stargazers_count as number; } catch (error) { // do nth @@ -50,11 +47,7 @@ const GitHubStarBadge = (props: Props) => { Star
- {isRequesting ? ( - - ) : ( - stars - )} + {isRequesting ? : stars}
); diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 62ff4521..a1b0792d 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -6,12 +6,10 @@ import * as Fi from "react-icons/fi"; import * as Gi from "react-icons/gi"; import * as Io from "react-icons/io"; import * as Io5 from "react-icons/io5"; -import {IconType, IconBaseProps} from "react-icons/lib"; -import {ReactSVG} from "react-svg"; +import { IconType, IconBaseProps } from "react-icons/lib"; +import { ReactSVG } from "react-svg"; -const TiDBCloudIcon: IconType = (props: IconBaseProps) => ( - -); +const TiDBCloudIcon: IconType = (props: IconBaseProps) => ; const Icon = { ...Ai, @@ -22,7 +20,7 @@ const Icon = { ...Gi, ...Io, ...Io5, - TiDBCloudIcon + TiDBCloudIcon, }; // Icon is a collection of all icons from react-icons. diff --git a/src/components/LocaleSelector.tsx b/src/components/LocaleSelector.tsx index 0e3b65fc..05c627d5 100644 --- a/src/components/LocaleSelector.tsx +++ b/src/components/LocaleSelector.tsx @@ -30,14 +30,7 @@ const LocaleSelector = () => { settingStore.setLocale(locale); }; - return ( - ; }; export default LocaleSelector; diff --git a/src/components/OpenAIApiConfigView.tsx b/src/components/OpenAIApiConfigView.tsx index 8ec45530..9c875a9e 100644 --- a/src/components/OpenAIApiConfigView.tsx +++ b/src/components/OpenAIApiConfigView.tsx @@ -4,14 +4,11 @@ import { useDebounce } from "react-use"; import { useSettingStore } from "@/store"; import { OpenAIApiConfig } from "@/types"; import TextField from "./kit/TextField"; -import { set } from "lodash-es"; const OpenAIApiConfigView = () => { const { t } = useTranslation(); const settingStore = useSettingStore(); - const [openAIApiConfig, setOpenAIApiConfig] = useState( - settingStore.setting.openAIApiConfig - ); + const [openAIApiConfig, setOpenAIApiConfig] = useState(settingStore.setting.openAIApiConfig); const [maskKey, setMaskKey] = useState(true); const maskedKey = (str: string) => { @@ -47,9 +44,7 @@ const OpenAIApiConfigView = () => { handleSetOpenAIApiConfig({ key: value })} />
diff --git a/src/components/PricingView.tsx b/src/components/PricingView.tsx index 27ec3be9..f41b1ea0 100644 --- a/src/components/PricingView.tsx +++ b/src/components/PricingView.tsx @@ -47,9 +47,7 @@ const PricingView = () => { className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-xl font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" onClick={() => (session?.user?.email ? checkout() : signIn())} > - {session?.user?.email - ? t("setting.plan.early-bird-checkout") - : t("payment.sign-in-to-buy")} + {session?.user?.email ? t("setting.plan.early-bird-checkout") : t("payment.sign-in-to-buy")}
diff --git a/src/components/ProductHuntBanner.tsx b/src/components/ProductHuntBanner.tsx index d090d4e3..2985e3e8 100644 --- a/src/components/ProductHuntBanner.tsx +++ b/src/components/ProductHuntBanner.tsx @@ -9,10 +9,7 @@ interface Props { const ProductHuntBanner = (props: Props) => { const { className } = props; const { t } = useTranslation(); - const [hideBanner, setHideBanner] = useLocalStorage( - "hide-product-hunt-banner", - false - ); + const [hideBanner, setHideBanner] = useLocalStorage("hide-product-hunt-banner", false); const show = !hideBanner; return ( @@ -28,10 +25,7 @@ const ProductHuntBanner = (props: Props) => { > {t("banner.product-hunt")}
-
diff --git a/src/components/QueryDrawer.tsx b/src/components/QueryDrawer.tsx index 177dc553..17223b8d 100644 --- a/src/components/QueryDrawer.tsx +++ b/src/components/QueryDrawer.tsx @@ -16,17 +16,12 @@ import ExecutionWarningBanner from "./ExecutionView/ExecutionWarningBanner"; const QueryDrawer = () => { const { t } = useTranslation(); const queryStore = useQueryStore(); - const [executionResult, setExecutionResult] = useState< - ExecutionResult | undefined - >(undefined); + const [executionResult, setExecutionResult] = useState(undefined); const [statement, setStatement] = useState(""); const [isLoading, setIsLoading] = useState(false); const context = queryStore.context; - const executionMessage = executionResult - ? getMessageFromExecutionResult(executionResult) - : ""; - const showExecutionWarningBanner = - statement.trim() && !checkStatementIsSelect(statement); + const executionMessage = executionResult ? getMessageFromExecutionResult(executionResult) : ""; + const showExecutionWarningBanner = statement.trim() && !checkStatementIsSelect(statement); useEffect(() => { if (!queryStore.showDrawer) { @@ -89,40 +84,25 @@ const QueryDrawer = () => { const close = () => queryStore.toggleDrawer(false); return ( - +
-

{t("execution.title")}

{!context ? (
- - {t("execution.message.no-connection")} - + {t("execution.message.no-connection")}
) : ( <>
{t("connection.self")}: - + {context.database?.name}
- {showExecutionWarningBanner && ( - - )} + {showExecutionWarningBanner && }
{ {isLoading ? (
- - {t("execution.message.executing")} - + {t("execution.message.executing")}
) : ( <> {executionResult ? ( executionMessage ? ( - + ) : ( - + ) ) : ( <> diff --git a/src/components/QuotaOverflowBanner.tsx b/src/components/QuotaOverflowBanner.tsx index 33154de1..685f2577 100644 --- a/src/components/QuotaOverflowBanner.tsx +++ b/src/components/QuotaOverflowBanner.tsx @@ -10,10 +10,7 @@ interface Props { const QuotaOverflowBanner = (props: Props) => { const { className } = props; const { t } = useTranslation(); - const [hideBanner, setHideBanner] = useLocalStorage( - "hide-quota-overflow-banner", - false - ); + const [hideBanner, setHideBanner] = useLocalStorage("hide-quota-overflow-banner", false); const show = !hideBanner; return ( @@ -29,10 +26,7 @@ const QuotaOverflowBanner = (props: Props) => { {t("banner.use-my-key")}
-
diff --git a/src/components/QuotaView.tsx b/src/components/QuotaView.tsx index 8e2fa107..25bf4098 100644 --- a/src/components/QuotaView.tsx +++ b/src/components/QuotaView.tsx @@ -16,11 +16,8 @@ const QuotaView = (props: Props) => { const { data: session } = useSession(); const showSupplyOwnKey = !session || quota.current >= quota.limit; - const expired = - session?.user?.subscription?.expireAt && - session?.user?.subscription?.expireAt < Date.now(); - const showActionButton = - !session || session.user.subscription.plan === "FREE" || expired; + const expired = session?.user?.subscription?.expireAt && session?.user?.subscription?.expireAt < Date.now(); + const showActionButton = !session || session.user.subscription.plan === "FREE" || expired; const refreshQuota = async (userId: string) => { let quota: Quota = { current: 0, limit: 0 }; @@ -51,9 +48,7 @@ const QuotaView = (props: Props) => {
- {session - ? t(`setting.plan.${session.user.subscription.plan.toLowerCase()}`) - : t("setting.plan.guest")} + {session ? t(`setting.plan.${session.user.subscription.plan.toLowerCase()}`) : t("setting.plan.guest")} {!!expired && ( @@ -63,11 +58,7 @@ const QuotaView = (props: Props) => {
{t("common.quota")}
-
= quota.limit ? "text-red-600" : "text-black" - } - > +
= quota.limit ? "text-red-600" : "text-black"}> {quota.current}/{quota.limit}
@@ -88,10 +79,7 @@ const QuotaView = (props: Props) => { ))} {!!showSupplyOwnKey && ( - + {t("banner.use-my-key")} )} diff --git a/src/components/SettingAvatarIcon.tsx b/src/components/SettingAvatarIcon.tsx index 9dd16fd7..33b77d26 100644 --- a/src/components/SettingAvatarIcon.tsx +++ b/src/components/SettingAvatarIcon.tsx @@ -1,9 +1,9 @@ import { useSession } from "next-auth/react"; import { useTranslation } from "react-i18next"; import Link from "next/link"; +import { hasFeature } from "../utils"; import Tooltip from "./kit/Tooltip"; import Icon from "./Icon"; -import { hasFeature } from "../utils"; interface Props {} @@ -33,9 +33,7 @@ const SettingAvatarIcon = (props: Props) => { /> ) : (
- {session.user.name - ? session.user.name.charAt(0) - : session.user.email?.charAt(0)} + {session.user.name ? session.user.name.charAt(0) : session.user.email?.charAt(0)}
)} diff --git a/src/components/Sidebar/ConnectionList.tsx b/src/components/Sidebar/ConnectionList.tsx index 9dc2e375..9ef99734 100644 --- a/src/components/Sidebar/ConnectionList.tsx +++ b/src/components/Sidebar/ConnectionList.tsx @@ -20,8 +20,7 @@ const ConnectionList = () => { showCreateConnectionModal: false, showUpdateConversationModal: false, }); - const [editConnectionModalContext, setEditConnectionModalContext] = - useState(); + const [editConnectionModalContext, setEditConnectionModalContext] = useState(); const connectionList = connectionStore.connectionList; const currentConnectionCtx = connectionStore.currentConnectionCtx; @@ -34,9 +33,7 @@ const ConnectionList = () => { }; const handleConnectionSelect = async (connection: Connection) => { - const databaseList = await connectionStore.getOrFetchDatabaseList( - connection - ); + const databaseList = await connectionStore.getOrFetchDatabaseList(connection); connectionStore.setCurrentConnectionCtx({ connection, database: head(databaseList), @@ -54,10 +51,7 @@ const ConnectionList = () => { return ( <> ))} @@ -97,10 +87,7 @@ const ConnectionList = () => { {state.showCreateConnectionModal && ( - toggleCreateConnectionModal(false)} - /> + toggleCreateConnectionModal(false)} /> )} ); diff --git a/src/components/Sidebar/ConversationList.tsx b/src/components/Sidebar/ConversationList.tsx index dc536496..eb99b386 100644 --- a/src/components/Sidebar/ConversationList.tsx +++ b/src/components/Sidebar/ConversationList.tsx @@ -1,10 +1,6 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { - useConversationStore, - useConnectionStore, - useLayoutStore, -} from "@/store"; +import { useConversationStore, useConnectionStore, useLayoutStore } from "@/store"; import { Conversation } from "@/types"; import Dropdown, { DropdownItem } from "../kit/Dropdown"; import Icon from "../Icon"; @@ -22,8 +18,7 @@ const ConversationList = () => { const [state, setState] = useState({ showUpdateConversationModal: false, }); - const [updateConversationModalContext, setUpdateConversationModalContext] = - useState(); + const [updateConversationModalContext, setUpdateConversationModalContext] = useState(); const currentConnectionCtx = connectionStore.currentConnectionCtx; const conversationList = conversationStore.conversationList.filter( (conversation) => @@ -42,10 +37,7 @@ const ConversationList = () => { if (!currentConnectionCtx) { conversationStore.createConversation(); } else { - conversationStore.createConversation( - currentConnectionCtx.connection.id, - currentConnectionCtx.database?.name - ); + conversationStore.createConversation(currentConnectionCtx.connection.id, currentConnectionCtx.database?.name); } }; @@ -77,8 +69,7 @@ const ConversationList = () => {
handleConversationSelect(conversation)} > @@ -87,9 +78,7 @@ const ConversationList = () => { ) : ( )} - - {conversation.title || "SQL Chat"} - + {conversation.title || "SQL Chat"} @@ -125,10 +114,7 @@ const ConversationList = () => { {updateConversationModalContext && state.showUpdateConversationModal && ( - toggleUpdateConversationModal(false)} - conversation={updateConversationModalContext} - /> + toggleUpdateConversationModal(false)} conversation={updateConversationModalContext} /> )} ); diff --git a/src/components/ThemeSelector.tsx b/src/components/ThemeSelector.tsx index 36bcb757..86e0782d 100644 --- a/src/components/ThemeSelector.tsx +++ b/src/components/ThemeSelector.tsx @@ -32,14 +32,7 @@ const ThemeSelector = () => { settingStore.setTheme(theme); }; - return ( - ; }; export default ThemeSelector; diff --git a/src/components/ThemeSwitch.tsx b/src/components/ThemeSwitch.tsx index 9098ea25..e7555081 100644 --- a/src/components/ThemeSwitch.tsx +++ b/src/components/ThemeSwitch.tsx @@ -14,10 +14,7 @@ const ThemeSwitch = () => { }; return ( - ); diff --git a/src/components/UpdateConversationModal.tsx b/src/components/UpdateConversationModal.tsx index c883f39c..d6feedb7 100644 --- a/src/components/UpdateConversationModal.tsx +++ b/src/components/UpdateConversationModal.tsx @@ -27,9 +27,7 @@ const UpdateConversationModal = (props: Props) => { label: assistant.name, }; }); - const currentAssistant = assistantList.find( - (assistant) => assistant.id === assistantId - ); + const currentAssistant = assistantList.find((assistant) => assistant.id === assistantId); const handleSaveEdit = () => { const formatedTitle = title.trim(); @@ -48,30 +46,17 @@ const UpdateConversationModal = (props: Props) => { return (
- - setTitle(value)} - /> + + setTitle(value)} />
- setAssistantId(value)} /> {currentAssistant && (
-

- {currentAssistant.description} -

+

{currentAssistant.description}

)} { href="https://github.com/sqlchat/sqlchat/tree/main/assistants" target="_blank" > - {t("assistant.create-your-bot")}{" "} - + {t("assistant.create-your-bot")}
diff --git a/src/components/kit/Modal.tsx b/src/components/kit/Modal.tsx index 5fccfd34..b328a54f 100644 --- a/src/components/kit/Modal.tsx +++ b/src/components/kit/Modal.tsx @@ -19,9 +19,7 @@ const Modal = (props: Props) => { className || "" } flex flex-col bg-white dark:bg-zinc-800 rounded-xl p-4 fixed top-[50%] left-[50%] h-auto max-h-[85vh] w-[90vw] max-w-[90vw] sm:max-w-lg translate-x-[-50%] translate-y-[-50%] z-100 outline-none`} > -

- {title} -

+

{title}

-
- {children} -
+
{children}
); diff --git a/src/components/kit/MultipleSelect.tsx b/src/components/kit/MultipleSelect.tsx index 9576dadd..3edb2744 100644 --- a/src/components/kit/MultipleSelect.tsx +++ b/src/components/kit/MultipleSelect.tsx @@ -16,8 +16,7 @@ interface Props { } const MultipleSelect = (props: Props & { children?: ReactNode }) => { - const { itemList, value, placeholder, className, onValueChange, children } = - props; + const { itemList, value, placeholder, className, onValueChange, children } = props; return ( { - + {children && ( diff --git a/src/components/kit/Popover.tsx b/src/components/kit/Popover.tsx index f67d5f08..0873cf87 100644 --- a/src/components/kit/Popover.tsx +++ b/src/components/kit/Popover.tsx @@ -16,9 +16,7 @@ const Popover = (props: Props) => { {children} diff --git a/src/components/kit/Select.tsx b/src/components/kit/Select.tsx index e5276b92..a309d4cb 100644 --- a/src/components/kit/Select.tsx +++ b/src/components/kit/Select.tsx @@ -39,32 +39,21 @@ const Select = (props: Props) => { > 7 - ? "h-80 overflow-hidden" - : "max-h-80 overflow-auto" + itemList.length > 7 ? "h-80 overflow-hidden" : "max-h-80 overflow-auto" } border dark:border-zinc-800 rounded-lg drop-shadow-lg`} type="auto" > - + - {placeholder && ( - - {placeholder} - - )} + {placeholder && {placeholder}} {itemList.map((item) => ( - - {item.label} - + {item.label} diff --git a/src/components/kit/TextField.tsx b/src/components/kit/TextField.tsx index 604cdfe5..bcb1020d 100644 --- a/src/components/kit/TextField.tsx +++ b/src/components/kit/TextField.tsx @@ -26,9 +26,7 @@ const TextField = (props: Props) => { return ( Promise; - execute: ( - databaseName: string, - statement: string - ) => Promise; + execute: (databaseName: string, statement: string) => Promise; getDatabases: () => Promise; getTables: (databaseName: string) => Promise; getTableStructure: ( diff --git a/src/lib/connectors/mssql/index.ts b/src/lib/connectors/mssql/index.ts index 877014d2..b588b79c 100644 --- a/src/lib/connectors/mssql/index.ts +++ b/src/lib/connectors/mssql/index.ts @@ -4,9 +4,7 @@ import { Connector } from ".."; const systemDatabases = ["master", "tempdb", "model", "msdb"]; -const getMSSQLConnection = async ( - connection: Connection -): Promise => { +const getMSSQLConnection = async (connection: Connection): Promise => { const connectionOptions: any = { server: connection.host, port: parseInt(connection.port), @@ -34,11 +32,7 @@ const testConnection = async (connection: Connection): Promise => { return true; }; -const execute = async ( - connection: Connection, - databaseName: string, - statement: string -): Promise => { +const execute = async (connection: Connection, databaseName: string, statement: string): Promise => { const pool = await getMSSQLConnection(connection); const request = pool.request(); const result = await request.query(`USE ${databaseName}; ${statement}`); @@ -54,11 +48,7 @@ const execute = async ( const getDatabases = async (connection: Connection): Promise => { const pool = await getMSSQLConnection(connection); const request = pool.request(); - const result = await request.query( - `SELECT name FROM sys.databases WHERE name NOT IN ('${systemDatabases.join( - "','" - )}');` - ); + const result = await request.query(`SELECT name FROM sys.databases WHERE name NOT IN ('${systemDatabases.join("','")}');`); await pool.close(); const databaseList = []; for (const row of result.recordset) { @@ -69,10 +59,7 @@ const getDatabases = async (connection: Connection): Promise => { return databaseList; }; -const getTables = async ( - connection: Connection, - databaseName: string -): Promise => { +const getTables = async (connection: Connection, databaseName: string): Promise => { const pool = await getMSSQLConnection(connection); const request = pool.request(); const result = await request.query( @@ -104,9 +91,7 @@ const getTableStructure = async ( // Transform to standard schema string. for (const row of recordset) { columnList.push( - `${row["COLUMN_NAME"]} ${row["DATA_TYPE"].toUpperCase()} ${ - String(row["IS_NULLABLE"]).toUpperCase() === "NO" ? "NOT NULL" : "" - }` + `${row["COLUMN_NAME"]} ${row["DATA_TYPE"].toUpperCase()} ${String(row["IS_NULLABLE"]).toUpperCase() === "NO" ? "NOT NULL" : ""}` ); } structureFetched( @@ -135,9 +120,7 @@ const getTableStructureBatch = async ( // Transform to standard schema string. for (const row of recordset) { columnList.push( - `${row["COLUMN_NAME"]} ${row["DATA_TYPE"].toUpperCase()} ${ - String(row["IS_NULLABLE"]).toUpperCase() === "NO" ? "NOT NULL" : "" - }` + `${row["COLUMN_NAME"]} ${row["DATA_TYPE"].toUpperCase()} ${String(row["IS_NULLABLE"]).toUpperCase() === "NO" ? "NOT NULL" : ""}` ); } structureFetched( @@ -153,27 +136,16 @@ const getTableStructureBatch = async ( const newConnector = (connection: Connection): Connector => { return { testConnection: () => testConnection(connection), - execute: (databaseName: string, statement: string) => - execute(connection, databaseName, statement), + execute: (databaseName: string, statement: string) => execute(connection, databaseName, statement), getDatabases: () => getDatabases(connection), getTables: (databaseName: string) => getTables(connection, databaseName), - getTableStructure: ( - databaseName: string, - tableName: string, - structureFetched: (tableName: string, structure: string) => void - ) => + getTableStructure: (databaseName: string, tableName: string, structureFetched: (tableName: string, structure: string) => void) => getTableStructure(connection, databaseName, tableName, structureFetched), getTableStructureBatch: ( databaseName: string, tableNameList: string[], structureFetched: (tableName: string, structure: string) => void - ) => - getTableStructureBatch( - connection, - databaseName, - tableNameList, - structureFetched - ), + ) => getTableStructureBatch(connection, databaseName, tableNameList, structureFetched), }; }; diff --git a/src/lib/connectors/mysql/index.ts b/src/lib/connectors/mysql/index.ts index 81cf9a3b..91ec7377 100644 --- a/src/lib/connectors/mysql/index.ts +++ b/src/lib/connectors/mysql/index.ts @@ -3,16 +3,9 @@ import mysql, { RowDataPacket } from "mysql2/promise"; import { Connection, ExecutionResult } from "@/types"; import { Connector } from ".."; -const systemDatabases = [ - "information_schema", - "mysql", - "performance_schema", - "sys", -]; +const systemDatabases = ["information_schema", "mysql", "performance_schema", "sys"]; -const getMySQLConnection = async ( - connection: Connection -): Promise => { +const getMySQLConnection = async (connection: Connection): Promise => { const connectionOptions: ConnectionOptions = { host: connection.host, port: parseInt(connection.port), @@ -37,11 +30,7 @@ const testConnection = async (connection: Connection): Promise => { return true; }; -const execute = async ( - connection: Connection, - databaseName: string, - statement: string -): Promise => { +const execute = async (connection: Connection, databaseName: string, statement: string): Promise => { connection.database = databaseName; const conn = await getMySQLConnection(connection); const [rows] = await conn.execute(statement); @@ -75,10 +64,7 @@ const getDatabases = async (connection: Connection): Promise => { return databaseList; }; -const getTables = async ( - connection: Connection, - databaseName: string -): Promise => { +const getTables = async (connection: Connection, databaseName: string): Promise => { const conn = await getMySQLConnection(connection); const [rows] = await conn.query( `SELECT TABLE_NAME as table_name FROM information_schema.tables WHERE TABLE_SCHEMA=? AND TABLE_TYPE='BASE TABLE';`, @@ -101,9 +87,7 @@ const getTableStructure = async ( structureFetched: (tableName: string, structure: string) => void ): Promise => { const conn = await getMySQLConnection(connection); - const [rows] = await conn.query( - `SHOW CREATE TABLE \`${databaseName}\`.\`${tableName}\`;` - ); + const [rows] = await conn.query(`SHOW CREATE TABLE \`${databaseName}\`.\`${tableName}\`;`); conn.destroy(); if (rows.length !== 1) { throw new Error("Unexpected number of rows."); @@ -121,9 +105,7 @@ const getTableStructureBatch = async ( await Promise.all( tableNameList.map(async (tableName) => { - const [rows] = await conn.query( - `SHOW CREATE TABLE \`${databaseName}\`.\`${tableName}\`;` - ); + const [rows] = await conn.query(`SHOW CREATE TABLE \`${databaseName}\`.\`${tableName}\`;`); if (rows.length !== 1) { throw new Error("Unexpected number of rows."); } @@ -137,27 +119,16 @@ const getTableStructureBatch = async ( const newConnector = (connection: Connection): Connector => { return { testConnection: () => testConnection(connection), - execute: (databaseName: string, statement: string) => - execute(connection, databaseName, statement), + execute: (databaseName: string, statement: string) => execute(connection, databaseName, statement), getDatabases: () => getDatabases(connection), getTables: (databaseName: string) => getTables(connection, databaseName), - getTableStructure: ( - databaseName: string, - tableName: string, - structureFetched: (tableName: string, structure: string) => void - ) => + getTableStructure: (databaseName: string, tableName: string, structureFetched: (tableName: string, structure: string) => void) => getTableStructure(connection, databaseName, tableName, structureFetched), getTableStructureBatch: ( databaseName: string, tableNameList: string[], structureFetched: (tableName: string, structure: string) => void - ) => - getTableStructureBatch( - connection, - databaseName, - tableNameList, - structureFetched - ), + ) => getTableStructureBatch(connection, databaseName, tableNameList, structureFetched), }; }; diff --git a/src/lib/connectors/postgres/index.ts b/src/lib/connectors/postgres/index.ts index 31ab8f4d..f2163d3a 100644 --- a/src/lib/connectors/postgres/index.ts +++ b/src/lib/connectors/postgres/index.ts @@ -27,11 +27,7 @@ const testConnection = async (connection: Connection): Promise => { return true; }; -const execute = async ( - connection: Connection, - databaseName: string, - statement: string -): Promise => { +const execute = async (connection: Connection, databaseName: string, statement: string): Promise => { connection.database = databaseName; const client = newPostgresClient(connection); await client.connect(); @@ -69,10 +65,7 @@ const getDatabases = async (connection: Connection): Promise => { return databaseList; }; -const getTables = async ( - connection: Connection, - databaseName: string -): Promise => { +const getTables = async (connection: Connection, databaseName: string): Promise => { connection.database = databaseName; const client = newPostgresClient(connection); await client.connect(); @@ -108,9 +101,7 @@ const getTableStructure = async ( // TODO(steven): transform it to standard schema string. for (const row of rows) { columnList.push( - `${row["column_name"]} ${row["data_type"].toUpperCase()} ${ - String(row["is_nullable"]).toUpperCase() === "NO" ? "NOT NULL" : "" - }` + `${row["column_name"]} ${row["data_type"].toUpperCase()} ${String(row["is_nullable"]).toUpperCase() === "NO" ? "NOT NULL" : ""}` ); } structureFetched( @@ -140,9 +131,7 @@ const getTableStructureBatch = async ( // TODO(steven): transform it to standard schema string. for (const row of rows) { columnList.push( - `${row["column_name"]} ${row["data_type"].toUpperCase()} ${ - String(row["is_nullable"]).toUpperCase() === "NO" ? "NOT NULL" : "" - }` + `${row["column_name"]} ${row["data_type"].toUpperCase()} ${String(row["is_nullable"]).toUpperCase() === "NO" ? "NOT NULL" : ""}` ); } structureFetched( @@ -160,27 +149,16 @@ const getTableStructureBatch = async ( const newConnector = (connection: Connection): Connector => { return { testConnection: () => testConnection(connection), - execute: (databaseName: string, statement: string) => - execute(connection, databaseName, statement), + execute: (databaseName: string, statement: string) => execute(connection, databaseName, statement), getDatabases: () => getDatabases(connection), getTables: (databaseName: string) => getTables(connection, databaseName), - getTableStructure: ( - databaseName: string, - tableName: string, - structureFetched: (tableName: string, structure: string) => void - ) => + getTableStructure: (databaseName: string, tableName: string, structureFetched: (tableName: string, structure: string) => void) => getTableStructure(connection, databaseName, tableName, structureFetched), getTableStructureBatch: ( databaseName: string, tableNameList: string[], structureFetched: (tableName: string, structure: string) => void - ) => - getTableStructureBatch( - connection, - databaseName, - tableNameList, - structureFetched - ), + ) => getTableStructureBatch(connection, databaseName, tableNameList, structureFetched), }; }; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9f3f58b1..32f6c7fd 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -19,10 +19,7 @@ import "@/styles/mui.css"; import type { Session } from "next-auth"; -function MyApp({ - Component, - pageProps: { session, ...pageProps }, -}: AppProps<{ session: Session }>) { +function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps<{ session: Session }>) { const { i18n } = useTranslation(); const settingStore = useSettingStore(); @@ -58,10 +55,7 @@ function MyApp({ const theme = settingStore.setting.theme; let currentAppearance = theme; if (theme === "system") { - if ( - window.matchMedia && - window.matchMedia("(prefers-color-scheme: dark)").matches - ) { + if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { currentAppearance = "dark"; } else { currentAppearance = "light"; diff --git a/src/pages/api/auth/end-user.ts b/src/pages/api/auth/end-user.ts index 0c9c6c96..3b8ece94 100644 --- a/src/pages/api/auth/end-user.ts +++ b/src/pages/api/auth/end-user.ts @@ -1,16 +1,13 @@ import { PrismaClient } from "@prisma/client"; import { NextApiRequest, NextApiResponse } from "next"; import { getServerSession } from "next-auth/next"; -import { authOptions } from "./[...nextauth]"; import requestIp from "request-ip"; +import { authOptions } from "./[...nextauth]"; const prisma = new PrismaClient(); // Returns the login user email or the client IP address -export const getEndUser = async ( - req: NextApiRequest, - res: NextApiResponse -): Promise => { +export const getEndUser = async (req: NextApiRequest, res: NextApiResponse): Promise => { // Get from server session if available const serverSession = await getServerSession(req, res, authOptions); if (serverSession?.user?.email) { diff --git a/src/pages/api/chat.ts b/src/pages/api/chat.ts index 5101e167..3decfd0c 100644 --- a/src/pages/api/chat.ts +++ b/src/pages/api/chat.ts @@ -1,8 +1,4 @@ -import { - createParser, - ParsedEvent, - ReconnectInterval, -} from "eventsource-parser"; +import { createParser, ParsedEvent, ReconnectInterval } from "eventsource-parser"; import { NextRequest } from "next/server"; import { openAIApiEndpoint, openAIApiKey, gpt35, hasFeature } from "@/utils"; @@ -25,8 +21,7 @@ const handler = async (req: NextRequest) => { return new Response( JSON.stringify({ error: { - message: - "OpenAI API Key is missing. You can supply your own key via Settings.", + message: "OpenAI API Key is missing. You can supply your own key via Settings.", }, }), { @@ -41,9 +36,7 @@ const handler = async (req: NextRequest) => { const useServerKey = !req.headers.get("x-openai-key"); const sessionToken = req.cookies.get("next-auth.session-token")?.value; const currentUrl = new URL(req.url); - const usageUrl = new URL( - currentUrl.protocol + "//" + currentUrl.host + "/api/usage" - ); + const usageUrl = new URL(currentUrl.protocol + "//" + currentUrl.host + "/api/usage"); const requestHeaders: any = { Authorization: `Bearer ${sessionToken}`, }; @@ -53,8 +46,7 @@ const handler = async (req: NextRequest) => { return new Response( JSON.stringify({ error: { - message: - "Please sign up to get free quota or supply your own OpenAI key.", + message: "Please sign up to get free quota or supply your own OpenAI key.", }, }), { @@ -97,9 +89,7 @@ const handler = async (req: NextRequest) => { } } - const apiEndpoint = getApiEndpoint( - req.headers.get("x-openai-endpoint") || openAIApiEndpoint - ); + const apiEndpoint = getApiEndpoint(req.headers.get("x-openai-endpoint") || openAIApiEndpoint); const remoteRes = await fetch(apiEndpoint, { headers: { "Content-Type": "application/json", diff --git a/src/pages/api/checkout_sessions/[id].ts b/src/pages/api/checkout_sessions/[id].ts index 7dc1b320..5d2fc218 100644 --- a/src/pages/api/checkout_sessions/[id].ts +++ b/src/pages/api/checkout_sessions/[id].ts @@ -6,24 +6,19 @@ const stripe = new Stripe(process.env.STRIPE_API_KEY, { apiVersion: "2022-11-15", }); -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { +export default async function handler(req: NextApiRequest, res: NextApiResponse) { const id: string = req.query.id as string; try { if (!id.startsWith("cs_")) { throw Error("Incorrect CheckoutSession ID."); } - const checkout_session: Stripe.Checkout.Session = - await stripe.checkout.sessions.retrieve(id, { - expand: ["payment_intent"], - }); + const checkout_session: Stripe.Checkout.Session = await stripe.checkout.sessions.retrieve(id, { + expand: ["payment_intent"], + }); res.status(200).json(checkout_session); } catch (err) { - const errorMessage = - err instanceof Error ? err.message : "Internal server error"; + const errorMessage = err instanceof Error ? err.message : "Internal server error"; res.status(500).json({ statusCode: 500, message: errorMessage }); } } diff --git a/src/pages/api/checkout_sessions/index.ts b/src/pages/api/checkout_sessions/index.ts index 4a8a82ac..e6f045be 100644 --- a/src/pages/api/checkout_sessions/index.ts +++ b/src/pages/api/checkout_sessions/index.ts @@ -3,7 +3,6 @@ import { getServerSession } from "next-auth/next"; import { authOptions } from "../auth/[...nextauth]"; import { PrismaClient } from "@prisma/client"; import Stripe from "stripe"; -import { PlanType } from "@/types"; const stripe = new Stripe(process.env.STRIPE_API_KEY, { // https://github.com/stripe/stripe-node#configuration @@ -12,10 +11,7 @@ const stripe = new Stripe(process.env.STRIPE_API_KEY, { const prisma = new PrismaClient(); -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { +export default async function handler(req: NextApiRequest, res: NextApiResponse) { const session = await getServerSession(req, res, authOptions); if (!session?.user?.email) { @@ -71,13 +67,11 @@ export default async function handler( success_url: `${req.headers.origin}/setting?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${req.headers.origin}/setting`, }; - const checkoutSession: Stripe.Checkout.Session = - await stripe.checkout.sessions.create(params); + const checkoutSession: Stripe.Checkout.Session = await stripe.checkout.sessions.create(params); res.status(200).json(checkoutSession); } catch (err) { - const errorMessage = - err instanceof Error ? err.message : "Internal server error"; + const errorMessage = err instanceof Error ? err.message : "Internal server error"; res.status(500).json({ statusCode: 500, message: errorMessage }); } } else { diff --git a/src/pages/api/collect.ts b/src/pages/api/collect.ts index c40bcbe5..f0d5647a 100644 --- a/src/pages/api/collect.ts +++ b/src/pages/api/collect.ts @@ -6,10 +6,7 @@ import { getEndUser } from "./auth/end-user"; const prisma = new PrismaClient(); -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { +export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== "POST") { return res.status(405).json([]); } diff --git a/src/pages/api/connection/db_schema.ts b/src/pages/api/connection/db_schema.ts index 85644d88..c199b9c8 100644 --- a/src/pages/api/connection/db_schema.ts +++ b/src/pages/api/connection/db_schema.ts @@ -28,11 +28,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { structure, }); }; - await connector.getTableStructureBatch( - db, - rawTableNameList, - structureFetched - ); + await connector.getTableStructureBatch(db, rawTableNameList, structureFetched); res.status(200).json({ data: tableStructures, diff --git a/src/pages/api/utils/subscription.ts b/src/pages/api/utils/subscription.ts index d07303b0..0ed85ab7 100644 --- a/src/pages/api/utils/subscription.ts +++ b/src/pages/api/utils/subscription.ts @@ -1,16 +1,9 @@ -import { - PlanConfig, - PlanType, - Subscription, - SubscriptionPurchase, -} from "@/types"; +import { PlanConfig, PlanType, Subscription, SubscriptionPurchase } from "@/types"; import { PrismaClient, SubscriptionStatus } from "@prisma/client"; const prisma = new PrismaClient(); -export const getSubscriptionByEmail = async ( - email: string -): Promise => { +export const getSubscriptionByEmail = async (email: string): Promise => { const subscriptions = await prisma.subscription.findMany({ where: { email: email }, orderBy: { expireAt: "desc" }, @@ -25,10 +18,7 @@ export const getSubscriptionByEmail = async ( startAt: subscription.startAt.getTime(), expireAt: subscription.expireAt.getTime(), }; - if ( - subscription.status === SubscriptionStatus.ACTIVE && - subscription.expireAt.getTime() > Date.now() - ) { + if (subscription.status === SubscriptionStatus.ACTIVE && subscription.expireAt.getTime() > Date.now()) { return result; } } @@ -57,9 +47,7 @@ export const getSubscriptionByEmail = async ( }; }; -export const getSubscriptionListByEmail = async ( - email: string -): Promise => { +export const getSubscriptionListByEmail = async (email: string): Promise => { const subscriptions = await prisma.subscription.findMany({ where: { email: email }, orderBy: { expireAt: "desc" }, diff --git a/src/pages/api/utils/usage.ts b/src/pages/api/utils/usage.ts index cc17de1f..3d01fda8 100644 --- a/src/pages/api/utils/usage.ts +++ b/src/pages/api/utils/usage.ts @@ -2,9 +2,7 @@ import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); -export const getCurrentMonthUsage = async ( - endUser: string -): Promise => { +export const getCurrentMonthUsage = async (endUser: string): Promise => { const now = new Date(); const start = new Date(now.getFullYear(), now.getMonth(), 1); const end = new Date(now.getFullYear(), now.getMonth() + 1, 1); @@ -27,9 +25,7 @@ export const getCurrentMonthUsage = async ( // We coerce individual usage to the begining of the day to reduce the usage records. export const addUsage = async (endUser: string): Promise => { const now = new Date(); - const today = new Date( - Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()) - ); + const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); const usage = await prisma.usage.findFirst({ where: { endUser: endUser, diff --git a/src/pages/api/webhook/index.ts b/src/pages/api/webhook/index.ts index b12a38a6..9ab8e008 100644 --- a/src/pages/api/webhook/index.ts +++ b/src/pages/api/webhook/index.ts @@ -1,10 +1,9 @@ +import { PrismaClient, Prisma, SubscriptionPlan } from "@prisma/client"; import { buffer } from "micro"; import Cors from "micro-cors"; import { NextApiRequest, NextApiResponse } from "next"; -import { PrismaClient, Prisma, SubscriptionPlan } from "@prisma/client"; - import Stripe from "stripe"; -import { PlanType } from "@/types"; + const stripe = new Stripe(process.env.STRIPE_API_KEY, { // https://github.com/stripe/stripe-node#configuration apiVersion: "2022-11-15", @@ -32,11 +31,7 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => { let event: Stripe.Event; try { - event = stripe.webhooks.constructEvent( - buf.toString(), - sig, - webhookSecret - ); + event = stripe.webhooks.constructEvent(buf.toString(), sig, webhookSecret); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Unknown error"; // On error, log and return the error message. @@ -49,9 +44,7 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Cast event data to Stripe object. if (event.type === "payment_intent.succeeded") { const paymentIntent = event.data.object as Stripe.PaymentIntent; - const charge = await stripe.charges.retrieve( - paymentIntent.latest_charge as string - ); + const charge = await stripe.charges.retrieve(paymentIntent.latest_charge as string); const customerId = paymentIntent.customer as string; if (customerId) { @@ -72,11 +65,7 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => { const today = new Date(new Date().setHours(0, 0, 0, 0)); // Subtract 1 second from the year from now to make it 23:59:59 - const yearFromNow = new Date( - new Date(new Date().setHours(0, 0, 0, 0)).setFullYear( - today.getFullYear() + 1 - ) - 1000 - ); + const yearFromNow = new Date(new Date(new Date().setHours(0, 0, 0, 0)).setFullYear(today.getFullYear() + 1) - 1000); const subscription: Prisma.SubscriptionUncheckedCreateInput = { userId: user.id, email: paymentIntent.metadata.email, @@ -95,9 +84,7 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => { await prisma.subscription.create({ data: subscription }); } else if (event.type === "payment_intent.payment_failed") { const paymentIntent = event.data.object as Stripe.PaymentIntent; - console.log( - `❌ Payment failed: ${paymentIntent.last_payment_error?.message}` - ); + console.log(`❌ Payment failed: ${paymentIntent.last_payment_error?.message}`); } // Return a response to acknowledge receipt of the event. diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4cd999cd..9b5f777d 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -6,18 +6,12 @@ import React from "react"; // Use dynamic import to avoid page hydrated. // reference: https://github.com/pmndrs/zustand/issues/1145#issuecomment-1316431268 -const ConnectionSidebar = dynamic( - () => import("@/components/ConnectionSidebar"), - { - ssr: false, - } -); -const ConversationView = dynamic( - () => import("@/components/ConversationView"), - { - ssr: false, - } -); +const ConnectionSidebar = dynamic(() => import("@/components/ConnectionSidebar"), { + ssr: false, +}); +const ConversationView = dynamic(() => import("@/components/ConversationView"), { + ssr: false, +}); const QueryDrawer = dynamic(() => import("@/components/QueryDrawer"), { ssr: false, }); @@ -26,31 +20,14 @@ const IndexPage: NextPage = () => { return (
- - SQL Chat - Chat-based SQL Client and Editor for the next decade - - + SQL Chat - Chat-based SQL Client and Editor for the next decade + - - + + - +

SQL Chat

@@ -61,11 +38,7 @@ const IndexPage: NextPage = () => { -