diff --git a/Dockerfile b/Dockerfile
index 7ed7bc15..7755b1a5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ WORKDIR /app
 
 COPY package.json yarn.lock ./
 
-RUN yarn config set registry 'https://registry.npm.taobao.org'
+RUN yarn config set registry 'https://registry.npmmirror.com/'
 RUN yarn install
 
 FROM base AS builder
diff --git a/README.md b/README.md
index 29d58caf..67044d80 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,9 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel.
 
 一键免费部署你的私人 ChatGPT 网页应用。
 
-[Demo](https://chat-gpt-next-web.vercel.app/) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Join Discord](https://discord.gg/zrhvHCr79N) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
+[Demo](https://chatgpt.nextweb.fun/) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Join Discord](https://discord.gg/zrhvHCr79N) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
 
-[演示](https://chat-gpt-next-web.vercel.app/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://user-images.githubusercontent.com/16968934/234462588-e8eff256-f5ca-46ef-8f5f-d7db6d28735a.jpg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
+[演示](https://chatgpt.nextweb.fun/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://user-images.githubusercontent.com/16968934/234462588-e8eff256-f5ca-46ef-8f5f-d7db6d28735a.jpg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
 
 [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
 
diff --git a/app/api/auth.ts b/app/api/auth.ts
index 9a834832..1005c5ff 100644
--- a/app/api/auth.ts
+++ b/app/api/auth.ts
@@ -1,6 +1,7 @@
 import { NextRequest } from "next/server";
 import { getServerSideConfig } from "../config/server";
 import md5 from "spark-md5";
+import { ACCESS_CODE_PREFIX } from "../constant";
 
 const serverConfig = getServerSideConfig();
 
@@ -17,10 +18,10 @@ function getIP(req: NextRequest) {
 
 function parseApiKey(bearToken: string) {
   const token = bearToken.trim().replaceAll("Bearer ", "").trim();
-  const isOpenAiKey = token.startsWith("sk-");
+  const isOpenAiKey = !token.startsWith(ACCESS_CODE_PREFIX);
 
   return {
-    accessCode: isOpenAiKey ? "" : token,
+    accessCode: isOpenAiKey ? "" : token.slice(ACCESS_CODE_PREFIX.length),
     apiKey: isOpenAiKey ? token : "",
   };
 }
diff --git a/app/api/config/route.ts b/app/api/config/route.ts
index 62b84c2e..62c8d60f 100644
--- a/app/api/config/route.ts
+++ b/app/api/config/route.ts
@@ -15,8 +15,11 @@ declare global {
   type DangerConfig = typeof DANGER_CONFIG;
 }
 
-export async function POST() {
+async function handle() {
   return NextResponse.json(DANGER_CONFIG);
 }
 
+export const GET = handle;
+export const POST = handle;
+
 export const runtime = "edge";
diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index 4173fc3a..ca51a06a 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -480,7 +480,7 @@ export function Chat() {
 
   // submit user input
   const onUserSubmit = () => {
-    if (userInput.length <= 0) return;
+    if (userInput.trim() === "") return;
     setIsLoading(true);
     chatStore.onUserInput(userInput).then(() => setIsLoading(false));
     setBeforeInput(userInput);
diff --git a/app/components/mask.tsx b/app/components/mask.tsx
index c980c48c..106418e8 100644
--- a/app/components/mask.tsx
+++ b/app/components/mask.tsx
@@ -21,7 +21,7 @@ import { useNavigate } from "react-router-dom";
 
 import chatStyle from "./chat.module.scss";
 import { useEffect, useState } from "react";
-import { downloadAs } from "../utils";
+import { downloadAs, readFromFile } from "../utils";
 import { Updater } from "../api/openai/typing";
 import { ModelConfigList } from "./model-config";
 import { FileName, Path } from "../constant";
@@ -222,6 +222,21 @@ export function MaskPage() {
     downloadAs(JSON.stringify(masks), FileName.Masks);
   };
 
+  const importFromFile = () => {
+    readFromFile().then((content) => {
+      try {
+        const importMasks = JSON.parse(content);
+        if (Array.isArray(importMasks)) {
+          for (const mask of importMasks) {
+            if (mask.name) {
+              maskStore.create(mask);
+            }
+          }
+        }
+      } catch {}
+    });
+  };
+
   return (
     <ErrorBoundary>
       <div className={styles["mask-page"]}>
@@ -247,7 +262,7 @@ export function MaskPage() {
               <IconButton
                 icon={<UploadIcon />}
                 bordered
-                onClick={() => showToast(Locale.WIP)}
+                onClick={() => importFromFile()}
               />
             </div>
             <div className="window-action-button">
@@ -371,7 +386,10 @@ export function MaskPage() {
                 key="export"
                 bordered
                 onClick={() =>
-                  downloadAs(JSON.stringify(editingMask), "mask.json")
+                  downloadAs(
+                    JSON.stringify(editingMask),
+                    `${editingMask.name}.json`,
+                  )
                 }
               />,
               <IconButton
diff --git a/app/constant.ts b/app/constant.ts
index e0ec03d0..fed20caf 100644
--- a/app/constant.ts
+++ b/app/constant.ts
@@ -36,3 +36,5 @@ export enum StoreKey {
 export const MAX_SIDEBAR_WIDTH = 500;
 export const MIN_SIDEBAR_WIDTH = 230;
 export const NARROW_SIDEBAR_WIDTH = 100;
+
+export const ACCESS_CODE_PREFIX = "ak-";
diff --git a/app/requests.ts b/app/requests.ts
index 6a155394..d38a91fd 100644
--- a/app/requests.ts
+++ b/app/requests.ts
@@ -8,6 +8,7 @@ import {
   useChatStore,
 } from "./store";
 import { showToast } from "./components/ui-lib";
+import { ACCESS_CODE_PREFIX } from "./constant";
 
 const TIME_OUT_MS = 60000;
 
@@ -44,9 +45,7 @@ const makeRequestParam = (
 
 function getHeaders() {
   const accessStore = useAccessStore.getState();
-  const headers = {
-    Authorization: "",
-  };
+  let headers: Record<string, string> = {};
 
   const makeBearer = (token: string) => `Bearer ${token.trim()}`;
   const validString = (x: string) => x && x.length > 0;
@@ -58,7 +57,9 @@ function getHeaders() {
     accessStore.enabledAccessControl() &&
     validString(accessStore.accessCode)
   ) {
-    headers.Authorization = makeBearer(accessStore.accessCode);
+    headers.Authorization = makeBearer(
+      ACCESS_CODE_PREFIX + accessStore.accessCode,
+    );
   }
 
   return headers;
diff --git a/app/store/mask.ts b/app/store/mask.ts
index b35e160c..98bd4702 100644
--- a/app/store/mask.ts
+++ b/app/store/mask.ts
@@ -57,6 +57,7 @@ export const useMaskStore = create<MaskStore>()(
           ...createEmptyMask(),
           ...mask,
           id,
+          builtin: false,
         };
 
         set(() => ({ masks }));
diff --git a/app/utils.ts b/app/utils.ts
index 95bbed2c..43ea796e 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -42,6 +42,26 @@ export function downloadAs(text: string, filename: string) {
   document.body.removeChild(element);
 }
 
+export function readFromFile() {
+  return new Promise<string>((res, rej) => {
+    const fileInput = document.createElement("input");
+    fileInput.type = "file";
+    fileInput.accept = "application/json";
+
+    fileInput.onchange = (event: any) => {
+      const file = event.target.files[0];
+      const fileReader = new FileReader();
+      fileReader.onload = (e: any) => {
+        res(e.target.result);
+      };
+      fileReader.onerror = (e) => rej(e);
+      fileReader.readAsText(file);
+    };
+
+    fileInput.click();
+  });
+}
+
 export function isIOS() {
   const userAgent = navigator.userAgent.toLowerCase();
   return /iphone|ipad|ipod/.test(userAgent);
diff --git a/next.config.mjs b/next.config.mjs
index 2a96df23..25770318 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -16,7 +16,9 @@ const nextConfig = {
       });
     }
 
-    return ret;
+    return {
+      afterFiles: ret,
+    };
   },
   webpack(config) {
     config.module.rules.push({
diff --git a/scripts/fetch-prompts.mjs b/scripts/fetch-prompts.mjs
index 02b52a31..bfcf24f3 100644
--- a/scripts/fetch-prompts.mjs
+++ b/scripts/fetch-prompts.mjs
@@ -40,7 +40,7 @@ async function fetchEN() {
     return raw
       .split("\n")
       .slice(1)
-      .map((v) => v.split('","').map((v) => v.replace('"', "")));
+      .map((v) => v.split('","').map((v) => v.replace(/^"|"$/g, '').replaceAll('""','"')));
   } catch (error) {
     console.error("[Fetch] failed to fetch en prompts", error);
     return [];