Browse Source

feat: 完成对接已有的后端

tuonian 1 year ago
parent
commit
336882b4ae

+ 1 - 1
.env.development

@@ -42,6 +42,6 @@ DISABLE_FAST_LINK=
 
 OAUTH_CLIENT_ID=52065645520a434ab8d2170a3db6e09f
 OAUTH_CLIENT_SECRET=e63a36c1024f41afb80004e49c5564cf
-OAUTH_REDIRECT_URI=http://localhost:3000/
+OAUTH_REDIRECT_URI=http://localhost:3000/chat
 OAUTH_AUTHORIZE_ENDPOINT=https://www.tonyandmoney.cn/oauth/authorize
 OAUTH_USERINFO=https://api.tonyandmoney.cn/oauth/user

+ 2 - 3
.env.production

@@ -3,7 +3,7 @@
 BASE_PATH=/chat
 
 # Your openai api key. (required)
-OPENAI_API_KEY=sk-xxxx
+OPENAI_API_KEY=
 
 # Access passsword, separated by comma. (optional)
 CODE=
@@ -14,8 +14,7 @@ PROXY_URL=http://10.10.0.6:13123
 # Override openai api request base url. (optional)
 # Default: https://api.openai.com
 # Examples: http://your-openai-proxy.com
-BASE_URL=http://10.10.0.1:20003/
-#BASE_URL=http://10.10.0.2:20003/
+BASE_URL=https://api.tonyandmoney.cn/
 
 # Specify OpenAI organization ID.(optional)
 # Default: Empty

+ 2 - 3
app/api/common.ts

@@ -17,8 +17,7 @@ export async function requestOpenai(req: NextRequest) {
     "",
   );
 
-  let baseUrl =
-    serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL;
+  let baseUrl = serverConfig.baseUrl ?? OPENAI_BASE_URL;
 
   if (!baseUrl.startsWith("http")) {
     baseUrl = `https://${baseUrl}`;
@@ -28,7 +27,7 @@ export async function requestOpenai(req: NextRequest) {
     baseUrl = baseUrl.slice(0, -1);
   }
 
-  console.log("[Proxy] ", path);
+  console.log("[Proxy Path] ", path, req.method);
   console.log("[Base Url]", baseUrl);
   // this fix [Org ID] undefined in server side if not using custom point
   if (serverConfig.openaiOrgId !== undefined) {

+ 1 - 1
app/client/api.ts

@@ -51,7 +51,7 @@ export abstract class LLMApi {
 }
 
 export abstract class UserApi {
-  abstract userinfo(): Promise<void>;
+  abstract userinfo(): Promise<Response | undefined>;
 }
 
 type ProviderName = "openai" | "azure" | "claude" | "palm";

+ 2 - 31
app/client/platforms/openai.ts

@@ -15,8 +15,7 @@ import {
   fetchEventSource,
 } from "@fortaine/fetch-event-source";
 import { prettyObject } from "@/app/utils/format";
-import { getClientConfig } from "@/app/config/client";
-import { makeAzurePath } from "@/app/azure";
+import { getFullApi } from "@/app/config/client";
 
 export interface OpenAIListModelResponse {
   object: string;
@@ -31,35 +30,7 @@ export class ChatGPTApi implements LLMApi {
   private disableListModels = true;
 
   path(path: string): string {
-    const accessStore = useAccessStore.getState();
-
-    const isAzure = accessStore.provider === ServiceProvider.Azure;
-
-    if (isAzure && !accessStore.isValidAzure()) {
-      throw Error(
-        "incomplete azure config, please check it in your settings page",
-      );
-    }
-
-    let baseUrl = isAzure ? accessStore.azureUrl : accessStore.openaiUrl;
-
-    if (baseUrl.length === 0) {
-      const isApp = !!getClientConfig()?.isApp;
-      baseUrl = isApp ? DEFAULT_API_HOST : ApiPath.OpenAI;
-    }
-
-    if (baseUrl.endsWith("/")) {
-      baseUrl = baseUrl.slice(0, baseUrl.length - 1);
-    }
-    if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.OpenAI)) {
-      baseUrl = "https://" + baseUrl;
-    }
-
-    if (isAzure) {
-      path = makeAzurePath(path, accessStore.azureApiVersion);
-    }
-
-    return [baseUrl, path].join("/");
+    return getFullApi([ApiPath.OpenAI, path].join("/"));
   }
 
   extractMessage(res: any) {

+ 6 - 4
app/client/platforms/user.ts

@@ -1,7 +1,7 @@
-import { REQUEST_TIMEOUT_MS } from "@/app/constant";
-import { useAccessStore, useAppConfig } from "@/app/store";
+import { useAccessStore } from "@/app/store";
 
 import { getHeaders, UserApi } from "../api";
+import { getFullApi } from "@/app/config/client";
 
 export class OauthUserApi implements UserApi {
   async userinfo() {
@@ -9,7 +9,7 @@ export class OauthUserApi implements UserApi {
     const controller = new AbortController();
 
     try {
-      const url = "/api/user";
+      const url = getFullApi("/api/user");
       const payload = {
         method: "GET",
         signal: controller.signal,
@@ -18,8 +18,10 @@ export class OauthUserApi implements UserApi {
       const res = await fetch(url, payload);
       if (res.status == 401) {
         accessStore.clearToken();
+      } else {
+        console.log("res status", res.status, res.statusText, res.ok);
       }
-      console.log("res status", res.status, res.statusText);
+      return res;
     } catch (e) {
       console.log("[Request] failed to make a chat request", e);
     }

+ 7 - 7
app/components/exporter.tsx

@@ -370,13 +370,13 @@ export function PreviewActions(props: {
           icon={<DownloadIcon />}
           onClick={props.download}
         ></IconButton>
-        <IconButton
-          text={Locale.Export.Share}
-          bordered
-          shadow
-          icon={loading ? <LoadingIcon /> : <ShareIcon />}
-          onClick={share}
-        ></IconButton>
+        {/*<IconButton*/}
+        {/*  text={Locale.Export.Share}*/}
+        {/*  bordered*/}
+        {/*  shadow*/}
+        {/*  icon={loading ? <LoadingIcon /> : <ShareIcon />}*/}
+        {/*  onClick={share}*/}
+        {/*></IconButton>*/}
       </div>
       <div
         style={{

+ 36 - 20
app/components/oauth/index.tsx

@@ -3,6 +3,7 @@ import { getClientConfig } from "@/app/config/client";
 import { useAccessStore } from "@/app/store";
 import { api } from "@/app/client/api";
 import dayjs from "dayjs";
+import { showToast } from "@/app/components/ui-lib";
 
 export type ITokenData = {
   access_token: string;
@@ -17,6 +18,7 @@ export type ITokenData = {
 export const EnsureToken = (props: any) => {
   const accessStore = useAccessStore();
   const [loading, setLoading] = useState(true);
+  const [user, setUser] = useState<any>();
 
   const config = getClientConfig();
 
@@ -43,15 +45,36 @@ export const EnsureToken = (props: any) => {
         } catch (e) {
           setLoading(false);
           console.log("parse token fail", e);
+          showToast("登录异常,请联系管理员处理!");
         }
-      } else if (accessStore.hasAccessToken()) {
-        api.user.userinfo().finally(() => {
+        return;
+      }
+      api.user
+        .userinfo()
+        .then(async (resp) => {
+          if (!resp) {
+            return;
+          }
+          if (resp.status == 401) {
+            if (config?.authorizeUrl) {
+              window.location.href = config?.authorizeUrl;
+            } else {
+              console.log("login expired");
+            }
+            return;
+          }
+          const data = await resp.json();
+          if (data.code == 1) {
+            accessStore.setLogin(true);
+            setUser(data);
+          } else {
+            console.log("fail", data);
+          }
+        })
+        .catch(() => {})
+        .finally(() => {
           setLoading(false);
         });
-      } else {
-        accessStore.clearToken();
-        setLoading(false);
-      }
     };
     parseToken();
     const handler = setInterval(parseToken, 10000);
@@ -60,19 +83,12 @@ export const EnsureToken = (props: any) => {
     };
   }, []);
 
-  if (loading) {
-    return (
-      <div>
-        <span>Loading...</span>
-      </div>
-    );
-  }
-  if (!accessStore?.hasAccessToken()) {
-    return (
-      <div>
-        <a href={config?.authorizeUrl ?? "/login"}>登录</a>
-      </div>
-    );
+  if (accessStore.isLogin() && user) {
+    return <>{props.children}</>;
   }
-  return <>{props.children}</>;
+  return (
+    <div>
+      <span>加载中,请稍后...</span>
+    </div>
+  );
 };

+ 2 - 0
app/config/build.ts

@@ -38,6 +38,7 @@ export const getBuildConfig = () => {
     OAUTH_REDIRECT_URI,
     OAUTH_CLIENT_SECRET,
     OAUTH_USERINFO,
+    BASE_PATH,
   } = process.env;
   let authorizeUrl = "";
   if (OAUTH_AUTHORIZE_ENDPOINT && OAUTH_CLIENT_ID && OAUTH_REDIRECT_URI) {
@@ -52,6 +53,7 @@ export const getBuildConfig = () => {
     buildMode,
     isApp,
     authorizeUrl,
+    baseApi: BASE_PATH,
   };
 };
 

+ 15 - 0
app/config/client.ts

@@ -12,6 +12,21 @@ export function getClientConfig() {
   }
 }
 
+/**
+ * 适配basePath不为空的情况
+ * @param api
+ */
+export function getFullApi(api: string) {
+  const base = getClientConfig()?.baseApi ?? "";
+  if (api.startsWith("/") && base.endsWith("/")) {
+    return base + api.substring(1);
+  }
+  if (api.startsWith("/") || base.endsWith("/")) {
+    return base + api;
+  }
+  return base + "/" + api;
+}
+
 function queryMeta(key: string, defaultValue?: string): string {
   let ret: string;
   if (document) {

+ 3 - 3
app/layout.tsx

@@ -2,7 +2,7 @@
 import "./styles/globals.scss";
 import "./styles/markdown.scss";
 import "./styles/highlight.scss";
-import { getClientConfig } from "./config/client";
+import { getClientConfig, getFullApi } from "./config/client";
 import { type Metadata } from "next";
 
 export const metadata: Metadata = {
@@ -32,8 +32,8 @@ export default function RootLayout({
     <html lang="en">
       <head>
         <meta name="config" content={JSON.stringify(getClientConfig())} />
-        <link rel="manifest" href="/site.webmanifest"></link>
-        <script src="/serviceWorkerRegister.js" defer></script>
+        <link rel="manifest" href={getFullApi("/site.webmanifest")}></link>
+        <script src={getFullApi("/serviceWorkerRegister.js")} defer></script>
       </head>
       <body>{children}</body>
     </html>

+ 21 - 21
app/locales/cn.ts

@@ -45,7 +45,7 @@ const cn = {
     },
     Commands: {
       new: "新建聊天",
-      newm: "从面具新建聊天",
+      newm: "从模板新建聊天",
       next: "下一个聊天",
       prev: "上一个聊天",
       clear: "清除上下文",
@@ -60,7 +60,7 @@ const cn = {
         dark: "深色模式",
       },
       Prompt: "快捷指令",
-      Masks: "所有面具",
+      Masks: "所有模板",
       Clear: "清除聊天",
       Settings: "对话设置",
     },
@@ -76,7 +76,7 @@ const cn = {
     Send: "发送",
     Config: {
       Reset: "清除记忆",
-      SaveAs: "存为面具",
+      SaveAs: "存为模板",
     },
     IsContext: "预设提示词",
   },
@@ -92,8 +92,8 @@ const cn = {
       SubTitle: "可以导出 Markdown 文本或者 PNG 图片",
     },
     IncludeContext: {
-      Title: "包含面具上下文",
-      SubTitle: "是否在消息中展示面具上下文",
+      Title: "包含模板上下文",
+      SubTitle: "是否在消息中展示模板上下文",
     },
     Steps: {
       Select: "选取",
@@ -218,18 +218,18 @@ const cn = {
 
       LocalState: "本地数据",
       Overview: (overview: any) => {
-        return `${overview.chat} 次对话,${overview.message} 条消息,${overview.prompt} 条提示词,${overview.mask} 个面具`;
+        return `${overview.chat} 次对话,${overview.message} 条消息,${overview.prompt} 条提示词,${overview.mask} 个模板`;
       },
       ImportFailed: "导入失败",
     },
     Mask: {
       Splash: {
-        Title: "面具启动页",
-        SubTitle: "新建聊天时,展示面具启动页",
+        Title: "模板启动页",
+        SubTitle: "新建聊天时,展示模板启动页",
       },
       Builtin: {
-        Title: "隐藏内置面具",
-        SubTitle: "在所有面具列表中隐藏内置面具",
+        Title: "隐藏内置模板",
+        SubTitle: "在所有模板列表中隐藏内置模板",
       },
     },
     Prompt: {
@@ -374,11 +374,11 @@ const cn = {
     Sysmessage: "你是一个助手",
   },
   Mask: {
-    Name: "面具",
+    Name: "模板",
     Page: {
-      Title: "预设角色面具",
+      Title: "预设角色模板",
       SubTitle: (count: number) => `${count} 个预设角色定义`,
-      Search: "搜索角色面具",
+      Search: "搜索角色模板",
       Create: "新建",
     },
     Item: {
@@ -391,7 +391,7 @@ const cn = {
     },
     EditModal: {
       Title: (readonly: boolean) =>
-        `编辑预设面具 ${readonly ? "(只读)" : ""}`,
+        `编辑预设模板 ${readonly ? "(只读)" : ""}`,
       Download: "下载预设",
       Clone: "克隆预设",
     },
@@ -408,8 +408,8 @@ const cn = {
         SubTitle: "隐藏后预设对话不会出现在聊天界面",
       },
       Share: {
-        Title: "分享此面具",
-        SubTitle: "生成此面具的直达链接",
+        Title: "分享此模板",
+        SubTitle: "生成此模板的直达链接",
         Action: "复制链接",
       },
     },
@@ -419,8 +419,8 @@ const cn = {
     Skip: "直接开始",
     NotShow: "不再展示",
     ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
-    Title: "挑选一个面具",
-    SubTitle: "现在开始,与面具背后的灵魂思维碰撞",
+    Title: "挑选一个模板",
+    SubTitle: "现在开始,与模板背后的灵魂思维碰撞",
     More: "查看全部",
   },
 
@@ -441,9 +441,9 @@ const cn = {
     Config: "配置",
   },
   Exporter: {
-    Description : {
-      Title: "只有清除上下文之后的消息会被展示"
-    },  
+    Description: {
+      Title: "只有清除上下文之后的消息会被展示",
+    },
     Model: "模型",
     Messages: "消息",
     Topic: "主题",

+ 11 - 2
app/store/access.ts

@@ -5,7 +5,7 @@ import {
   StoreKey,
 } from "../constant";
 import { getHeaders } from "../client/api";
-import { getClientConfig } from "../config/client";
+import { getClientConfig, getFullApi } from "../config/client";
 import { createPersistStore } from "../utils/store";
 import { ensure } from "../utils/clone";
 import dayjs from "dayjs";
@@ -29,6 +29,7 @@ const DEFAULT_ACCESS_STATE = {
   accessToken: "",
   expiresIn: 0,
   tokenType: "bearer",
+  hasLogin: false,
 
   // azure
   azureUrl: "",
@@ -59,6 +60,7 @@ export const useAccessStore = createPersistStore(
         accessToken: "",
         expiresIn: 0,
         tokenType: "bearer",
+        hasLogin: false,
       }));
     },
 
@@ -66,6 +68,13 @@ export const useAccessStore = createPersistStore(
       set(() => ({ ...token }));
     },
 
+    setLogin(login = true) {
+      set(() => ({ hasLogin: login }));
+    },
+    isLogin() {
+      return get().hasLogin;
+    },
+
     hasAccessToken() {
       const state = get();
       if (state.provider == ServiceProvider.Oauth) {
@@ -96,7 +105,7 @@ export const useAccessStore = createPersistStore(
     fetch() {
       if (fetchState > 0 || getClientConfig()?.buildMode === "export") return;
       fetchState = 1;
-      fetch("/api/config", {
+      fetch(getFullApi("/api/config"), {
         method: "post",
         body: null,
         headers: {

+ 1 - 4
app/store/chat.ts

@@ -251,10 +251,7 @@ export const useChatStore = createPersistStore(
           index = Math.min(sessions.length - 1, Math.max(0, index));
           set(() => ({ currentSessionIndex: index }));
         }
-
-        const session = sessions[index];
-
-        return session;
+        return sessions[index];
       },
 
       onNewMessage(message: ChatMessage) {

+ 2 - 2
app/store/config.ts

@@ -49,11 +49,11 @@ export const DEFAULT_CONFIG = {
     model: "gpt-3.5-turbo" as ModelType,
     temperature: 0.5,
     top_p: 1,
-    max_tokens: 4000,
+    max_tokens: 2000,
     presence_penalty: 0,
     frequency_penalty: 0,
     sendMemory: true,
-    historyMessageCount: 4,
+    historyMessageCount: 2,
     compressMessageLengthThreshold: 1000,
     enableInjectSystemPrompts: true,
     template: DEFAULT_INPUT_TEMPLATE,

+ 2 - 2
app/store/prompt.ts

@@ -3,6 +3,7 @@ import { getLang } from "../locales";
 import { StoreKey } from "../constant";
 import { nanoid } from "nanoid";
 import { createPersistStore } from "../utils/store";
+import { getFullApi } from "@/app/config/client";
 
 export interface Prompt {
   id: string;
@@ -147,8 +148,7 @@ export const usePromptStore = createPersistStore(
     },
 
     onRehydrateStorage(state) {
-      const PROMPT_URL = "./prompts.json";
-
+      const PROMPT_URL = getFullApi("./prompts.json");
       type PromptList = Array<[string, string]>;
 
       fetch(PROMPT_URL)

+ 3 - 1
next.config.mjs

@@ -6,9 +6,11 @@ console.log("[Next] build mode", mode);
 const disableChunk = !!process.env.DISABLE_CHUNK || mode === "export";
 console.log("[Next] build with chunk: ", !disableChunk);
 
+const basePath = process.env.BASE_PATH ?? ''
+
 /** @type {import('next').NextConfig} */
 const nextConfig = {
-  basePath: process.env.BASE_PATH ?? '/',
+  basePath: basePath,
   webpack(config) {
     config.module.rules.push({
       test: /\.svg$/,

+ 1 - 1
package.json

@@ -25,7 +25,7 @@
     "html-to-image": "^1.11.11",
     "mermaid": "^10.6.1",
     "nanoid": "^5.0.3",
-    "next": "^13.4.9",
+    "next": "13.4.20-canary.13",
     "node-fetch": "^3.3.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",

+ 62 - 62
yarn.lock

@@ -1218,10 +1218,10 @@
     "@jridgewell/resolve-uri" "3.1.0"
     "@jridgewell/sourcemap-codec" "1.4.14"
 
-"@next/env@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.9.tgz#b77759514dd56bfa9791770755a2482f4d6ca93e"
-  integrity sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw==
+"@next/env@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/env/-/env-13.4.20-canary.13.tgz#d64e08afe21b6193afa95a3209728c35680ff9e6"
+  integrity sha512-3LZ4bfjOPh7gx+Ggx1hMMMjzdBcSgCU2d3te7dOsf8/m4XrTfYCSIfFzUHmo78/YvOhlYFg+e7Hn7s06msB8sQ==
 
 "@next/eslint-plugin-next@13.4.19":
   version "13.4.19"
@@ -1230,50 +1230,50 @@
   dependencies:
     glob "7.1.7"
 
-"@next/swc-darwin-arm64@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.9.tgz#0ed408d444bbc6b0a20f3506a9b4222684585677"
-  integrity sha512-TVzGHpZoVBk3iDsTOQA/R6MGmFp0+17SWXMEWd6zG30AfuELmSSMe2SdPqxwXU0gbpWkJL1KgfLzy5ReN0crqQ==
-
-"@next/swc-darwin-x64@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.9.tgz#a08fccdee68201522fe6618ec81f832084b222f8"
-  integrity sha512-aSfF1fhv28N2e7vrDZ6zOQ+IIthocfaxuMWGReB5GDriF0caTqtHttAvzOMgJgXQtQx6XhyaJMozLTSEXeNN+A==
-
-"@next/swc-linux-arm64-gnu@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.9.tgz#1798c2341bb841e96521433eed00892fb24abbd1"
-  integrity sha512-JhKoX5ECzYoTVyIy/7KykeO4Z2lVKq7HGQqvAH+Ip9UFn1MOJkOnkPRB7v4nmzqAoY+Je05Aj5wNABR1N18DMg==
-
-"@next/swc-linux-arm64-musl@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.9.tgz#cee04c51610eddd3638ce2499205083656531ea0"
-  integrity sha512-OOn6zZBIVkm/4j5gkPdGn4yqQt+gmXaLaSjRSO434WplV8vo2YaBNbSHaTM9wJpZTHVDYyjzuIYVEzy9/5RVZw==
-
-"@next/swc-linux-x64-gnu@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.9.tgz#1932d0367916adbc6844b244cda1d4182bd11f7a"
-  integrity sha512-iA+fJXFPpW0SwGmx/pivVU+2t4zQHNOOAr5T378PfxPHY6JtjV6/0s1vlAJUdIHeVpX98CLp9k5VuKgxiRHUpg==
-
-"@next/swc-linux-x64-musl@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.9.tgz#a66aa8c1383b16299b72482f6360facd5cde3c7a"
-  integrity sha512-rlNf2WUtMM+GAQrZ9gMNdSapkVi3koSW3a+dmBVp42lfugWVvnyzca/xJlN48/7AGx8qu62WyO0ya1ikgOxh6A==
-
-"@next/swc-win32-arm64-msvc@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.9.tgz#39482ee856c867177a612a30b6861c75e0736a4a"
-  integrity sha512-5T9ybSugXP77nw03vlgKZxD99AFTHaX8eT1ayKYYnGO9nmYhJjRPxcjU5FyYI+TdkQgEpIcH7p/guPLPR0EbKA==
-
-"@next/swc-win32-ia32-msvc@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.9.tgz#29db85e34b597ade1a918235d16a760a9213c190"
-  integrity sha512-ojZTCt1lP2ucgpoiFgrFj07uq4CZsq4crVXpLGgQfoFq00jPKRPgesuGPaz8lg1yLfvafkU3Jd1i8snKwYR3LA==
-
-"@next/swc-win32-x64-msvc@13.4.9":
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.9.tgz#0c2758164cccd61bc5a1c6cd8284fe66173e4a2b"
-  integrity sha512-QbT03FXRNdpuL+e9pLnu+XajZdm/TtIXVYY4lA9t+9l0fLZbHXDYEKitAqxrOj37o3Vx5ufxiRAniaIebYDCgw==
+"@next/swc-darwin-arm64@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.20-canary.13.tgz#131408faec0b0855adc045d7c05433d5c3a9035f"
+  integrity sha512-FrCOjIAZO8095T/QPFmRPM7szXYq4Kx4U+adFhF0H+7qayfTVVuQk03iG4tLZ4ESVnmKzTPuH5F2ZX5F6o/QVQ==
+
+"@next/swc-darwin-x64@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.20-canary.13.tgz#0e27592848825a1f0e1a6ae9da8902cf1f73d953"
+  integrity sha512-rZlPa7wlQ4gcs5Lbq6v8yTHk2adRSKBxFD7apgCPqyuM7G86AQInVb3b43ntHc0/PiUJg55rwK8pMA93nFf8pg==
+
+"@next/swc-linux-arm64-gnu@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.20-canary.13.tgz#6306c69344a96cf4bd3556058e85002c5137339a"
+  integrity sha512-icRoY+a+rB231XG8R1rtR83QyWhvH+JNShZBgu+x3CmTPMYXAnTM+qpkNrUGo1SNuS98Cv9LvWQf3roMDASMYQ==
+
+"@next/swc-linux-arm64-musl@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.20-canary.13.tgz#8367484344cdcc7523bbac3b19ae11b48377efc5"
+  integrity sha512-dASaggPDUpQYkQ7oUSKOIx9DEvmH8JBRcUwOZ8J/LF1yJp1hmGkNIGBlqsirV3LP0/9Su4MgNRV5FXjfVNEUzQ==
+
+"@next/swc-linux-x64-gnu@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.20-canary.13.tgz#3dc440dfb77b3c5f7ffb7f24541fda5a13e32fb4"
+  integrity sha512-dD6/hHkkeiM3Mjz6kDkd7ydtMdAECpcF8gUpwFoBjFrDFc4v5Bf45px56FC95I2R3vUMNzuRfus7U9QsQWdi7w==
+
+"@next/swc-linux-x64-musl@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.20-canary.13.tgz#5e96168fe8874fe2d9e40bce0d3f17df491d5642"
+  integrity sha512-eHgzJobusbRfBLbR1jcgEMO2qsU+L9prDWQsOY1uBrmlqn9qjii4rmMAWQ7bj8G4F+TpCUv/uKN2S2W5aREeVw==
+
+"@next/swc-win32-arm64-msvc@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.20-canary.13.tgz#eea17873732223090b19266b90ea2225d365f2a8"
+  integrity sha512-eq+XM3nDNxKasQI874uspQ9EbzkssgcnqXN4CQ5f+vOVvVXFA0o82YmqrSx+amBd310MdKdBnwiGWTEhjClsHw==
+
+"@next/swc-win32-ia32-msvc@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.20-canary.13.tgz#83003dc11f34f280725db48593b7a03b668c40da"
+  integrity sha512-zyL+8BvYY5vJc4EtXw4kMfoAb/YssycTB8AEaSHyv1+p64JrgUNfMT7/wzVcy1POhJVqAHtYdgh9NH2jPeUetA==
+
+"@next/swc-win32-x64-msvc@13.4.20-canary.13":
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.20-canary.13.tgz#3c219d287c4de615098f996d77a3f1a2a9ec7500"
+  integrity sha512-g7V6L60ig76+4lxq/08JnAktEWXs9ddiGIWLEhyflwaT5GN14ItbDVY/OaVK0XjN9OHM6KYZaqXa3CxQ5KNwzQ==
 
 "@nodelib/fs.scandir@2.1.5":
   version "2.1.5"
@@ -4787,12 +4787,12 @@ neo-async@^2.6.2:
   resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
   integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
 
-next@^13.4.9:
-  version "13.4.9"
-  resolved "https://registry.yarnpkg.com/next/-/next-13.4.9.tgz#473de5997cb4c5d7a4fb195f566952a1cbffbeba"
-  integrity sha512-vtefFm/BWIi/eWOqf1GsmKG3cjKw1k3LjuefKRcL3iiLl3zWzFdPG3as6xtxrGO6gwTzzaO1ktL4oiHt/uvTjA==
+next@13.4.20-canary.13:
+  version "13.4.20-canary.13"
+  resolved "https://registry.npmjs.org/next/-/next-13.4.20-canary.13.tgz#0135a7bc9e6f63ab40042f20afd56ad96c86553b"
+  integrity sha512-o25BQSItqFlks8Fzvi4FHUXUhnArIgMB6/gfAR3RgqIRBzl7oJrZqBs45W8MHWgK188ndrlzMSCeOMn0TJPF/w==
   dependencies:
-    "@next/env" "13.4.9"
+    "@next/env" "13.4.20-canary.13"
     "@swc/helpers" "0.5.1"
     busboy "1.6.0"
     caniuse-lite "^1.0.30001406"
@@ -4801,15 +4801,15 @@ next@^13.4.9:
     watchpack "2.4.0"
     zod "3.21.4"
   optionalDependencies:
-    "@next/swc-darwin-arm64" "13.4.9"
-    "@next/swc-darwin-x64" "13.4.9"
-    "@next/swc-linux-arm64-gnu" "13.4.9"
-    "@next/swc-linux-arm64-musl" "13.4.9"
-    "@next/swc-linux-x64-gnu" "13.4.9"
-    "@next/swc-linux-x64-musl" "13.4.9"
-    "@next/swc-win32-arm64-msvc" "13.4.9"
-    "@next/swc-win32-ia32-msvc" "13.4.9"
-    "@next/swc-win32-x64-msvc" "13.4.9"
+    "@next/swc-darwin-arm64" "13.4.20-canary.13"
+    "@next/swc-darwin-x64" "13.4.20-canary.13"
+    "@next/swc-linux-arm64-gnu" "13.4.20-canary.13"
+    "@next/swc-linux-arm64-musl" "13.4.20-canary.13"
+    "@next/swc-linux-x64-gnu" "13.4.20-canary.13"
+    "@next/swc-linux-x64-musl" "13.4.20-canary.13"
+    "@next/swc-win32-arm64-msvc" "13.4.20-canary.13"
+    "@next/swc-win32-ia32-msvc" "13.4.20-canary.13"
+    "@next/swc-win32-x64-msvc" "13.4.20-canary.13"
 
 node-domexception@^1.0.0:
   version "1.0.0"