mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-26 04:02:47 +00:00
Configurations guide for node aria2
This commit is contained in:
parent
b190979ca5
commit
9be016c2bf
|
|
@ -0,0 +1,429 @@
|
|||
import { lighten, makeStyles } from "@material-ui/core/styles";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSnackbar } from "../../../../actions";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import RadioGroup from "@material-ui/core/RadioGroup";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Radio from "@material-ui/core/Radio";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import API from "../../../../middleware/Api";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
stepContent: {
|
||||
padding: "16px 32px 16px 32px",
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
subStepContainer: {
|
||||
display: "flex",
|
||||
marginBottom: 20,
|
||||
padding: 10,
|
||||
transition: theme.transitions.create("background-color", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
"&:focus-within": {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
stepNumber: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
backgroundColor: lighten(theme.palette.secondary.light, 0.2),
|
||||
color: theme.palette.secondary.contrastText,
|
||||
textAlign: "center",
|
||||
borderRadius: " 50%",
|
||||
},
|
||||
stepNumberContainer: {
|
||||
marginRight: 10,
|
||||
},
|
||||
stepFooter: {
|
||||
marginTop: 32,
|
||||
},
|
||||
button: {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
viewButtonLabel: { textTransform: "none" },
|
||||
"@global": {
|
||||
code: {
|
||||
color: "rgba(0, 0, 0, 0.87)",
|
||||
display: "inline-block",
|
||||
padding: "2px 6px",
|
||||
fontFamily:
|
||||
' Consolas, "Liberation Mono", Menlo, Courier, monospace',
|
||||
borderRadius: "2px",
|
||||
backgroundColor: "rgba(255,229,100,0.1)",
|
||||
},
|
||||
pre: {
|
||||
margin: "24px 0",
|
||||
padding: "12px 18px",
|
||||
overflow: "auto",
|
||||
direction: "ltr",
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "#272c34",
|
||||
color: "#fff",
|
||||
fontFamily:
|
||||
' Consolas, "Liberation Mono", Menlo, Courier, monospace',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Aria2RPC(props) {
|
||||
const classes = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const testAria2 = () => {
|
||||
setLoading(true);
|
||||
API.post("/admin/node/aria2/test", {
|
||||
type: props.node.Type,
|
||||
server: props.node.Server,
|
||||
secret: props.node.SlaveKey,
|
||||
rpc: props.node.Aria2Options.Server,
|
||||
token: props.node.Aria2Options.Token,
|
||||
})
|
||||
.then((response) => {
|
||||
ToggleSnackbar(
|
||||
"top",
|
||||
"right",
|
||||
"连接成功,Aria2 版本为:" + response.data,
|
||||
"success"
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const mode = props.node.Type === 0 ? "从机" : "主机";
|
||||
|
||||
return (
|
||||
<form
|
||||
className={classes.stepContent}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
props.onSubmit(e);
|
||||
}}
|
||||
>
|
||||
<Alert severity="info" style={{ marginBottom: 10 }}>
|
||||
<Typography variant="body2">
|
||||
Cloudreve 的离线下载功能由{" "}
|
||||
<Link href={"https://aria2.github.io/"} target={"_blank"}>
|
||||
Aria2
|
||||
</Link>{" "}
|
||||
驱动。如需使用,请在目标节点服务器上以和运行 Cloudreve
|
||||
相同的用户身份启动 Aria2, 并在 Aria2 的配置文件中开启 RPC
|
||||
服务,
|
||||
<Box component="span" fontWeight="fontWeightBold">
|
||||
Aria2 需要和{mode} Cloudreve 进程共用相同的文件系统。
|
||||
</Box>{" "}
|
||||
更多信息及指引请参考文档的{" "}
|
||||
<Link
|
||||
href={"https://docs.cloudreve.org/use/aria2"}
|
||||
target={"_blank"}
|
||||
>
|
||||
离线下载
|
||||
</Link>{" "}
|
||||
章节。
|
||||
</Typography>
|
||||
</Alert>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>1</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
{props.node.Type === 0
|
||||
? "是否需要此节点接管离线下载任务?"
|
||||
: "是否需要主机接管离线下载任务?"}
|
||||
<br />
|
||||
{props.node.Type === 0
|
||||
? "开启后,用户的离线下载请求可以被分流到此节点处理。"
|
||||
: "开启后,用户的离线下载请求可以被分流到主机处理。"}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl required component="fieldset">
|
||||
<RadioGroup
|
||||
required
|
||||
value={props.node.Aria2Enabled}
|
||||
onChange={props.handleTextChange(
|
||||
"Aria2Enabled"
|
||||
)}
|
||||
row
|
||||
>
|
||||
<FormControlLabel
|
||||
value={"true"}
|
||||
control={<Radio color={"primary"} />}
|
||||
label="启用"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value={"false"}
|
||||
control={<Radio color={"primary"} />}
|
||||
label="关闭"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Collapse in={props.node.Aria2Enabled === "true"}>
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>2</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
{props.node.Type === 0
|
||||
? " 在目标节点服务器上与节点 "
|
||||
: "在与 "}
|
||||
Cloudreve 进程相同的文件系统环境下启动 Aria2
|
||||
进程。在启动 Aria2 时,需要在其配置文件中启用 RPC
|
||||
服务,并设定 RPC
|
||||
Secret,以便后续使用。以下为一个供参考的配置:
|
||||
</Typography>
|
||||
<pre>
|
||||
# 启用 RPC 服务
|
||||
<br />
|
||||
enable-rpc=true
|
||||
<br />
|
||||
# RPC 监听端口
|
||||
<br />
|
||||
rpc-listen-port=6800
|
||||
<br />
|
||||
# RPC 授权令牌,可自行设定
|
||||
<br />
|
||||
rpc-secure={props.node.Aria2Options.Token}
|
||||
<br />
|
||||
</pre>
|
||||
<Alert severity="info" style={{ marginBottom: 10 }}>
|
||||
<Typography variant="body2">
|
||||
推荐在日常启动流程中,先启动 Aria2,再启动节点
|
||||
Cloudreve,这样节点 Cloudreve 可以向 Aria2
|
||||
订阅事件通知,下载状态变更处理更及时。当然,如果没有这一流程,节点
|
||||
Cloudreve 也会通过轮询追踪任务状态。
|
||||
</Typography>
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>3</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
在下方填写{mode} Cloudreve 与 Aria2 通信的 RPC
|
||||
服务地址。一般可填写为
|
||||
<code>http://127.0.0.1:6800/</code>,其中端口号
|
||||
<code>6800</code>与上文配置文件中
|
||||
<code>rpc-listen-port</code>保持一致。
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
RPC 服务器地址
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
type={"url"}
|
||||
value={props.node.Aria2Options.Server}
|
||||
onChange={props.handleOptionChange(
|
||||
"Server"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
包含端口的完整 RPC
|
||||
服务器地址,例如:http://127.0.0.1:6800/,留空表示不启用
|
||||
Aria2 服务
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>4</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
RPC 授权令牌,与 Aria2 配置文件中
|
||||
<code>rpc-secure</code>保持一致,未设置请留空。
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<Input
|
||||
value={props.node.Aria2Options.Token}
|
||||
onChange={props.handleOptionChange("Token")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>5</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
在下方填写 Aria2 用作临时下载目录的 节点上的
|
||||
<strong>绝对路径</strong>,节点上的 Cloudreve
|
||||
进程需要此目录的读、写、执行权限。
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<Input
|
||||
value={props.node.Aria2Options.TempPath}
|
||||
onChange={props.handleOptionChange("TempPath")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>5</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
在下方按需要填写一些 Aria2 额外参数信息。
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
状态刷新间隔 (秒)
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
step: 1,
|
||||
min: 1,
|
||||
}}
|
||||
required
|
||||
value={props.node.Aria2Options.Interval}
|
||||
onChange={props.handleOptionChange(
|
||||
"Interval"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
Cloudreve 向 Aria2 请求刷新任务状态的间隔。
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
RPC 调用超时 (秒)
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
step: 1,
|
||||
min: 1,
|
||||
}}
|
||||
required
|
||||
value={props.node.Aria2Options.Timeout}
|
||||
onChange={props.handleOptionChange(
|
||||
"Timeout"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
调用 RPC 服务时最长等待时间
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
全局任务参数
|
||||
</InputLabel>
|
||||
<Input
|
||||
multiline
|
||||
required
|
||||
value={props.node.Aria2Options.Options}
|
||||
onChange={props.handleOptionChange(
|
||||
"Options"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
创建下载任务时携带的额外设置参数,以 JSON
|
||||
编码后的格式书写,您可也可以将这些设置写在
|
||||
Aria2 配置文件里,可用参数请查阅官方文档
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>6</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
完成以上步骤后,你可以点击下方的测试按钮测试{mode}
|
||||
Cloudreve 向 Aria2 通信是否正常。
|
||||
{props.node.Type === 0 &&
|
||||
"在进行测试前请先确保您已进行并通过上一页面中的“从机通信测试”。"}
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
variant={"outlined"}
|
||||
onClick={() => testAria2()}
|
||||
color={"primary"}
|
||||
>
|
||||
测试 Aria2 通信
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Collapse>
|
||||
|
||||
<div className={classes.stepFooter}>
|
||||
{props.activeStep !== 0 && (
|
||||
<Button
|
||||
color={"default"}
|
||||
className={classes.button}
|
||||
onClick={props.onBack}
|
||||
>
|
||||
上一步
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
下一步
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,29 +1,15 @@
|
|||
import { lighten, makeStyles } from "@material-ui/core/styles";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import Stepper from "@material-ui/core/Stepper";
|
||||
import StepLabel from "@material-ui/core/StepLabel";
|
||||
import Step from "@material-ui/core/Step";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSnackbar } from "../../../../actions";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import RadioGroup from "@material-ui/core/RadioGroup";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Radio from "@material-ui/core/Radio";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../../middleware/Api";
|
||||
import MagicVar from "../../Dialogs/MagicVar";
|
||||
import DomainInput from "../../Common/DomainInput";
|
||||
import SizeInput from "../../Common/SizeInput";
|
||||
import { useHistory } from "react-router";
|
||||
import { getNumber, randomStr } from "../../../../utils";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import Box from "@material-ui/core/Box";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
stepContent: {
|
||||
|
|
@ -78,40 +64,49 @@ const useStyles = makeStyles((theme) => ({
|
|||
borderRadius: "2px",
|
||||
backgroundColor: "rgba(255,229,100,0.1)",
|
||||
},
|
||||
pre: {
|
||||
margin: "24px 0",
|
||||
padding: "12px 18px",
|
||||
overflow: "auto",
|
||||
direction: "ltr",
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "#272c34",
|
||||
color: "#fff",
|
||||
fontFamily:
|
||||
' Consolas, "Liberation Mono", Menlo, Courier, monospace',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Communication(props) {
|
||||
const classes = useStyles();
|
||||
const history = useHistory();
|
||||
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const [skipped, setSkipped] = React.useState(new Set());
|
||||
const [node, setNode] = useState(
|
||||
props.node
|
||||
? props.node
|
||||
: {
|
||||
Status: 1,
|
||||
Type: 0,
|
||||
Aria2Enabled: false,
|
||||
Server: "https://example.com:5212",
|
||||
SlaveKey: randomStr(64),
|
||||
MasterKey: randomStr(64),
|
||||
Aria2Options: {},
|
||||
}
|
||||
);
|
||||
|
||||
const isStepSkipped = (step) => {
|
||||
return skipped.has(step);
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const testSlave = () => {
|
||||
setLoading(true);
|
||||
|
||||
// 测试路径是否可用
|
||||
API.post("/admin/policy/test/slave", {
|
||||
server: props.node.Server,
|
||||
secret: props.node.SlaveKey,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", "通信正常", "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className={classes.stepContent}
|
||||
|
|
@ -121,13 +116,154 @@ export default function Communication(props) {
|
|||
}}
|
||||
>
|
||||
<Alert severity="info" style={{ marginBottom: 10 }}>
|
||||
从机存储策略允许你使用同样运行了 Cloudreve 的服务器作为存储端,
|
||||
用户上传下载流量通过 HTTP 直传。
|
||||
您可以添加同样运行了 Cloudreve 的服务器作为从机端,
|
||||
正常运行工作的从机端可以为主机分担某些异步任务(如离线下载及转存)。
|
||||
请参考下面向导部署并配置连接 Cloudreve 从机节点。
|
||||
<Box fontWeight="fontWeightBold">
|
||||
如果你已经在目标服务器上部署了从机存储策略,您可以跳过本页面的某些步骤,
|
||||
只将从机密钥、服务器地址在这里填写并保持与从机存储策略中一致即可。
|
||||
</Box>
|
||||
在后续版本中,从机存储策略的相关配置会合并到这里。
|
||||
</Alert>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>1</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
将和主站相同版本的 Cloudreve
|
||||
程序拷贝至要作为从机的服务器上。
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>2</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
下方为系统为您随机生成的从机端密钥,一般无需改动,如果有自定义需求,
|
||||
可将您的密钥填入下方:
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
从机密钥
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
inputProps={{
|
||||
minlength: 64,
|
||||
}}
|
||||
value={props.node.SlaveKey}
|
||||
onChange={props.handleTextChange("SlaveKey")}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>3</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
修改从机配置文件。
|
||||
<br />
|
||||
在从机端 Cloudreve 的同级目录下新建
|
||||
<code>conf.ini</code>
|
||||
文件,填入从机配置,启动/重启从机端 Cloudreve。
|
||||
以下为一个可供参考的配置例子,其中密钥部分已帮您填写为上一步所生成的。
|
||||
</Typography>
|
||||
<pre>
|
||||
[System]
|
||||
<br />
|
||||
Mode = slave
|
||||
<br />
|
||||
Listen = :5212
|
||||
<br />
|
||||
<br />
|
||||
[Slave]
|
||||
<br />
|
||||
Secret = {props.node.SlaveKey}
|
||||
<br />
|
||||
</pre>
|
||||
<Typography variant={"body2"}>
|
||||
从机端配置文件格式大致与主站端相同,区别在于:
|
||||
<ul>
|
||||
<li>
|
||||
<code>System</code>
|
||||
分区下的
|
||||
<code>mode</code>
|
||||
字段必须更改为<code>slave</code>
|
||||
</li>
|
||||
<li>
|
||||
必须指定<code>Slave</code>分区下的
|
||||
<code>Secret</code>
|
||||
字段,其值为第二步里填写或生成的密钥。
|
||||
</li>
|
||||
</ul>
|
||||
一个从机 Cloudreve 实例可以对接多个 Cloudreve
|
||||
主节点,只需在所有主节点中添加此从机节点并保持密钥一致即可。
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>4</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
填写从机地址。
|
||||
<br />
|
||||
如果主站启用了 HTTPS,从机也需要启用,并在下方填入 HTTPS
|
||||
协议的地址。
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
从机地址
|
||||
</InputLabel>
|
||||
<Input
|
||||
fullWidth
|
||||
required
|
||||
type={"url"}
|
||||
value={props.node.Server}
|
||||
onChange={props.handleTextChange("Server")}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.subStepContainer}>
|
||||
<div className={classes.stepNumberContainer}>
|
||||
<div className={classes.stepNumber}>5</div>
|
||||
</div>
|
||||
<div className={classes.subStepContent}>
|
||||
<Typography variant={"body2"}>
|
||||
完成以上步骤后,你可以点击下方的测试按钮测试通信是否正常。
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
variant={"outlined"}
|
||||
onClick={() => testSlave()}
|
||||
color={"primary"}
|
||||
>
|
||||
测试从机通信
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.stepFooter}>
|
||||
<Button
|
||||
disabled={props.loading}
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
import React, { useCallback, useState } from "react";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import Stepper from "@material-ui/core/Stepper";
|
||||
import StepLabel from "@material-ui/core/StepLabel";
|
||||
import Step from "@material-ui/core/Step";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSnackbar } from "../../../../actions";
|
||||
import { useHistory } from "react-router";
|
||||
import { randomStr } from "../../../../utils";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import { lighten, makeStyles } from "@material-ui/core/styles";
|
||||
import Communication from "./Communication";
|
||||
import Aria2RPC from "./Aria2RPC";
|
||||
|
||||
const steps = [
|
||||
{
|
||||
slaveOnly: true,
|
||||
title: "通信配置",
|
||||
optional: false,
|
||||
component: function show(p) {
|
||||
|
|
@ -20,17 +19,20 @@ const steps = [
|
|||
},
|
||||
},
|
||||
{
|
||||
slaveOnly: false,
|
||||
title: "离线下载",
|
||||
optional: false,
|
||||
component: function show(p) {
|
||||
return <Communication {...p} />;
|
||||
return <Aria2RPC {...p} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
slaveOnly: false,
|
||||
title: "杂项信息",
|
||||
optional: false,
|
||||
},
|
||||
{
|
||||
slaveOnly: false,
|
||||
title: "完成",
|
||||
optional: false,
|
||||
},
|
||||
|
|
@ -38,7 +40,6 @@ const steps = [
|
|||
|
||||
export default function NodeGuide(props) {
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [skipped, setSkipped] = React.useState(new Set());
|
||||
const [node, setNode] = useState(
|
||||
props.node
|
||||
|
|
@ -46,18 +47,43 @@ export default function NodeGuide(props) {
|
|||
: {
|
||||
Status: 1,
|
||||
Type: 0,
|
||||
Aria2Enabled: false,
|
||||
Aria2Enabled: "false",
|
||||
Server: "https://example.com:5212",
|
||||
SlaveKey: randomStr(64),
|
||||
MasterKey: randomStr(64),
|
||||
Aria2Options: {},
|
||||
Aria2Options: {
|
||||
Token: randomStr(32),
|
||||
Options: "{}",
|
||||
Interval: 10,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const usedSteps = useMemo(() => {
|
||||
return steps.filter((step) => !(step.slaveOnly && node.Type === 1));
|
||||
}, [node.Type]);
|
||||
|
||||
const isStepSkipped = (step) => {
|
||||
return skipped.has(step);
|
||||
};
|
||||
|
||||
const handleTextChange = (name) => (event) => {
|
||||
setNode({
|
||||
...node,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleOptionChange = (name) => (event) => {
|
||||
setNode({
|
||||
...node,
|
||||
Aria2Options: {
|
||||
...node.Aria2Options,
|
||||
[name]: event.target.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
|
|
@ -71,7 +97,7 @@ export default function NodeGuide(props) {
|
|||
{props.node ? "修改" : "添加"} 节点
|
||||
</Typography>
|
||||
<Stepper activeStep={activeStep}>
|
||||
{steps.map((label, index) => {
|
||||
{usedSteps.map((label, index) => {
|
||||
const stepProps = {};
|
||||
const labelProps = {};
|
||||
if (label.optional) {
|
||||
|
|
@ -82,18 +108,25 @@ export default function NodeGuide(props) {
|
|||
if (isStepSkipped(index)) {
|
||||
stepProps.completed = false;
|
||||
}
|
||||
return (
|
||||
<Step key={label.title} {...stepProps}>
|
||||
<StepLabel {...labelProps}>{label.title}</StepLabel>
|
||||
</Step>
|
||||
);
|
||||
if (!(label.slaveOnly && node.Type === 1)) {
|
||||
return (
|
||||
<Step key={label.title} {...stepProps}>
|
||||
<StepLabel {...labelProps}>
|
||||
{label.title}
|
||||
</StepLabel>
|
||||
</Step>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</Stepper>
|
||||
|
||||
{steps[activeStep].component({
|
||||
onSubmit: (e) => setActiveStep(1),
|
||||
loading: loading,
|
||||
{usedSteps[activeStep].component({
|
||||
onSubmit: (e) => setActiveStep(activeStep + 1),
|
||||
node: node,
|
||||
onBack: (e) => setActiveStep(activeStep - 1),
|
||||
handleTextChange: handleTextChange,
|
||||
activeStep: activeStep,
|
||||
handleOptionChange: handleOptionChange,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue