mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-02-07 06:00:07 +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,
|
||||
}
|
||||
|
||||
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}`, {
|
||||
"type": rType,
|
||||
sType: rType,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const bindOauth2 = async (provider: string, state: string, code: string): Promise<ModelOauth2LoginResponse> => {
|
||||
return fetcher<ModelOauth2LoginResponse>(FetcherMethod.POST, `/api/v1/oauth2/${provider}/bind`, {
|
||||
"state": state,
|
||||
"code": code,
|
||||
})
|
||||
export const bindOauth2 = async (
|
||||
provider: string,
|
||||
state: string,
|
||||
code: string,
|
||||
): Promise<ModelOauth2LoginResponse> => {
|
||||
return fetcher<ModelOauth2LoginResponse>(
|
||||
FetcherMethod.POST,
|
||||
`/api/v1/oauth2/${provider}/bind`,
|
||||
{
|
||||
state: state,
|
||||
code: code,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
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 (provider: string, state: string, code: string): Promise<void> => {
|
||||
export const oauth2callback = async (
|
||||
provider: string,
|
||||
state: string,
|
||||
code: string,
|
||||
): Promise<void> => {
|
||||
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 { toast } from "sonner"
|
||||
|
||||
import { Button } from "./ui/button"
|
||||
import { TableCell, TableRow } from "./ui/table"
|
||||
import { Filepath } from "./xui/filepath"
|
||||
import { IconButton } from "./xui/icon-button"
|
||||
@@ -102,7 +103,7 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
||||
header: () => <span>{t("Actions")}</span>,
|
||||
id: "download",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
return row.original.type == 0 ? (
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
icon="download"
|
||||
@@ -111,6 +112,8 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
||||
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>) => {
|
||||
switch (event.data.type) {
|
||||
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("")
|
||||
useEffect(() => {
|
||||
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
||||
@@ -193,69 +272,6 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
||||
}
|
||||
}, [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 prefix = new Int8Array([FMOpcode.List])
|
||||
const pathMsg = new TextEncoder().encode(currentPath)
|
||||
@@ -317,7 +333,7 @@ const FMComponent: React.FC<FMProps & JSX.IntrinsicElements["div"]> = ({ wsUrl,
|
||||
toast("FM" + " " + t("Error"), {
|
||||
description: error.message,
|
||||
})
|
||||
console.log("copy error: ", error)
|
||||
console.error("copy error: ", error)
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { useAuth } from "@/hooks/useAuth"
|
||||
import useSettings from "@/hooks/useSetting"
|
||||
import { copyToClipboard } from "@/lib/utils"
|
||||
import { ModelProfile, ModelConfig } from "@/types"
|
||||
import { ModelConfig, ModelProfile } from "@/types"
|
||||
import i18next from "i18next"
|
||||
import { Check, Clipboard } from "lucide-react"
|
||||
import { forwardRef, useState } from "react"
|
||||
|
||||
@@ -52,7 +52,7 @@ export const Filepath: React.FC<FilepathProps> = ({ path, setPath }) => {
|
||||
setPath("/")
|
||||
}}
|
||||
>
|
||||
{"/"}
|
||||
/
|
||||
</p>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { oauth2callback } from "@/api/oauth2"
|
||||
import { getProfile, login as loginRequest } from "@/api/user"
|
||||
import { AuthContextProps } from "@/types"
|
||||
import { createContext, useContext, useEffect, useMemo } from "react"
|
||||
@@ -5,13 +6,12 @@ import { useNavigate } from "react-router-dom"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { useMainStore } from "./useMainStore"
|
||||
import { oauth2callback } from "@/api/oauth2"
|
||||
|
||||
const AuthContext = createContext<AuthContextProps>({
|
||||
profile: undefined,
|
||||
login: () => { },
|
||||
loginOauth2: () => { },
|
||||
logout: () => { },
|
||||
login: () => {},
|
||||
loginOauth2: () => {},
|
||||
logout: () => {},
|
||||
})
|
||||
|
||||
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
@@ -19,7 +19,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const setProfile = useMainStore((store) => store.setProfile)
|
||||
|
||||
useEffect(() => {
|
||||
; (async () => {
|
||||
;(async () => {
|
||||
try {
|
||||
const user = await getProfile()
|
||||
user.role = user.role || 0
|
||||
|
||||
@@ -3,6 +3,8 @@ import { GithubComNezhahqNezhaModelSettingResponseModelConfig } from "@/types"
|
||||
import useSWR from "swr"
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
const dataChunk = arrayBuffer
|
||||
accessHandle.write(dataChunk, { at: receivedLength })
|
||||
receivedLength += dataChunk.byteLength
|
||||
accessHandle.write(arrayBuffer, { at: receivedLength })
|
||||
receivedLength += arrayBuffer.byteLength
|
||||
|
||||
if (receivedLength === expectedLength) {
|
||||
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 {
|
||||
Form,
|
||||
@@ -103,9 +103,9 @@ function Login() {
|
||||
</form>
|
||||
</Form>
|
||||
<div className="mt-4">
|
||||
{settingData?.config?.oauth2_providers?.map((p: string) =>
|
||||
{settingData?.config?.oauth2_providers?.map((p: string) => (
|
||||
<Button onClick={() => loginWith(p)}>{p}</Button>
|
||||
)}
|
||||
))}
|
||||
</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 { ProfileCard } from "@/components/profile"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
@@ -31,7 +31,8 @@ export default function ProfilePage() {
|
||||
getProfile().then((profile) => {
|
||||
setProfile(profile)
|
||||
})
|
||||
}).finally(() => {
|
||||
})
|
||||
.finally(() => {
|
||||
window.history.replaceState({}, document.title, window.location.pathname)
|
||||
})
|
||||
}
|
||||
@@ -116,12 +117,20 @@ export default function ProfilePage() {
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-lg font-semibold">
|
||||
{settingData?.config?.oauth2_providers?.map((provider) => <div>
|
||||
{provider}: {profile.oauth2_bind?.[provider.toLowerCase()]} {profile.oauth2_bind?.[provider.toLowerCase()] ?
|
||||
<Button size="sm" onClick={() => unbindO2(provider)}>Unbind</Button>
|
||||
:
|
||||
<Button size="sm" onClick={() => bindO2(provider)}>Bind</Button>}
|
||||
</div>)}
|
||||
{settingData?.config?.oauth2_providers?.map((provider) => (
|
||||
<div>
|
||||
{provider}: {profile.oauth2_bind?.[provider.toLowerCase()]}{" "}
|
||||
{profile.oauth2_bind?.[provider.toLowerCase()] ? (
|
||||
<Button size="sm" onClick={() => unbindO2(provider)}>
|
||||
Unbind
|
||||
</Button>
|
||||
) : (
|
||||
<Button size="sm" onClick={() => bindO2(provider)}>
|
||||
Bind
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user