Browse Source

feat: [WIP] support webdav

Yidadaa 1 year ago
parent
commit
1dd75b63de
3 changed files with 164 additions and 0 deletions
  1. 76 0
      app/components/settings.tsx
  2. 1 0
      app/constant.ts
  3. 87 0
      app/store/sync.ts

+ 76 - 0
app/components/settings.tsx

@@ -46,6 +46,7 @@ import { InputRange } from "./input-range";
 import { useNavigate } from "react-router-dom";
 import { Avatar, AvatarPicker } from "./emoji";
 import { getClientConfig } from "../config/client";
+import { useSyncStore } from "../store/sync";
 
 function EditPromptModal(props: { id: number; onClose: () => void }) {
   const promptStore = usePromptStore();
@@ -198,6 +199,78 @@ function UserPromptModal(props: { onClose?: () => void }) {
   );
 }
 
+function SyncItems() {
+  const syncStore = useSyncStore();
+  const webdav = syncStore.webDavConfig;
+
+  // not ready: https://github.com/Yidadaa/ChatGPT-Next-Web/issues/920#issuecomment-1609866332
+  return null;
+
+  return (
+    <List>
+      <ListItem
+        title={"上次同步:" + new Date().toLocaleString()}
+        subTitle={"20 次对话,100 条消息,200 提示词,20 面具"}
+      >
+        <IconButton
+          icon={<ResetIcon />}
+          text="同步"
+          onClick={() => {
+            syncStore.check().then(console.log);
+          }}
+        />
+      </ListItem>
+
+      <ListItem
+        title={"本地备份"}
+        subTitle={"20 次对话,100 条消息,200 提示词,20 面具"}
+      ></ListItem>
+
+      <ListItem
+        title={"Web Dav Server"}
+        subTitle={Locale.Settings.AccessCode.SubTitle}
+      >
+        <input
+          value={webdav.server}
+          type="text"
+          placeholder={"https://example.com"}
+          onChange={(e) => {
+            syncStore.update(
+              (config) => (config.server = e.currentTarget.value),
+            );
+          }}
+        />
+      </ListItem>
+
+      <ListItem title="Web Dav User Name" subTitle="user name here">
+        <input
+          value={webdav.username}
+          type="text"
+          placeholder={"username"}
+          onChange={(e) => {
+            syncStore.update(
+              (config) => (config.username = e.currentTarget.value),
+            );
+          }}
+        />
+      </ListItem>
+
+      <ListItem title="Web Dav Password" subTitle="password here">
+        <input
+          value={webdav.password}
+          type="text"
+          placeholder={"password"}
+          onChange={(e) => {
+            syncStore.update(
+              (config) => (config.password = e.currentTarget.value),
+            );
+          }}
+        />
+      </ListItem>
+    </List>
+  );
+}
+
 function formatVersionDate(t: string) {
   const d = new Date(+t);
   const year = d.getUTCFullYear();
@@ -556,6 +629,7 @@ export function Settings() {
               <input
                 type="text"
                 value={accessStore.openaiUrl}
+                placeholder="https://api.openai.com/"
                 onChange={(e) =>
                   accessStore.updateOpenAiUrl(e.currentTarget.value)
                 }
@@ -596,6 +670,8 @@ export function Settings() {
           </ListItem>
         </List>
 
+        <SyncItems />
+
         <List>
           <ModelConfigList
             modelConfig={config.modelConfig}

+ 1 - 0
app/constant.ts

@@ -33,6 +33,7 @@ export enum StoreKey {
   Mask = "mask-store",
   Prompt = "prompt-store",
   Update = "chat-update",
+  Sync = "sync",
 }
 
 export const MAX_SIDEBAR_WIDTH = 500;

+ 87 - 0
app/store/sync.ts

@@ -0,0 +1,87 @@
+import { Updater } from "../typing";
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+import { StoreKey } from "../constant";
+
+export interface WebDavConfig {
+  server: string;
+  username: string;
+  password: string;
+}
+
+export interface SyncStore {
+  webDavConfig: WebDavConfig;
+  lastSyncTime: number;
+
+  update: Updater<WebDavConfig>;
+  check: () => Promise<boolean>;
+
+  path: (path: string) => string;
+  headers: () => { Authorization: string };
+}
+
+const FILE = {
+  root: "/chatgpt-next-web/",
+};
+
+export const useSyncStore = create<SyncStore>()(
+  persist(
+    (set, get) => ({
+      webDavConfig: {
+        server: "",
+        username: "",
+        password: "",
+      },
+
+      lastSyncTime: 0,
+
+      update(updater) {
+        const config = { ...get().webDavConfig };
+        updater(config);
+        set({ webDavConfig: config });
+      },
+
+      async check() {
+        try {
+          const res = await fetch(this.path(""), {
+            method: "PROFIND",
+            headers: this.headers(),
+          });
+          console.log(res);
+          return res.status === 207;
+        } catch (e) {
+          console.error("[Sync] ", e);
+          return false;
+        }
+      },
+
+      path(path: string) {
+        let url = get().webDavConfig.server;
+
+        if (!url.endsWith("/")) {
+          url += "/";
+        }
+
+        if (path.startsWith("/")) {
+          path = path.slice(1);
+        }
+
+        return url + path;
+      },
+
+      headers() {
+        const auth = btoa(
+          [get().webDavConfig.username, get().webDavConfig.password].join(":"),
+        );
+
+        return {
+          Authorization: `Basic ${auth}`,
+        };
+      },
+    }),
+    {
+      name: StoreKey.Sync,
+      version: 1,
+    },
+  ),
+);