From 3767b2c7f98540934ebfb4862cc13a87b29547dc Mon Sep 17 00:00:00 2001 From: licoy Date: Mon, 15 Jul 2024 15:29:56 +0800 Subject: [PATCH 01/10] test --- app/components/sd-panel.tsx | 6 +- app/components/sd.tsx | 55 ++++++++--- app/constant.ts | 2 - app/store/sd.ts | 176 +++++++++++++++++++++--------------- 4 files changed, 149 insertions(+), 90 deletions(-) diff --git a/app/components/sd-panel.tsx b/app/components/sd-panel.tsx index d7476ccf84a..12a54c7320a 100644 --- a/app/components/sd-panel.tsx +++ b/app/components/sd-panel.tsx @@ -6,7 +6,7 @@ import locales from "@/app/locales"; import { nanoid } from "nanoid"; import { useIndexedDB } from "react-indexed-db-hook"; import { StoreKey } from "@/app/constant"; -import { SdDbInit, sendSdTask, useSdStore } from "@/app/store/sd"; +import { SdDbInit, useSdStore } from "@/app/store/sd"; SdDbInit(); @@ -287,7 +287,7 @@ export function SdPanel() { setParams(getModelParamBasicData(model.params({}), params)); }; const sdListDb = useIndexedDB(StoreKey.SdList); - const { execCountInc } = useSdStore(); + const sdStore = useSdStore(); const handleSubmit = () => { const columns = currentModel.params(params); const reqParams: any = {}; @@ -309,7 +309,7 @@ export function SdPanel() { created_at: new Date().toLocaleString(), img_data: "", }; - sendSdTask(data, sdListDb, execCountInc, () => { + sdStore.sendTask(data, sdListDb, () => { setParams(getModelParamBasicData(columns, params, true)); }); }; diff --git a/app/components/sd.tsx b/app/components/sd.tsx index cc0e326e393..02f94ae0916 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -21,7 +21,7 @@ import CopyIcon from "@/app/icons/copy.svg"; import PromptIcon from "@/app/icons/prompt.svg"; import ResetIcon from "@/app/icons/reload.svg"; import { useIndexedDB } from "react-indexed-db-hook"; -import { sendSdTask, useSdStore } from "@/app/store/sd"; +import { useSdStore } from "@/app/store/sd"; import locales from "@/app/locales"; import LoadingIcon from "../icons/three-dots.svg"; import ErrorIcon from "../icons/delete.svg"; @@ -31,6 +31,7 @@ import { showImageModal, showModal, } from "@/app/components/ui-lib"; +import { func } from "prop-types"; function getBase64ImgUrl(base64Data: string, contentType: string) { const byteCharacters = atob(base64Data); @@ -93,7 +94,7 @@ function getSdTaskStatus(item: any) { ); } -export function Sd() { +export async function Sd() { const isMobileScreen = useMobileScreen(); const navigate = useNavigate(); const clientConfig = useMemo(() => getClientConfig(), []); @@ -101,14 +102,41 @@ export function Sd() { const config = useAppConfig(); const scrollRef = useRef(null); const sdListDb = useIndexedDB(StoreKey.SdList); - const [sdImages, setSdImages] = useState([]); - const { execCount, execCountInc } = useSdStore(); + const sdStore = useSdStore(); + const [sdImages, setSdImages] = useState(sdStore.draw); useEffect(() => { - sdListDb.getAll().then((data) => { - setSdImages(((data as never[]) || []).reverse()); + setSdImages(sdStore.draw); + }, [sdStore.currentId]); + + const useIndexeddb: any = {}; + + async function getImageData(item: any) { + let id = item.img_data; + if (id.indexOf("indexeddb://")) { + id = id.replace("indexeddb://", ""); + } + const link = id.split("@"); + if (link.length != 2) { + return id; + } + let db = useIndexeddb[link[0]]; + if (!db) { + // eslint-disable-next-line react-hooks/rules-of-hooks + db = useIndexedDB(link[0]); + useIndexeddb[link[0]] = db; + } + db.getByID(link[1]).then((data: any) => { + console.log(data); + item.img = data; }); - }, [execCount]); + } + + sdImages.forEach((item: any) => { + if (item.status === "success") { + getImageData(item); + } + }); return (
@@ -161,11 +189,11 @@ export function Sd() { {item.status === "success" ? ( {`${item.id}`} { showImageModal( - getBase64ImgUrl(item.img_data, "image/png"), + getBase64ImgUrl(item.img, "image/png"), true, isMobileScreen ? { width: "100%", height: "fit-content" } @@ -258,7 +286,7 @@ export function Sd() { created_at: new Date().toLocaleString(), img_data: "", }; - sendSdTask(reqData, sdListDb, execCountInc); + sdStore.sendTask(reqData, sdListDb); }} /> { - setSdImages( - sdImages.filter( - (i: any) => i.id !== item.id, - ), + sdStore.draw = sdImages.filter( + (i: any) => i.id !== item.id, ); + sdStore.getNextId(); }, (error) => { console.error(error); diff --git a/app/constant.ts b/app/constant.ts index c51e85a150a..201843e901f 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -1,5 +1,3 @@ -import { stabilityRequestCall } from "@/app/store/sd"; - export const OWNER = "Yidadaa"; export const REPO = "ChatGPT-Next-Web"; export const REPO_URL = `https://github.com/${OWNER}/${REPO}`; diff --git a/app/store/sd.ts b/app/store/sd.ts index 970f190f816..ec0d5cfecfb 100644 --- a/app/store/sd.ts +++ b/app/store/sd.ts @@ -1,8 +1,9 @@ -import { initDB, useIndexedDB } from "react-indexed-db-hook"; +import { initDB } from "react-indexed-db-hook"; import { StabilityPath, StoreKey } from "@/app/constant"; -import { create, StoreApi } from "zustand"; import { showToast } from "@/app/components/ui-lib"; import { getHeaders } from "@/app/client/api"; +import { createPersistStore } from "@/app/utils/store"; +import { nanoid } from "nanoid"; export const SdDbConfig = { name: "@chatgpt-next-web/sd", @@ -12,16 +13,7 @@ export const SdDbConfig = { store: StoreKey.SdList, storeConfig: { keyPath: "id", autoIncrement: true }, storeSchema: [ - { name: "model", keypath: "model", options: { unique: false } }, - { - name: "model_name", - keypath: "model_name", - options: { unique: false }, - }, - { name: "status", keypath: "status", options: { unique: false } }, - { name: "params", keypath: "params", options: { unique: false } }, - { name: "img_data", keypath: "img_data", options: { unique: false } }, - { name: "error", keypath: "error", options: { unique: false } }, + { name: "data", keypath: "data", options: { unique: false } }, { name: "created_at", keypath: "created_at", @@ -36,64 +28,106 @@ export function SdDbInit() { initDB(SdDbConfig); } -type SdStore = { - execCount: number; - execCountInc: () => void; -}; - -export const useSdStore = create()((set) => ({ - execCount: 1, - execCountInc: () => set((state) => ({ execCount: state.execCount + 1 })), -})); +export const useSdStore = createPersistStore< + { + currentId: number; + draw: any[]; + }, + { + getNextId: () => number; + sendTask: (data: any, db: any, okCall?: Function) => void; + updateDraw: (draw: any) => void; + } +>( + { + currentId: 0, + draw: [], + }, + (set, _get) => { + function get() { + return { + ..._get(), + ...methods, + }; + } -export function sendSdTask(data: any, db: any, inc: any, okCall?: Function) { - db.add(data).then( - (id: number) => { - data = { ...data, id, status: "running" }; - db.update(data); - inc(); - stabilityRequestCall(data, db, inc); - okCall?.(); - }, - (error: any) => { - console.error(error); - showToast(`error: ` + error.message); - }, - ); -} + const methods = { + getNextId() { + const id = ++_get().currentId; + set({ currentId: id }); + return id; + }, + sendTask(data: any, db: any, okCall?: Function) { + data = { ...data, id: nanoid(), status: "running" }; + set({ draw: [data, ..._get().draw] }); + // db.update(data); + this.getNextId(); + this.stabilityRequestCall(data, db); + okCall?.(); + }, + stabilityRequestCall(data: any, db: any) { + const formData = new FormData(); + for (let paramsKey in data.params) { + formData.append(paramsKey, data.params[paramsKey]); + } + const headers = getHeaders(); + delete headers["Content-Type"]; + fetch(`/api/stability/${StabilityPath.GeneratePath}/${data.model}`, { + method: "POST", + headers: { + ...headers, + Accept: "application/json", + }, + body: formData, + }) + .then((response) => response.json()) + .then((resData) => { + if (resData.errors && resData.errors.length > 0) { + this.updateDraw({ + ...data, + status: "error", + error: resData.errors[0], + }); + this.getNextId(); + return; + } + if (resData.finish_reason === "SUCCESS") { + const imgId = nanoid(); + db.add({ id: data.id, data: resData.image }); + this.updateDraw({ + ...data, + status: "success", + img_data: `indexeddb://${StoreKey.SdList}@${imgId}`, + }); + } else { + this.updateDraw({ + ...data, + status: "error", + error: JSON.stringify(resData), + }); + } + this.getNextId(); + }) + .catch((error) => { + this.updateDraw({ ...data, status: "error", error: error.message }); + console.error("Error:", error); + this.getNextId(); + }); + }, + updateDraw(draw: any) { + _get().draw.some((item, index) => { + if (item.id === draw.id) { + _get().draw[index] = draw; + return true; + } + }); + }, + }; -export function stabilityRequestCall(data: any, db: any, inc: any) { - const formData = new FormData(); - for (let paramsKey in data.params) { - formData.append(paramsKey, data.params[paramsKey]); - } - const headers = getHeaders(); - delete headers["Content-Type"]; - fetch(`/api/stability/${StabilityPath.GeneratePath}/${data.model}`, { - method: "POST", - headers: { - ...headers, - Accept: "application/json", - }, - body: formData, - }) - .then((response) => response.json()) - .then((resData) => { - if (resData.errors && resData.errors.length > 0) { - db.update({ ...data, status: "error", error: resData.errors[0] }); - inc(); - return; - } - if (resData.finish_reason === "SUCCESS") { - db.update({ ...data, status: "success", img_data: resData.image }); - } else { - db.update({ ...data, status: "error", error: JSON.stringify(resData) }); - } - inc(); - }) - .catch((error) => { - db.update({ ...data, status: "error", error: error.message }); - console.error("Error:", error); - inc(); - }); -} + return methods; + }, + { + name: StoreKey.SdList, + version: 1.0, + }, +); From e2f0206d88572bad4979324b3c22d789e40854da Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 15 Jul 2024 19:29:25 +0800 Subject: [PATCH 02/10] update using indexdb read sd image data --- app/components/sd.tsx | 95 ++++++++++++++++++++++--------------------- app/store/sd.ts | 3 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/app/components/sd.tsx b/app/components/sd.tsx index 02f94ae0916..ece3e18a01c 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -94,7 +94,46 @@ function getSdTaskStatus(item: any) { ); } -export async function Sd() { +function IndexDBImage({ img_data, title, isMobileScreen }) { + const [src, setSrc] = useState(img_data); + const sdListDb = useIndexedDB(StoreKey.SdList); + const img_id = useMemo( + () => img_data.replace("indexeddb://", "").split("@").pop(), + [img_data], + ); + useEffect(() => { + sdListDb + .getByID(img_id) + .then(({ data }) => { + setSrc(data); + }) + .catch((e) => { + setSrc(img_data); + }); + }, [img_data, img_id]); + + return ( + {title} { + showImageModal( + getBase64ImgUrl(src, "image/png"), + true, + isMobileScreen + ? { width: "100%", height: "fit-content" } + : { maxWidth: "100%", maxHeight: "100%" }, + isMobileScreen + ? { width: "100%", height: "fit-content" } + : { width: "100%", height: "100%" }, + ); + }} + /> + ); +} + +export function Sd() { const isMobileScreen = useMobileScreen(); const navigate = useNavigate(); const clientConfig = useMemo(() => getClientConfig(), []); @@ -109,35 +148,6 @@ export async function Sd() { setSdImages(sdStore.draw); }, [sdStore.currentId]); - const useIndexeddb: any = {}; - - async function getImageData(item: any) { - let id = item.img_data; - if (id.indexOf("indexeddb://")) { - id = id.replace("indexeddb://", ""); - } - const link = id.split("@"); - if (link.length != 2) { - return id; - } - let db = useIndexeddb[link[0]]; - if (!db) { - // eslint-disable-next-line react-hooks/rules-of-hooks - db = useIndexedDB(link[0]); - useIndexeddb[link[0]] = db; - } - db.getByID(link[1]).then((data: any) => { - console.log(data); - item.img = data; - }); - } - - sdImages.forEach((item: any) => { - if (item.status === "success") { - getImageData(item); - } - }); - return (
@@ -187,23 +197,13 @@ export async function Sd() { className={styles["sd-img-item"]} > {item.status === "success" ? ( - {`${item.id}`} { - showImageModal( - getBase64ImgUrl(item.img, "image/png"), - true, - isMobileScreen - ? { width: "100%", height: "fit-content" } - : { maxWidth: "100%", maxHeight: "100%" }, - isMobileScreen - ? { width: "100%", height: "fit-content" } - : { width: "100%", height: "100%" }, - ); - }} - /> + <> + + ) : item.status === "error" ? (
@@ -294,6 +294,7 @@ export async function Sd() { icon={} onClick={async () => { if (await showConfirm(Locale.Sd.Danger.Delete)) { + // remove img_data + remove item in list sdListDb.deleteRecord(item.id).then( () => { sdStore.draw = sdImages.filter( diff --git a/app/store/sd.ts b/app/store/sd.ts index ec0d5cfecfb..7218926df6c 100644 --- a/app/store/sd.ts +++ b/app/store/sd.ts @@ -92,12 +92,11 @@ export const useSdStore = createPersistStore< return; } if (resData.finish_reason === "SUCCESS") { - const imgId = nanoid(); db.add({ id: data.id, data: resData.image }); this.updateDraw({ ...data, status: "success", - img_data: `indexeddb://${StoreKey.SdList}@${imgId}`, + img_data: `indexeddb://${StoreKey.SdList}@${data.id}`, }); } else { this.updateDraw({ From 33450ce429ee7b06488709d61a1afd2dae6262c1 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 15 Jul 2024 20:09:21 +0800 Subject: [PATCH 03/10] move code to utils/file --- app/components/sd-panel.tsx | 5 ++- app/components/sd.tsx | 87 +++++++++++-------------------------- app/components/ui-lib.tsx | 27 ++++++++++++ app/constant.ts | 1 + app/store/sd.ts | 26 +---------- app/utils/file.tsx | 31 +++++++++++++ 6 files changed, 88 insertions(+), 89 deletions(-) create mode 100644 app/utils/file.tsx diff --git a/app/components/sd-panel.tsx b/app/components/sd-panel.tsx index 12a54c7320a..36842c7427e 100644 --- a/app/components/sd-panel.tsx +++ b/app/components/sd-panel.tsx @@ -6,9 +6,10 @@ import locales from "@/app/locales"; import { nanoid } from "nanoid"; import { useIndexedDB } from "react-indexed-db-hook"; import { StoreKey } from "@/app/constant"; -import { SdDbInit, useSdStore } from "@/app/store/sd"; +import { useSdStore } from "@/app/store/sd"; +import { FileDbInit } from "@/app/utils/file"; -SdDbInit(); +FileDbInit(); const sdCommonParams = (model: string, data: any) => { return [ diff --git a/app/components/sd.tsx b/app/components/sd.tsx index ece3e18a01c..40e0c9abd07 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -3,7 +3,7 @@ import styles from "@/app/components/sd.module.scss"; import { IconButton } from "@/app/components/button"; import ReturnIcon from "@/app/icons/return.svg"; import Locale from "@/app/locales"; -import { Path, StoreKey } from "@/app/constant"; +import { Path } from "@/app/constant"; import React, { useEffect, useMemo, useRef, useState } from "react"; import { copyToClipboard, @@ -20,7 +20,6 @@ import DeleteIcon from "@/app/icons/clear.svg"; import CopyIcon from "@/app/icons/copy.svg"; import PromptIcon from "@/app/icons/prompt.svg"; import ResetIcon from "@/app/icons/reload.svg"; -import { useIndexedDB } from "react-indexed-db-hook"; import { useSdStore } from "@/app/store/sd"; import locales from "@/app/locales"; import LoadingIcon from "../icons/three-dots.svg"; @@ -30,19 +29,10 @@ import { showConfirm, showImageModal, showModal, + IndexDBImage, } from "@/app/components/ui-lib"; import { func } from "prop-types"; - -function getBase64ImgUrl(base64Data: string, contentType: string) { - const byteCharacters = atob(base64Data); - const byteNumbers = new Array(byteCharacters.length); - for (let i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - const byteArray = new Uint8Array(byteNumbers); - const blob = new Blob([byteArray], { type: contentType }); - return URL.createObjectURL(blob); -} +import { useFileDB } from "@/app/utils/file"; function getSdTaskStatus(item: any) { let s: string; @@ -94,45 +84,6 @@ function getSdTaskStatus(item: any) { ); } -function IndexDBImage({ img_data, title, isMobileScreen }) { - const [src, setSrc] = useState(img_data); - const sdListDb = useIndexedDB(StoreKey.SdList); - const img_id = useMemo( - () => img_data.replace("indexeddb://", "").split("@").pop(), - [img_data], - ); - useEffect(() => { - sdListDb - .getByID(img_id) - .then(({ data }) => { - setSrc(data); - }) - .catch((e) => { - setSrc(img_data); - }); - }, [img_data, img_id]); - - return ( - {title} { - showImageModal( - getBase64ImgUrl(src, "image/png"), - true, - isMobileScreen - ? { width: "100%", height: "fit-content" } - : { maxWidth: "100%", maxHeight: "100%" }, - isMobileScreen - ? { width: "100%", height: "fit-content" } - : { width: "100%", height: "100%" }, - ); - }} - /> - ); -} - export function Sd() { const isMobileScreen = useMobileScreen(); const navigate = useNavigate(); @@ -140,7 +91,7 @@ export function Sd() { const showMaxIcon = !isMobileScreen && !clientConfig?.isApp; const config = useAppConfig(); const scrollRef = useRef(null); - const sdListDb = useIndexedDB(StoreKey.SdList); + const fileDb = useFileDB(); const sdStore = useSdStore(); const [sdImages, setSdImages] = useState(sdStore.draw); @@ -197,13 +148,25 @@ export function Sd() { className={styles["sd-img-item"]} > {item.status === "success" ? ( - <> - - + { + showImageModal( + data, + true, + isMobileScreen + ? { width: "100%", height: "fit-content" } + : { maxWidth: "100%", maxHeight: "100%" }, + isMobileScreen + ? { width: "100%", height: "fit-content" } + : { width: "100%", height: "100%" }, + ); + }} + isMobileScreen={isMobileScreen} + /> ) : item.status === "error" ? (
@@ -286,7 +249,7 @@ export function Sd() { created_at: new Date().toLocaleString(), img_data: "", }; - sdStore.sendTask(reqData, sdListDb); + sdStore.sendTask(reqData, fileDb); }} /> { if (await showConfirm(Locale.Sd.Danger.Delete)) { // remove img_data + remove item in list - sdListDb.deleteRecord(item.id).then( + fileDb.deleteRecord(item.id).then( () => { sdStore.draw = sdImages.filter( (i: any) => i.id !== item.id, diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index 27555105afb..a859b969560 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -19,6 +19,7 @@ import React, { MouseEvent, useEffect, useState, + useMemo, } from "react"; import { IconButton } from "./button"; @@ -510,3 +511,29 @@ export function Selector(props: {
); } + +export function IndexDBImage({ src, alt, onClick, db, className }) { + const [data, setData] = useState(src); + const imgId = useMemo( + () => src.replace("indexeddb://", "").split("@").pop(), + [src], + ); + useEffect(() => { + db.getByID(imgId) + .then(({ data }) => { + setData(`data:image/png;base64,${data}`); + }) + .catch((e) => { + setData(src); + }); + }, [src, imgId]); + + return ( + {alt} onClick(data, e)} + /> + ); +} diff --git a/app/constant.ts b/app/constant.ts index 201843e901f..509b90ee0e5 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -55,6 +55,7 @@ export enum FileName { } export enum StoreKey { + File = "chat-next-web-file", Chat = "chat-next-web-store", Access = "access-control", Config = "app-config", diff --git a/app/store/sd.ts b/app/store/sd.ts index 7218926df6c..77b20d001f6 100644 --- a/app/store/sd.ts +++ b/app/store/sd.ts @@ -1,33 +1,9 @@ -import { initDB } from "react-indexed-db-hook"; import { StabilityPath, StoreKey } from "@/app/constant"; import { showToast } from "@/app/components/ui-lib"; import { getHeaders } from "@/app/client/api"; import { createPersistStore } from "@/app/utils/store"; import { nanoid } from "nanoid"; -export const SdDbConfig = { - name: "@chatgpt-next-web/sd", - version: 1, - objectStoresMeta: [ - { - store: StoreKey.SdList, - storeConfig: { keyPath: "id", autoIncrement: true }, - storeSchema: [ - { name: "data", keypath: "data", options: { unique: false } }, - { - name: "created_at", - keypath: "created_at", - options: { unique: false }, - }, - ], - }, - ], -}; - -export function SdDbInit() { - initDB(SdDbConfig); -} - export const useSdStore = createPersistStore< { currentId: number; @@ -96,7 +72,7 @@ export const useSdStore = createPersistStore< this.updateDraw({ ...data, status: "success", - img_data: `indexeddb://${StoreKey.SdList}@${data.id}`, + img_data: `indexeddb://${StoreKey.File}@${data.id}`, }); } else { this.updateDraw({ diff --git a/app/utils/file.tsx b/app/utils/file.tsx new file mode 100644 index 00000000000..0e77840e452 --- /dev/null +++ b/app/utils/file.tsx @@ -0,0 +1,31 @@ +"use client"; +import { initDB } from "react-indexed-db-hook"; +import { StoreKey } from "@/app/constant"; +import { useIndexedDB } from "react-indexed-db-hook"; + +export const FileDbConfig = { + name: "@chatgpt-next-web/file", + version: 1, + objectStoresMeta: [ + { + store: StoreKey.File, + storeConfig: { keyPath: "id", autoIncrement: true }, + storeSchema: [ + { name: "data", keypath: "data", options: { unique: false } }, + { + name: "created_at", + keypath: "created_at", + options: { unique: false }, + }, + ], + }, + ], +}; + +export function FileDbInit() { + initDB(FileDbConfig); +} + +export function useFileDB() { + return useIndexedDB(StoreKey.File); +} From 5df09d5e2af74a099ca65fcfab9e9f0900a28905 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 15 Jul 2024 20:26:03 +0800 Subject: [PATCH 04/10] move code to utils/file --- app/components/sd.tsx | 3 +-- app/components/ui-lib.tsx | 26 -------------------------- app/store/sd.ts | 4 ++-- app/utils/file.tsx | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/app/components/sd.tsx b/app/components/sd.tsx index 40e0c9abd07..5d520bd1ada 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -29,10 +29,9 @@ import { showConfirm, showImageModal, showModal, - IndexDBImage, } from "@/app/components/ui-lib"; import { func } from "prop-types"; -import { useFileDB } from "@/app/utils/file"; +import { useFileDB, IndexDBImage } from "@/app/utils/file"; function getSdTaskStatus(item: any) { let s: string; diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index a859b969560..ca13a621570 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -511,29 +511,3 @@ export function Selector(props: {
); } - -export function IndexDBImage({ src, alt, onClick, db, className }) { - const [data, setData] = useState(src); - const imgId = useMemo( - () => src.replace("indexeddb://", "").split("@").pop(), - [src], - ); - useEffect(() => { - db.getByID(imgId) - .then(({ data }) => { - setData(`data:image/png;base64,${data}`); - }) - .catch((e) => { - setData(src); - }); - }, [src, imgId]); - - return ( - {alt} onClick(data, e)} - /> - ); -} diff --git a/app/store/sd.ts b/app/store/sd.ts index 77b20d001f6..70d44cac313 100644 --- a/app/store/sd.ts +++ b/app/store/sd.ts @@ -3,6 +3,7 @@ import { showToast } from "@/app/components/ui-lib"; import { getHeaders } from "@/app/client/api"; import { createPersistStore } from "@/app/utils/store"; import { nanoid } from "nanoid"; +import { saveFileData } from "@/app/utils/file"; export const useSdStore = createPersistStore< { @@ -68,11 +69,10 @@ export const useSdStore = createPersistStore< return; } if (resData.finish_reason === "SUCCESS") { - db.add({ id: data.id, data: resData.image }); this.updateDraw({ ...data, status: "success", - img_data: `indexeddb://${StoreKey.File}@${data.id}`, + img_data: saveFileData(db, data.id, resData.image), }); } else { this.updateDraw({ diff --git a/app/utils/file.tsx b/app/utils/file.tsx index 0e77840e452..63039359f9c 100644 --- a/app/utils/file.tsx +++ b/app/utils/file.tsx @@ -1,4 +1,4 @@ -"use client"; +import { useState, useMemo, useEffect } from "react"; import { initDB } from "react-indexed-db-hook"; import { StoreKey } from "@/app/constant"; import { useIndexedDB } from "react-indexed-db-hook"; @@ -23,9 +23,44 @@ export const FileDbConfig = { }; export function FileDbInit() { - initDB(FileDbConfig); + if (typeof window !== "undefined") { + initDB(FileDbConfig); + } } export function useFileDB() { return useIndexedDB(StoreKey.File); } + +export function saveFileData(db, fileId, data) { + // save file content and return url start with `indexeddb://` + db.add({ id: fileId, data }); + return `indexeddb://${StoreKey.File}@${fileId}`; +} + +export async function getFileData(db, fileId, contentType = "image/png") { + const { data } = await db.getByID(fileId); + return `data:${contentType};base64,${data}`; +} + +export function IndexDBImage({ src, alt, onClick, db, className }) { + const [data, setData] = useState(src); + const imgId = useMemo( + () => src.replace("indexeddb://", "").split("@").pop(), + [src], + ); + useEffect(() => { + getFileData(db, imgId) + .then((data) => setData(data)) + .catch((e) => setData(src)); + }, [src, imgId]); + + return ( + {alt} onClick(data, e)} + /> + ); +} From 6ece818d6915cf76f549be30f222ff22d3b9254f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 15 Jul 2024 20:28:55 +0800 Subject: [PATCH 05/10] remove no need code --- app/components/ui-lib.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index ca13a621570..27555105afb 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -19,7 +19,6 @@ import React, { MouseEvent, useEffect, useState, - useMemo, } from "react"; import { IconButton } from "./button"; From a1117cd4ee0ae28ce9a965447dd0ec8d0c9ee5c0 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 15 Jul 2024 20:47:49 +0800 Subject: [PATCH 06/10] save blob to indexeddb instead of base64 image string --- app/store/sd.ts | 9 +++++++-- app/utils/file.tsx | 13 +++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/store/sd.ts b/app/store/sd.ts index 70d44cac313..842763f9f46 100644 --- a/app/store/sd.ts +++ b/app/store/sd.ts @@ -3,7 +3,7 @@ import { showToast } from "@/app/components/ui-lib"; import { getHeaders } from "@/app/client/api"; import { createPersistStore } from "@/app/utils/store"; import { nanoid } from "nanoid"; -import { saveFileData } from "@/app/utils/file"; +import { saveFileData, base64Image2Blob } from "@/app/utils/file"; export const useSdStore = createPersistStore< { @@ -72,7 +72,12 @@ export const useSdStore = createPersistStore< this.updateDraw({ ...data, status: "success", - img_data: saveFileData(db, data.id, resData.image), + // save blob to indexeddb instead of base64 image string + img_data: saveFileData( + db, + data.id, + base64Image2Blob(resData.image, "image/png"), + ), }); } else { this.updateDraw({ diff --git a/app/utils/file.tsx b/app/utils/file.tsx index 63039359f9c..52a258edf83 100644 --- a/app/utils/file.tsx +++ b/app/utils/file.tsx @@ -32,6 +32,16 @@ export function useFileDB() { return useIndexedDB(StoreKey.File); } +export function base64Image2Blob(base64Data: string, contentType: string) { + const byteCharacters = atob(base64Data); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + return new Blob([byteArray], { type: contentType }); +} + export function saveFileData(db, fileId, data) { // save file content and return url start with `indexeddb://` db.add({ id: fileId, data }); @@ -40,6 +50,9 @@ export function saveFileData(db, fileId, data) { export async function getFileData(db, fileId, contentType = "image/png") { const { data } = await db.getByID(fileId); + if (typeof data == "object") { + return URL.createObjectURL(data); + } return `data:${contentType};base64,${data}`; } From b3a324b6f5fe84ab5f0e1774e288d834d23e7500 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Mon, 15 Jul 2024 21:21:50 +0800 Subject: [PATCH 07/10] fixed typescript error --- app/components/sd.tsx | 3 +-- app/page.tsx | 1 - app/utils/file.tsx | 24 ++++++++++++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/components/sd.tsx b/app/components/sd.tsx index 5d520bd1ada..dfbe640d611 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -152,7 +152,7 @@ export function Sd() { db={fileDb} src={item.img_data} alt={item.id} - onClick={(data, e) => { + onClick={(data: any, e: any) => { showImageModal( data, true, @@ -164,7 +164,6 @@ export function Sd() { : { width: "100%", height: "100%" }, ); }} - isMobileScreen={isMobileScreen} /> ) : item.status === "error" ? (
diff --git a/app/page.tsx b/app/page.tsx index 6d24b015438..b3f169a9b74 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,7 +3,6 @@ import { Analytics } from "@vercel/analytics/react"; import { Home } from "./components/home"; import { getServerSideConfig } from "./config/server"; -import { SdDbInit } from "@/app/store/sd"; const serverConfig = getServerSideConfig(); diff --git a/app/utils/file.tsx b/app/utils/file.tsx index 52a258edf83..b9b697f5680 100644 --- a/app/utils/file.tsx +++ b/app/utils/file.tsx @@ -42,13 +42,17 @@ export function base64Image2Blob(base64Data: string, contentType: string) { return new Blob([byteArray], { type: contentType }); } -export function saveFileData(db, fileId, data) { +export function saveFileData(db: any, fileId: string, data: Blob | string) { // save file content and return url start with `indexeddb://` db.add({ id: fileId, data }); return `indexeddb://${StoreKey.File}@${fileId}`; } -export async function getFileData(db, fileId, contentType = "image/png") { +export async function getFileData( + db: any, + fileId: string, + contentType = "image/png", +) { const { data } = await db.getByID(fileId); if (typeof data == "object") { return URL.createObjectURL(data); @@ -56,14 +60,26 @@ export async function getFileData(db, fileId, contentType = "image/png") { return `data:${contentType};base64,${data}`; } -export function IndexDBImage({ src, alt, onClick, db, className }) { +export function IndexDBImage({ + src, + alt, + onClick, + db, + className, +}: { + src: string; + alt: string; + onClick: any; + db: any; + className: string; +}) { const [data, setData] = useState(src); const imgId = useMemo( () => src.replace("indexeddb://", "").split("@").pop(), [src], ); useEffect(() => { - getFileData(db, imgId) + getFileData(db, imgId as string) .then((data) => setData(data)) .catch((e) => setData(src)); }, [src, imgId]); From bab3e0bc9bf72f41f905add1b9140960d345d3db Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 16 Jul 2024 01:19:40 +0800 Subject: [PATCH 08/10] using CacheStorage to store image #5013 --- app/components/home.tsx | 1 - app/components/sd-panel.tsx | 7 +-- app/components/sd.tsx | 19 ++++---- app/constant.ts | 2 + app/store/sd.ts | 51 ++++++++++++-------- app/utils/chat.ts | 38 +++++++++++++++ app/utils/file.tsx | 95 ------------------------------------- package.json | 1 - public/serviceWorker.js | 37 ++++++++++++++- yarn.lock | 5 -- 10 files changed, 115 insertions(+), 141 deletions(-) delete mode 100644 app/utils/file.tsx diff --git a/app/components/home.tsx b/app/components/home.tsx index 875f37e5445..aacd2326480 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -29,7 +29,6 @@ import { AuthPage } from "./auth"; import { getClientConfig } from "../config/client"; import { type ClientApi, getClientApi } from "../client/api"; import { useAccessStore } from "../store"; -import { initDB } from "react-indexed-db-hook"; export function Loading(props: { noLogo?: boolean }) { return ( diff --git a/app/components/sd-panel.tsx b/app/components/sd-panel.tsx index 36842c7427e..c6b28f22157 100644 --- a/app/components/sd-panel.tsx +++ b/app/components/sd-panel.tsx @@ -4,12 +4,8 @@ import { Select, showToast } from "@/app/components/ui-lib"; import { IconButton } from "@/app/components/button"; import locales from "@/app/locales"; import { nanoid } from "nanoid"; -import { useIndexedDB } from "react-indexed-db-hook"; import { StoreKey } from "@/app/constant"; import { useSdStore } from "@/app/store/sd"; -import { FileDbInit } from "@/app/utils/file"; - -FileDbInit(); const sdCommonParams = (model: string, data: any) => { return [ @@ -287,7 +283,6 @@ export function SdPanel() { setCurrentModel(model); setParams(getModelParamBasicData(model.params({}), params)); }; - const sdListDb = useIndexedDB(StoreKey.SdList); const sdStore = useSdStore(); const handleSubmit = () => { const columns = currentModel.params(params); @@ -310,7 +305,7 @@ export function SdPanel() { created_at: new Date().toLocaleString(), img_data: "", }; - sdStore.sendTask(data, sdListDb, () => { + sdStore.sendTask(data, () => { setParams(getModelParamBasicData(columns, params, true)); }); }; diff --git a/app/components/sd.tsx b/app/components/sd.tsx index dfbe640d611..e06ee168324 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -30,8 +30,7 @@ import { showImageModal, showModal, } from "@/app/components/ui-lib"; -import { func } from "prop-types"; -import { useFileDB, IndexDBImage } from "@/app/utils/file"; +import { removeImage } from "@/app/utils/chat"; function getSdTaskStatus(item: any) { let s: string; @@ -90,7 +89,6 @@ export function Sd() { const showMaxIcon = !isMobileScreen && !clientConfig?.isApp; const config = useAppConfig(); const scrollRef = useRef(null); - const fileDb = useFileDB(); const sdStore = useSdStore(); const [sdImages, setSdImages] = useState(sdStore.draw); @@ -147,14 +145,13 @@ export function Sd() { className={styles["sd-img-item"]} > {item.status === "success" ? ( - { + onClick={(e) => showImageModal( - data, + item.img_data, true, isMobileScreen ? { width: "100%", height: "fit-content" } @@ -162,8 +159,8 @@ export function Sd() { isMobileScreen ? { width: "100%", height: "fit-content" } : { width: "100%", height: "100%" }, - ); - }} + ) + } /> ) : item.status === "error" ? (
@@ -247,7 +244,7 @@ export function Sd() { created_at: new Date().toLocaleString(), img_data: "", }; - sdStore.sendTask(reqData, fileDb); + sdStore.sendTask(reqData); }} /> { if (await showConfirm(Locale.Sd.Danger.Delete)) { // remove img_data + remove item in list - fileDb.deleteRecord(item.id).then( + removeImage(item.img_data).finally( () => { sdStore.draw = sdImages.filter( (i: any) => i.id !== item.id, diff --git a/app/constant.ts b/app/constant.ts index 509b90ee0e5..d50db15f911 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -23,6 +23,8 @@ export const BYTEDANCE_BASE_URL = "https://ark.cn-beijing.volces.com"; export const ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/api/"; +export const UPLOAD_URL = "/api/cache/upload"; + export enum Path { Home = "/", Chat = "/chat", diff --git a/app/store/sd.ts b/app/store/sd.ts index 842763f9f46..d40c53a8525 100644 --- a/app/store/sd.ts +++ b/app/store/sd.ts @@ -3,7 +3,7 @@ import { showToast } from "@/app/components/ui-lib"; import { getHeaders } from "@/app/client/api"; import { createPersistStore } from "@/app/utils/store"; import { nanoid } from "nanoid"; -import { saveFileData, base64Image2Blob } from "@/app/utils/file"; +import { uploadImage, base64Image2Blob } from "@/app/utils/chat"; export const useSdStore = createPersistStore< { @@ -12,7 +12,7 @@ export const useSdStore = createPersistStore< }, { getNextId: () => number; - sendTask: (data: any, db: any, okCall?: Function) => void; + sendTask: (data: any, okCall?: Function) => void; updateDraw: (draw: any) => void; } >( @@ -34,15 +34,14 @@ export const useSdStore = createPersistStore< set({ currentId: id }); return id; }, - sendTask(data: any, db: any, okCall?: Function) { + sendTask(data: any, okCall?: Function) { data = { ...data, id: nanoid(), status: "running" }; set({ draw: [data, ..._get().draw] }); - // db.update(data); this.getNextId(); - this.stabilityRequestCall(data, db); + this.stabilityRequestCall(data); okCall?.(); }, - stabilityRequestCall(data: any, db: any) { + stabilityRequestCall(data: any) { const formData = new FormData(); for (let paramsKey in data.params) { formData.append(paramsKey, data.params[paramsKey]); @@ -69,18 +68,26 @@ export const useSdStore = createPersistStore< return; } if (resData.finish_reason === "SUCCESS") { - this.updateDraw({ - ...data, - status: "success", - // save blob to indexeddb instead of base64 image string - img_data: saveFileData( - db, - data.id, - base64Image2Blob(resData.image, "image/png"), - ), - }); + const self = this; + uploadImage(base64Image2Blob(resData.image, "image/png")) + .then((img_data) => { + console.debug("uploadImage success", img_data, self); + self.updateDraw({ + ...data, + status: "success", + img_data, + }); + }) + .catch((e) => { + console.error("uploadImage error", e); + self.updateDraw({ + ...data, + status: "error", + error: JSON.stringify(resData), + }); + }); } else { - this.updateDraw({ + self.updateDraw({ ...data, status: "error", error: JSON.stringify(resData), @@ -94,10 +101,12 @@ export const useSdStore = createPersistStore< this.getNextId(); }); }, - updateDraw(draw: any) { - _get().draw.some((item, index) => { - if (item.id === draw.id) { - _get().draw[index] = draw; + updateDraw(_draw: any) { + const draw = _get().draw || []; + draw.some((item, index) => { + if (item.id === _draw.id) { + draw[index] = _draw; + set(() => ({ draw })); return true; } }); diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 991d06b7320..55127f50e75 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -1,3 +1,4 @@ +import { UPLOAD_URL } from "@/app/constant"; import heic2any from "heic2any"; export function compressImage(file: File, maxSize: number): Promise { @@ -52,3 +53,40 @@ export function compressImage(file: File, maxSize: number): Promise { reader.readAsDataURL(file); }); } + +export function base64Image2Blob(base64Data: string, contentType: string) { + const byteCharacters = atob(base64Data); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + return new Blob([byteArray], { type: contentType }); +} + +export function uploadImage(file: File): Promise { + const body = new FormData(); + body.append("file", file); + return fetch(UPLOAD_URL, { + method: "post", + body, + mode: "cors", + credentials: "include", + }) + .then((res) => res.json()) + .then((res) => { + console.log("res", res); + if (res?.code == 0 && res?.data) { + return res?.data; + } + throw Error(`upload Error: ${res?.msg}`); + }); +} + +export function removeImage(imageUrl: string) { + return fetch(imageUrl, { + method: "DELETE", + mode: "cors", + credentials: "include", + }); +} diff --git a/app/utils/file.tsx b/app/utils/file.tsx deleted file mode 100644 index b9b697f5680..00000000000 --- a/app/utils/file.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useState, useMemo, useEffect } from "react"; -import { initDB } from "react-indexed-db-hook"; -import { StoreKey } from "@/app/constant"; -import { useIndexedDB } from "react-indexed-db-hook"; - -export const FileDbConfig = { - name: "@chatgpt-next-web/file", - version: 1, - objectStoresMeta: [ - { - store: StoreKey.File, - storeConfig: { keyPath: "id", autoIncrement: true }, - storeSchema: [ - { name: "data", keypath: "data", options: { unique: false } }, - { - name: "created_at", - keypath: "created_at", - options: { unique: false }, - }, - ], - }, - ], -}; - -export function FileDbInit() { - if (typeof window !== "undefined") { - initDB(FileDbConfig); - } -} - -export function useFileDB() { - return useIndexedDB(StoreKey.File); -} - -export function base64Image2Blob(base64Data: string, contentType: string) { - const byteCharacters = atob(base64Data); - const byteNumbers = new Array(byteCharacters.length); - for (let i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - const byteArray = new Uint8Array(byteNumbers); - return new Blob([byteArray], { type: contentType }); -} - -export function saveFileData(db: any, fileId: string, data: Blob | string) { - // save file content and return url start with `indexeddb://` - db.add({ id: fileId, data }); - return `indexeddb://${StoreKey.File}@${fileId}`; -} - -export async function getFileData( - db: any, - fileId: string, - contentType = "image/png", -) { - const { data } = await db.getByID(fileId); - if (typeof data == "object") { - return URL.createObjectURL(data); - } - return `data:${contentType};base64,${data}`; -} - -export function IndexDBImage({ - src, - alt, - onClick, - db, - className, -}: { - src: string; - alt: string; - onClick: any; - db: any; - className: string; -}) { - const [data, setData] = useState(src); - const imgId = useMemo( - () => src.replace("indexeddb://", "").split("@").pop(), - [src], - ); - useEffect(() => { - getFileData(db, imgId as string) - .then((data) => setData(data)) - .catch((e) => setData(src)); - }, [src, imgId]); - - return ( - {alt} onClick(data, e)} - /> - ); -} diff --git a/package.json b/package.json index 15ae9b69987..ed5edb04330 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "node-fetch": "^3.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-indexed-db-hook": "^1.0.14", "react-markdown": "^8.0.7", "react-router-dom": "^6.15.0", "rehype-highlight": "^6.0.0", diff --git a/public/serviceWorker.js b/public/serviceWorker.js index f5a24b70176..b9b80c319ef 100644 --- a/public/serviceWorker.js +++ b/public/serviceWorker.js @@ -1,4 +1,6 @@ const CHATGPT_NEXT_WEB_CACHE = "chatgpt-next-web-cache"; +const CHATGPT_NEXT_WEB_FILE_CACHE = "chatgpt-next-web-file"; +let a="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let nanoid=(e=21)=>{let t="",r=crypto.getRandomValues(new Uint8Array(e));for(let n=0;n {}); +async function upload(request, url) { + const formData = await request.formData() + const file = formData.getAll('file')[0] + let ext = file.name.split('.').pop() + if (ext === 'blob') { + ext = file.type.split('/').pop() + } + const fileUrl = `${url.origin}/api/cache/${nanoid()}.${ext}` + // console.debug('file', file, fileUrl) + const cache = await caches.open(CHATGPT_NEXT_WEB_FILE_CACHE) + await cache.put(new Request(fileUrl), new Response(file)) + return Response.json({ code: 0, data: fileUrl }) +} + +async function remove(request, url) { + const cache = await caches.open(CHATGPT_NEXT_WEB_FILE_CACHE) + const res = await cache.delete(request.url) + return Response.json({ code: 0 }) +} + +self.addEventListener("fetch", (e) => { + const url = new URL(e.request.url); + if (/^\/api\/cache/.test(url.pathname)) { + if ('GET' == e.request.method) { + e.respondWith(caches.match(e.request)) + } + if ('POST' == e.request.method) { + e.respondWith(upload(e.request, url)) + } + if ('DELETE' == e.request.method) { + e.respondWith(remove(e.request, url)) + } + } +}); diff --git a/yarn.lock b/yarn.lock index af46f1feac1..c323a5c38db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5278,11 +5278,6 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-indexed-db-hook@^1.0.14: - version "1.0.14" - resolved "https://registry.npmmirror.com/react-indexed-db-hook/-/react-indexed-db-hook-1.0.14.tgz#a29cd732d592735b6a68dfc94316b7a4a091e6be" - integrity sha512-tQ6rWofgXUCBhZp9pRpWzthzPbjqcll5uXMo07lbQTKl47VyL9nw9wfVswRxxzS5yj5Sq/VHUkNUjamWbA/M/w== - react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" From 94bc880b7f2a4a96b425b13d092e37c56510f071 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 16 Jul 2024 01:45:15 +0800 Subject: [PATCH 09/10] fixed typescript error --- app/components/sd.tsx | 17 ++++++----------- app/utils/chat.ts | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/components/sd.tsx b/app/components/sd.tsx index e06ee168324..19684c24088 100644 --- a/app/components/sd.tsx +++ b/app/components/sd.tsx @@ -253,17 +253,12 @@ export function Sd() { onClick={async () => { if (await showConfirm(Locale.Sd.Danger.Delete)) { // remove img_data + remove item in list - removeImage(item.img_data).finally( - () => { - sdStore.draw = sdImages.filter( - (i: any) => i.id !== item.id, - ); - sdStore.getNextId(); - }, - (error) => { - console.error(error); - }, - ); + removeImage(item.img_data).finally(() => { + sdStore.draw = sdImages.filter( + (i: any) => i.id !== item.id, + ); + sdStore.getNextId(); + }); } }} /> diff --git a/app/utils/chat.ts b/app/utils/chat.ts index 55127f50e75..be9908906ce 100644 --- a/app/utils/chat.ts +++ b/app/utils/chat.ts @@ -64,7 +64,7 @@ export function base64Image2Blob(base64Data: string, contentType: string) { return new Blob([byteArray], { type: contentType }); } -export function uploadImage(file: File): Promise { +export function uploadImage(file: Blob): Promise { const body = new FormData(); body.append("file", file); return fetch(UPLOAD_URL, { From 5267ad46daa41a546f94fe5303a85424e29fb1d5 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Tue, 16 Jul 2024 11:51:43 +0800 Subject: [PATCH 10/10] add header for service worker upload api --- public/serviceWorker.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/public/serviceWorker.js b/public/serviceWorker.js index b9b80c319ef..154f2247e9b 100644 --- a/public/serviceWorker.js +++ b/public/serviceWorker.js @@ -22,9 +22,16 @@ async function upload(request, url) { ext = file.type.split('/').pop() } const fileUrl = `${url.origin}/api/cache/${nanoid()}.${ext}` - // console.debug('file', file, fileUrl) + // console.debug('file', file, fileUrl, request) const cache = await caches.open(CHATGPT_NEXT_WEB_FILE_CACHE) - await cache.put(new Request(fileUrl), new Response(file)) + await cache.put(new Request(fileUrl), new Response(file, { + headers: { + 'content-type': file.type, + 'content-length': file.size, + 'cache-control': 'no-cache', // file already store in disk + 'server': 'ServiceWorker', + } + })) return Response.json({ code: 0, data: fileUrl }) }