mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-02-06 05:30:06 +00:00
optimize fm (#82)
* optimize fm * chore: auto-fix linting and formatting issues * fix: type * chore: auto-fix linting and formatting issues --------- Co-authored-by: uubulb <uubulb@users.noreply.github.com> Co-authored-by: naiba <hi@nai.ba> Co-authored-by: naiba <naiba@users.noreply.github.com>
This commit is contained in:
@@ -7,27 +7,45 @@ export enum Oauth2RequestType {
|
|||||||
BIND = 2,
|
BIND = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getOauth2RedirectURL = async (provider: string, rType: Oauth2RequestType): Promise<ModelOauth2LoginResponse> => {
|
export const getOauth2RedirectURL = async (
|
||||||
|
provider: string,
|
||||||
|
rType: Oauth2RequestType,
|
||||||
|
): Promise<ModelOauth2LoginResponse> => {
|
||||||
|
const sType = "type"
|
||||||
return fetcher<ModelOauth2LoginResponse>(FetcherMethod.GET, `/api/v1/oauth2/${provider}`, {
|
return fetcher<ModelOauth2LoginResponse>(FetcherMethod.GET, `/api/v1/oauth2/${provider}`, {
|
||||||
"type": rType,
|
sType: rType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const bindOauth2 = async (
|
||||||
export const bindOauth2 = async (provider: string, state: string, code: string): Promise<ModelOauth2LoginResponse> => {
|
provider: string,
|
||||||
return fetcher<ModelOauth2LoginResponse>(FetcherMethod.POST, `/api/v1/oauth2/${provider}/bind`, {
|
state: string,
|
||||||
"state": state,
|
code: string,
|
||||||
"code": code,
|
): Promise<ModelOauth2LoginResponse> => {
|
||||||
})
|
return fetcher<ModelOauth2LoginResponse>(
|
||||||
|
FetcherMethod.POST,
|
||||||
|
`/api/v1/oauth2/${provider}/bind`,
|
||||||
|
{
|
||||||
|
state: state,
|
||||||
|
code: code,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const unbindOauth2 = async (provider: string): Promise<ModelOauth2LoginResponse> => {
|
export const unbindOauth2 = async (provider: string): Promise<ModelOauth2LoginResponse> => {
|
||||||
return fetcher<ModelOauth2LoginResponse>(FetcherMethod.POST, `/api/v1/oauth2/${provider}/unbind`)
|
return fetcher<ModelOauth2LoginResponse>(
|
||||||
|
FetcherMethod.POST,
|
||||||
|
`/api/v1/oauth2/${provider}/unbind`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const oauth2callback = async (
|
||||||
export const oauth2callback = async (provider: string, state: string, code: string): Promise<void> => {
|
provider: string,
|
||||||
|
state: string,
|
||||||
|
code: string,
|
||||||
|
): Promise<void> => {
|
||||||
return fetcher<void>(FetcherMethod.POST, `/api/v1/oauth2/${provider}/callback`, {
|
return fetcher<void>(FetcherMethod.POST, `/api/v1/oauth2/${provider}/callback`, {
|
||||||
state, code
|
state,
|
||||||
|
code,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import { HTMLAttributes, useEffect, useRef, useState } from "react"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
|
import { Button } from "./ui/button"
|
||||||
import { TableCell, TableRow } from "./ui/table"
|
import { TableCell, TableRow } from "./ui/table"
|
||||||
import { Filepath } from "./xui/filepath"
|
import { Filepath } from "./xui/filepath"
|
||||||
import { IconButton } from "./xui/icon-button"
|
import { IconButton } from "./xui/icon-button"
|
||||||
@@ -102,7 +103,7 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
|||||||
header: () => <span>{t("Actions")}</span>,
|
header: () => <span>{t("Actions")}</span>,
|
||||||
id: "download",
|
id: "download",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return row.original.type == 0 ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
icon="download"
|
icon="download"
|
||||||
@@ -111,6 +112,8 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
|||||||
downloadFile(row.original.name)
|
downloadFile(row.original.name)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<Button size="icon" variant="ghost" className="pointer-events-none" />
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -157,6 +160,70 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const ws = new WebSocket(wsUrl)
|
||||||
|
wsRef.current = ws
|
||||||
|
ws.binaryType = "arraybuffer"
|
||||||
|
ws.onopen = () => {
|
||||||
|
listFile()
|
||||||
|
}
|
||||||
|
ws.onclose = (e) => {
|
||||||
|
console.log("WebSocket connection closed:", e)
|
||||||
|
}
|
||||||
|
ws.onerror = (e) => {
|
||||||
|
console.error(e)
|
||||||
|
toast("Websocket" + " " + t("Error"), {
|
||||||
|
description: t("Results.UnExpectedError"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ws.onmessage = async (e: MessageEvent<ArrayBufferLike>) => {
|
||||||
|
try {
|
||||||
|
const identifier = new Uint8Array(e.data, 0, 4)
|
||||||
|
if (arraysEqual(identifier, FMIdentifier.error)) {
|
||||||
|
const errBytes = e.data.slice(4)
|
||||||
|
const errMsg = new TextDecoder("utf-8").decode(errBytes)
|
||||||
|
throw new Error(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstChunk.current) {
|
||||||
|
if (arraysEqual(identifier, FMIdentifier.file)) {
|
||||||
|
worker.postMessage({
|
||||||
|
operation: 1,
|
||||||
|
arrayBuffer: e.data,
|
||||||
|
fileName: currentBasename.current,
|
||||||
|
})
|
||||||
|
firstChunk.current = false
|
||||||
|
} else if (arraysEqual(identifier, FMIdentifier.fileName)) {
|
||||||
|
const { path, fmList } = await fm.parseFMList(e.data)
|
||||||
|
setPath(path)
|
||||||
|
setFMEntries(fmList)
|
||||||
|
} else if (arraysEqual(identifier, FMIdentifier.complete)) {
|
||||||
|
// Upload completed
|
||||||
|
if (uOpen) setuOpen(false)
|
||||||
|
listFile()
|
||||||
|
} else {
|
||||||
|
throw new Error(t("Results.UnknownIdentifier"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await waitForHandleReady()
|
||||||
|
worker.postMessage({
|
||||||
|
operation: 2,
|
||||||
|
arrayBuffer: e.data,
|
||||||
|
fileName: currentBasename.current,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing received data:", error)
|
||||||
|
toast("FM" + " " + t("Error"), {
|
||||||
|
description: t("Results.UnExpectedError"),
|
||||||
|
})
|
||||||
|
setdOpen(false)
|
||||||
|
setuOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [wsUrl])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
worker.onmessage = async (event: MessageEvent<FMWorkerData>) => {
|
worker.onmessage = async (event: MessageEvent<FMWorkerData>) => {
|
||||||
switch (event.data.type) {
|
switch (event.data.type) {
|
||||||
case FMWorkerOpcode.Error: {
|
case FMWorkerOpcode.Error: {
|
||||||
@@ -186,6 +253,18 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBeforeUnload = () => {
|
||||||
|
worker.postMessage({
|
||||||
|
operation: 3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", handleBeforeUnload)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("beforeunload", handleBeforeUnload)
|
||||||
|
}
|
||||||
|
}, [worker, dOpen])
|
||||||
|
|
||||||
const [currentPath, setPath] = useState("")
|
const [currentPath, setPath] = useState("")
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
||||||
@@ -193,69 +272,6 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
|||||||
}
|
}
|
||||||
}, [wsRef.current, currentPath])
|
}, [wsRef.current, currentPath])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const ws = new WebSocket(wsUrl)
|
|
||||||
wsRef.current = ws
|
|
||||||
ws.binaryType = "arraybuffer"
|
|
||||||
ws.onopen = () => {
|
|
||||||
listFile()
|
|
||||||
}
|
|
||||||
ws.onclose = (e) => {
|
|
||||||
console.log("WebSocket connection closed:", e)
|
|
||||||
}
|
|
||||||
ws.onerror = (e) => {
|
|
||||||
console.error(e)
|
|
||||||
toast("Websocket" + " " + t("Error"), {
|
|
||||||
description: t("Results.UnExpectedError"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ws.onmessage = async (e) => {
|
|
||||||
try {
|
|
||||||
const buf: ArrayBufferLike = e.data
|
|
||||||
|
|
||||||
if (firstChunk.current) {
|
|
||||||
const identifier = new Uint8Array(buf, 0, 4)
|
|
||||||
if (arraysEqual(identifier, FMIdentifier.file)) {
|
|
||||||
worker.postMessage({
|
|
||||||
operation: 1,
|
|
||||||
arrayBuffer: buf,
|
|
||||||
fileName: currentBasename.current,
|
|
||||||
})
|
|
||||||
firstChunk.current = false
|
|
||||||
} else if (arraysEqual(identifier, FMIdentifier.fileName)) {
|
|
||||||
const { path, fmList } = await fm.parseFMList(buf)
|
|
||||||
setPath(path)
|
|
||||||
setFMEntries(fmList)
|
|
||||||
} else if (arraysEqual(identifier, FMIdentifier.error)) {
|
|
||||||
const errBytes = buf.slice(4)
|
|
||||||
const errMsg = new TextDecoder("utf-8").decode(errBytes)
|
|
||||||
throw new Error(errMsg)
|
|
||||||
} else if (arraysEqual(identifier, FMIdentifier.complete)) {
|
|
||||||
// Upload completed
|
|
||||||
if (uOpen) setuOpen(false)
|
|
||||||
listFile()
|
|
||||||
} else {
|
|
||||||
throw new Error(t("Results.UnknownIdentifier"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await waitForHandleReady()
|
|
||||||
worker.postMessage({
|
|
||||||
operation: 2,
|
|
||||||
arrayBuffer: buf,
|
|
||||||
fileName: currentBasename.current,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error processing received data:", error)
|
|
||||||
toast("FM" + " " + t("Error"), {
|
|
||||||
description: t("Results.UnExpectedError"),
|
|
||||||
})
|
|
||||||
if (dOpen) setdOpen(false)
|
|
||||||
if (uOpen) setuOpen(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [wsUrl])
|
|
||||||
|
|
||||||
const listFile = () => {
|
const listFile = () => {
|
||||||
const prefix = new Int8Array([FMOpcode.List])
|
const prefix = new Int8Array([FMOpcode.List])
|
||||||
const pathMsg = new TextEncoder().encode(currentPath)
|
const pathMsg = new TextEncoder().encode(currentPath)
|
||||||
@@ -317,7 +333,7 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
|||||||
toast("FM" + " " + t("Error"), {
|
toast("FM" + " " + t("Error"), {
|
||||||
description: error.message,
|
description: error.message,
|
||||||
})
|
})
|
||||||
console.log("copy error: ", error)
|
console.error("copy error: ", error)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
import { useAuth } from "@/hooks/useAuth"
|
import { useAuth } from "@/hooks/useAuth"
|
||||||
import useSettings from "@/hooks/useSetting"
|
import useSettings from "@/hooks/useSetting"
|
||||||
import { copyToClipboard } from "@/lib/utils"
|
import { copyToClipboard } from "@/lib/utils"
|
||||||
import { ModelProfile, ModelConfig } from "@/types"
|
import { ModelConfig, ModelProfile } from "@/types"
|
||||||
import i18next from "i18next"
|
import i18next from "i18next"
|
||||||
import { Check, Clipboard } from "lucide-react"
|
import { Check, Clipboard } from "lucide-react"
|
||||||
import { forwardRef, useState } from "react"
|
import { forwardRef, useState } from "react"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const Filepath: React.FC<FilepathProps> = ({ path, setPath }) => {
|
|||||||
setPath("/")
|
setPath("/")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{"/"}
|
/
|
||||||
</p>
|
</p>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
<BreadcrumbSeparator />
|
<BreadcrumbSeparator />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { oauth2callback } from "@/api/oauth2"
|
||||||
import { getProfile, login as loginRequest } from "@/api/user"
|
import { getProfile, login as loginRequest } from "@/api/user"
|
||||||
import { AuthContextProps } from "@/types"
|
import { AuthContextProps } from "@/types"
|
||||||
import { createContext, useContext, useEffect, useMemo } from "react"
|
import { createContext, useContext, useEffect, useMemo } from "react"
|
||||||
@@ -5,7 +6,6 @@ import { useNavigate } from "react-router-dom"
|
|||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
import { useMainStore } from "./useMainStore"
|
import { useMainStore } from "./useMainStore"
|
||||||
import { oauth2callback } from "@/api/oauth2"
|
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextProps>({
|
const AuthContext = createContext<AuthContextProps>({
|
||||||
profile: undefined,
|
profile: undefined,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { GithubComNezhahqNezhaModelSettingResponseModelConfig } from "@/types"
|
|||||||
import useSWR from "swr"
|
import useSWR from "swr"
|
||||||
|
|
||||||
export default function useSetting() {
|
export default function useSetting() {
|
||||||
return useSWR<GithubComNezhahqNezhaModelSettingResponseModelConfig>("/api/v1/setting", swrFetcher)
|
return useSWR<GithubComNezhahqNezhaModelSettingResponseModelConfig>(
|
||||||
|
"/api/v1/setting",
|
||||||
|
swrFetcher,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ onmessage = async function (event) {
|
|||||||
throw new Error("accessHandle is undefined")
|
throw new Error("accessHandle is undefined")
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataChunk = arrayBuffer
|
accessHandle.write(arrayBuffer, { at: receivedLength })
|
||||||
accessHandle.write(dataChunk, { at: receivedLength })
|
receivedLength += arrayBuffer.byteLength
|
||||||
receivedLength += dataChunk.byteLength
|
|
||||||
|
|
||||||
if (receivedLength === expectedLength) {
|
if (receivedLength === expectedLength) {
|
||||||
accessHandle.flush()
|
accessHandle.flush()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getOauth2RedirectURL, Oauth2RequestType } from "@/api/oauth2"
|
import { Oauth2RequestType, getOauth2RedirectURL } from "@/api/oauth2"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
@@ -103,9 +103,9 @@ function Login() {
|
|||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
{settingData?.config?.oauth2_providers?.map((p: string) =>
|
{settingData?.config?.oauth2_providers?.map((p: string) => (
|
||||||
<Button onClick={() => loginWith(p)}>{p}</Button>
|
<Button onClick={() => loginWith(p)}>{p}</Button>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { bindOauth2, getOauth2RedirectURL, Oauth2RequestType, unbindOauth2 } from "@/api/oauth2"
|
import { Oauth2RequestType, bindOauth2, getOauth2RedirectURL, unbindOauth2 } from "@/api/oauth2"
|
||||||
import { getProfile } from "@/api/user"
|
import { getProfile } from "@/api/user"
|
||||||
import { ProfileCard } from "@/components/profile"
|
import { ProfileCard } from "@/components/profile"
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||||
@@ -31,7 +31,8 @@ export default function ProfilePage() {
|
|||||||
getProfile().then((profile) => {
|
getProfile().then((profile) => {
|
||||||
setProfile(profile)
|
setProfile(profile)
|
||||||
})
|
})
|
||||||
}).finally(() => {
|
})
|
||||||
|
.finally(() => {
|
||||||
window.history.replaceState({}, document.title, window.location.pathname)
|
window.history.replaceState({}, document.title, window.location.pathname)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -116,12 +117,20 @@ export default function ProfilePage() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="text-lg font-semibold">
|
<CardContent className="text-lg font-semibold">
|
||||||
{settingData?.config?.oauth2_providers?.map((provider) => <div>
|
{settingData?.config?.oauth2_providers?.map((provider) => (
|
||||||
{provider}: {profile.oauth2_bind?.[provider.toLowerCase()]} {profile.oauth2_bind?.[provider.toLowerCase()] ?
|
<div>
|
||||||
<Button size="sm" onClick={() => unbindO2(provider)}>Unbind</Button>
|
{provider}: {profile.oauth2_bind?.[provider.toLowerCase()]}{" "}
|
||||||
:
|
{profile.oauth2_bind?.[provider.toLowerCase()] ? (
|
||||||
<Button size="sm" onClick={() => bindO2(provider)}>Bind</Button>}
|
<Button size="sm" onClick={() => unbindO2(provider)}>
|
||||||
</div>)}
|
Unbind
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button size="sm" onClick={() => bindO2(provider)}>
|
||||||
|
Bind
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user