mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-26 04:02:47 +00:00
235 lines
7.2 KiB
TypeScript
235 lines
7.2 KiB
TypeScript
import { Box, Stack, Typography, useTheme } from "@mui/material";
|
|
import { grey } from "@mui/material/colors";
|
|
import { useSnackbar } from "notistack";
|
|
import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react";
|
|
import { Trans, useTranslation } from "react-i18next";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { testNode, upsertNode } from "../../../../api/api";
|
|
import { DownloaderProvider, Node, NodeStatus, NodeType } from "../../../../api/dashboard";
|
|
import { useAppDispatch } from "../../../../redux/hooks";
|
|
import { randomString } from "../../../../util";
|
|
import FacebookCircularProgress from "../../../Common/CircularProgress";
|
|
import { DefaultCloseAction } from "../../../Common/Snackbar/snackbar";
|
|
import { DenseFilledTextField, SecondaryButton } from "../../../Common/StyledComponents";
|
|
import DraggableDialog from "../../../Dialogs/DraggableDialog";
|
|
import SettingForm from "../../../Pages/Setting/SettingForm";
|
|
import { Code } from "../../../Common/Code.tsx";
|
|
import { EndpointInput } from "../../Common/EndpointInput";
|
|
import { NoMarginHelperText } from "../../Settings/Settings";
|
|
const MonacoEditor = lazy(() => import("../../../Viewers/CodeViewer/MonacoEditor"));
|
|
|
|
export interface NewNodeDialogProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const defaultNode: Node = {
|
|
id: 0,
|
|
name: "",
|
|
type: NodeType.slave,
|
|
status: NodeStatus.active,
|
|
server: "",
|
|
slave_key: "",
|
|
capabilities: "",
|
|
weight: 1,
|
|
settings: {
|
|
provider: DownloaderProvider.aria2,
|
|
qbittorrent: {},
|
|
aria2: {},
|
|
interval: 5,
|
|
},
|
|
edges: {
|
|
storage_policy: [],
|
|
},
|
|
};
|
|
|
|
export const Step = ({ step, children }: { step: number; children: React.ReactNode }) => {
|
|
return (
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
padding: "10px",
|
|
transition: (theme) =>
|
|
theme.transitions.create("background-color", {
|
|
easing: theme.transitions.easing.sharp,
|
|
duration: theme.transitions.duration.leavingScreen,
|
|
}),
|
|
"&:focus-within": {
|
|
backgroundColor: (theme) => (theme.palette.mode == "dark" ? grey[900] : grey[100]),
|
|
},
|
|
}}
|
|
>
|
|
<Box sx={{ ml: "20px" }}>
|
|
<Box
|
|
sx={{
|
|
width: "20px",
|
|
fontSize: (t) => t.typography.body2.fontSize,
|
|
height: "20px",
|
|
backgroundColor: (theme) => theme.palette.primary.light,
|
|
color: (theme) => theme.palette.primary.contrastText,
|
|
textAlign: "center",
|
|
borderRadius: " 50%",
|
|
}}
|
|
>
|
|
{step}
|
|
</Box>
|
|
</Box>
|
|
<Box sx={{ ml: "10px", mr: "20px", flexGrow: 1 }}>{children}</Box>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export const NewNodeDialog = ({ open, onClose }: NewNodeDialogProps) => {
|
|
const { t } = useTranslation("dashboard");
|
|
const theme = useTheme();
|
|
const dispatch = useAppDispatch();
|
|
const navigate = useNavigate();
|
|
const { enqueueSnackbar } = useSnackbar();
|
|
const [loading, setLoading] = useState(false);
|
|
const [node, setNode] = useState<Node>({ ...defaultNode });
|
|
const formRef = useRef<HTMLFormElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
setNode({ ...defaultNode, slave_key: randomString(64) });
|
|
}
|
|
}, [open]);
|
|
|
|
const handleSubmit = () => {
|
|
if (!formRef.current?.checkValidity()) {
|
|
formRef.current?.reportValidity();
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
dispatch(upsertNode({ node }))
|
|
.then((r) => {
|
|
navigate(`/admin/node/${r.id}`);
|
|
onClose();
|
|
})
|
|
.finally(() => {
|
|
setLoading(false);
|
|
});
|
|
};
|
|
|
|
const config = useMemo(() => {
|
|
return `[System]
|
|
Mode = slave
|
|
Listen = :5212
|
|
|
|
[Slave]
|
|
Secret = ${node.slave_key}
|
|
|
|
; ${t("node.keepIfUpload")}
|
|
[CORS]
|
|
AllowOrigins = *
|
|
AllowMethods = OPTIONS,GET,POST
|
|
AllowHeaders = *
|
|
`;
|
|
}, [t, node.slave_key]);
|
|
|
|
const handleTest = () => {
|
|
if (!formRef.current?.checkValidity()) {
|
|
formRef.current?.reportValidity();
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
dispatch(testNode({ node }))
|
|
.then(() => {
|
|
setLoading(false);
|
|
enqueueSnackbar(t("node.testNodeSuccess"), { variant: "success", action: DefaultCloseAction });
|
|
})
|
|
.finally(() => setLoading(false));
|
|
};
|
|
|
|
return (
|
|
<DraggableDialog
|
|
onAccept={handleSubmit}
|
|
loading={loading}
|
|
title={t("node.addNewNode")}
|
|
showActions
|
|
showCancel
|
|
dialogProps={{
|
|
open,
|
|
onClose,
|
|
fullWidth: true,
|
|
maxWidth: "sm",
|
|
}}
|
|
>
|
|
<form ref={formRef} onSubmit={handleSubmit}>
|
|
<Stack spacing={1}>
|
|
<Step step={1}>
|
|
<SettingForm lgWidth={12}>
|
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
{t("node.nameTheNode")}
|
|
</Typography>
|
|
<DenseFilledTextField
|
|
fullWidth
|
|
required
|
|
value={node.name}
|
|
onChange={(e) => setNode({ ...node, name: e.target.value })}
|
|
/>
|
|
<NoMarginHelperText>{t("node.nameNode")}</NoMarginHelperText>
|
|
</SettingForm>
|
|
</Step>
|
|
<Step step={2}>
|
|
<SettingForm lgWidth={12}>
|
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
{t("node.runCrSlave")}
|
|
</Typography>
|
|
<Suspense fallback={<FacebookCircularProgress />}>
|
|
<MonacoEditor
|
|
theme={theme.palette.mode === "dark" ? "vs-dark" : "vs"}
|
|
language="ini"
|
|
value={config}
|
|
height="200px"
|
|
minHeight="200px"
|
|
options={{
|
|
wordWrap: "on",
|
|
minimap: { enabled: false },
|
|
scrollBeyondLastLine: false,
|
|
readOnly: true,
|
|
}}
|
|
/>
|
|
</Suspense>
|
|
<NoMarginHelperText sx={{ mt: 1 }}>
|
|
<Trans i18nKey="node.runCrWithConfig" ns="dashboard" components={[<Code />]} />
|
|
</NoMarginHelperText>
|
|
</SettingForm>
|
|
</Step>
|
|
<Step step={3}>
|
|
<SettingForm lgWidth={12}>
|
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
{t("node.inputServer")}
|
|
</Typography>
|
|
<EndpointInput
|
|
variant={"outlined"}
|
|
fullWidth
|
|
required
|
|
enforceProtocol
|
|
value={node.server}
|
|
onChange={(e) => setNode({ ...node, server: e.target.value })}
|
|
/>
|
|
<NoMarginHelperText>{t("node.serverDes")}</NoMarginHelperText>
|
|
</SettingForm>
|
|
</Step>
|
|
<Step step={4}>
|
|
<SettingForm lgWidth={12}>
|
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
{t("node.testButton")}
|
|
</Typography>
|
|
<SecondaryButton loading={loading} variant="contained" onClick={handleTest}>
|
|
{t("node.testNode")}
|
|
</SecondaryButton>
|
|
<NoMarginHelperText sx={{ mt: 1 }}>
|
|
<Trans i18nKey="node.hostHeaderHint" ns="dashboard" components={[<Code />]} />
|
|
</NoMarginHelperText>
|
|
</SettingForm>
|
|
</Step>
|
|
</Stack>
|
|
</form>
|
|
</DraggableDialog>
|
|
);
|
|
};
|