init
This commit is contained in:
502
src/component/Admin/Setting/Access.js
Normal file
502
src/component/Admin/Setting/Access.js
Normal file
@@ -0,0 +1,502 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import AlertDialog from "../Dialogs/Alert";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import FileSelector from "../Common/FileSelector";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Access() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const { t: tVas } = useTranslation("dashboard", { keyPrefix: "vas" });
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [initCompleted, setInitComplete] = useState(false);
|
||||
const [options, setOptions] = useState({
|
||||
register_enabled: "1",
|
||||
default_group: "1",
|
||||
email_active: "0",
|
||||
login_captcha: "0",
|
||||
reg_captcha: "0",
|
||||
forget_captcha: "0",
|
||||
qq_login: "0",
|
||||
qq_direct_login: "0",
|
||||
qq_login_id: "",
|
||||
qq_login_key: "",
|
||||
authn_enabled: "0",
|
||||
mail_domain_filter: "0",
|
||||
mail_domain_filter_list: "",
|
||||
initial_files: "[]",
|
||||
});
|
||||
const [siteURL, setSiteURL] = useState("");
|
||||
const [groups, setGroups] = useState([]);
|
||||
const [httpAlert, setHttpAlert] = useState(false);
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
let value = event.target.value;
|
||||
if (event.target.checked !== undefined) {
|
||||
value = event.target.checked ? "1" : "0";
|
||||
}
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleInputChange = (name) => (event) => {
|
||||
const value = event.target.value;
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
API.post("/admin/setting", {
|
||||
keys: [...Object.keys(options), "siteURL"],
|
||||
})
|
||||
.then((response) => {
|
||||
setSiteURL(response.data.siteURL);
|
||||
delete response.data.siteURL;
|
||||
setOptions(response.data);
|
||||
setInitComplete(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
|
||||
API.get("/admin/groups")
|
||||
.then((response) => {
|
||||
setGroups(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
option.push({
|
||||
key: k,
|
||||
value: options[k],
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AlertDialog
|
||||
title={t("hint")}
|
||||
msg={t("webauthnNoHttps")}
|
||||
onClose={() => setHttpAlert(false)}
|
||||
open={httpAlert}
|
||||
/>
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("accountManagement")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.register_enabled === "1"
|
||||
}
|
||||
onChange={handleChange(
|
||||
"register_enabled"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("allowNewRegistrations")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("allowNewRegistrationsDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.email_active === "1"
|
||||
}
|
||||
onChange={handleChange(
|
||||
"email_active"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("emailActivation")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("emailActivationDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.reg_captcha === "1"
|
||||
}
|
||||
onChange={handleChange(
|
||||
"reg_captcha"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("captchaForSignup")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("captchaForSignupDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.login_captcha === "1"
|
||||
}
|
||||
onChange={handleChange(
|
||||
"login_captcha"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("captchaForLogin")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("captchaForLoginDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.forget_captcha === "1"
|
||||
}
|
||||
onChange={handleChange(
|
||||
"forget_captcha"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("captchaForReset")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("captchaForResetDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.authn_enabled === "1"
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (
|
||||
!siteURL.startsWith(
|
||||
"https://"
|
||||
)
|
||||
) {
|
||||
setHttpAlert(true);
|
||||
return;
|
||||
}
|
||||
handleChange("authn_enabled")(
|
||||
e
|
||||
);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={t("webauthn")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("webauthnDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("defaultGroup")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.default_group}
|
||||
onChange={handleInputChange(
|
||||
"default_group"
|
||||
)}
|
||||
required
|
||||
>
|
||||
{groups.map((v) => {
|
||||
if (v.ID === 3) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
key={v.ID}
|
||||
value={v.ID.toString()}
|
||||
>
|
||||
{v.Name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("defaultGroupDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
{initCompleted && (
|
||||
<FileSelector
|
||||
label={tVas("initialFiles")}
|
||||
value={JSON.parse(
|
||||
options.initial_files
|
||||
)}
|
||||
onChange={(v) =>
|
||||
handleInputChange("initial_files")({
|
||||
target: { value: v },
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("initialFilesDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("filterEmailProvider")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.mail_domain_filter}
|
||||
onChange={handleInputChange(
|
||||
"mail_domain_filter"
|
||||
)}
|
||||
required
|
||||
>
|
||||
{[
|
||||
tVas("filterEmailProviderDisabled"),
|
||||
tVas("filterEmailProviderWhitelist"),
|
||||
tVas("filterEmailProviderBlacklist"),
|
||||
].map((v, i) => (
|
||||
<MenuItem key={i} value={i.toString()}>
|
||||
{v}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("filterEmailProviderDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
{options.mail_domain_filter !== "0" && (
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("filterEmailProviderRule")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.mail_domain_filter_list}
|
||||
onChange={handleChange(
|
||||
"mail_domain_filter_list"
|
||||
)}
|
||||
multiline
|
||||
rowsMax="10"
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("filterEmailProviderRuleDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{tVas("qqConnect")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<Alert severity="info">
|
||||
{tVas("qqConnectHint", {
|
||||
url: siteURL.endsWith("/")
|
||||
? siteURL + "login/qq"
|
||||
: siteURL + "/login/qq",
|
||||
})}
|
||||
</Alert>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={options.qq_login === "1"}
|
||||
onChange={handleChange("qq_login")}
|
||||
/>
|
||||
}
|
||||
label={tVas("enableQQConnect")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("enableQQConnectDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
{options.qq_login === "1" && (
|
||||
<>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.qq_direct_login ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleChange(
|
||||
"qq_direct_login"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={tVas("loginWithoutBinding")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("loginWithoutBindingDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("appid")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={options.qq_login_id}
|
||||
onChange={handleInputChange(
|
||||
"qq_login_id"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("appidDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("appKey")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={options.qq_login_key}
|
||||
onChange={handleInputChange(
|
||||
"qq_login_key"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("appKeyDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
537
src/component/Admin/Setting/Captcha.js
Normal file
537
src/component/Admin/Setting/Captcha.js
Normal file
@@ -0,0 +1,537 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Captcha() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [options, setOptions] = useState({
|
||||
captcha_type: "normal",
|
||||
captcha_height: "1",
|
||||
captcha_width: "1",
|
||||
captcha_mode: "3",
|
||||
captcha_CaptchaLen: "6",
|
||||
captcha_ComplexOfNoiseText: "0",
|
||||
captcha_ComplexOfNoiseDot: "0",
|
||||
captcha_IsShowHollowLine: "0",
|
||||
captcha_IsShowNoiseDot: "0",
|
||||
captcha_IsShowNoiseText: "0",
|
||||
captcha_IsShowSlimeLine: "0",
|
||||
captcha_IsShowSineLine: "0",
|
||||
captcha_ReCaptchaKey: "",
|
||||
captcha_ReCaptchaSecret: "",
|
||||
captcha_TCaptcha_CaptchaAppId: "",
|
||||
captcha_TCaptcha_AppSecretKey: "",
|
||||
captcha_TCaptcha_SecretId: "",
|
||||
captcha_TCaptcha_SecretKey: "",
|
||||
});
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
API.post("/admin/setting", {
|
||||
keys: Object.keys(options),
|
||||
})
|
||||
.then((response) => {
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
option.push({
|
||||
key: k,
|
||||
value: options[k],
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCheckChange = (name) => (event) => {
|
||||
const value = event.target.checked ? "1" : "0";
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("captcha")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("captchaType")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.captcha_type}
|
||||
onChange={handleChange("captcha_type")}
|
||||
required
|
||||
>
|
||||
<MenuItem value={"normal"}>
|
||||
{t("plainCaptcha")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"recaptcha"}>
|
||||
{t("reCaptchaV2")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"tcaptcha"}>
|
||||
{t("tencentCloudCaptcha")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("captchaProvider")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{options.captcha_type === "normal" && (
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("plainCaptchaTitle")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("captchaWidth")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.captcha_width}
|
||||
onChange={handleChange("captcha_width")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("captchaHeight")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.captcha_height}
|
||||
onChange={handleChange(
|
||||
"captcha_height"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("captchaLength")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.captcha_CaptchaLen}
|
||||
onChange={handleChange(
|
||||
"captcha_CaptchaLen"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("captchaMode")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.captcha_mode}
|
||||
onChange={handleChange("captcha_mode")}
|
||||
required
|
||||
>
|
||||
<MenuItem value={"0"}>
|
||||
{t("captchaModeNumber")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"1"}>
|
||||
{t("captchaModeLetter")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"2"}>
|
||||
{t("captchaModeMath")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"3"}>
|
||||
{t("captchaModeNumberLetter")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("captchaElement")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
{[
|
||||
{
|
||||
name: "complexOfNoiseText",
|
||||
field: "captcha_ComplexOfNoiseText",
|
||||
},
|
||||
{
|
||||
name: "complexOfNoiseDot",
|
||||
field: "captcha_ComplexOfNoiseDot",
|
||||
},
|
||||
{
|
||||
name: "showHollowLine",
|
||||
field: "captcha_IsShowHollowLine",
|
||||
},
|
||||
{
|
||||
name: "showNoiseDot",
|
||||
field: "captcha_IsShowNoiseDot",
|
||||
},
|
||||
{
|
||||
name: "showNoiseText",
|
||||
field: "captcha_IsShowNoiseText",
|
||||
},
|
||||
{
|
||||
name: "showSlimeLine",
|
||||
field: "captcha_IsShowSlimeLine",
|
||||
},
|
||||
{
|
||||
name: "showSineLine",
|
||||
field: "captcha_IsShowSineLine",
|
||||
},
|
||||
].map((input) => (
|
||||
<div key={input.name} className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options[input.field] ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
input.field
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t(input.name)}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{options.captcha_type === "recaptcha" && (
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("reCaptchaV2")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("siteKey")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={options.captcha_ReCaptchaKey}
|
||||
onChange={handleChange(
|
||||
"captcha_ReCaptchaKey"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={"settings.siteKeyDes"}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://www.google.com/recaptcha/admin/create"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("siteSecret")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={
|
||||
options.captcha_ReCaptchaSecret
|
||||
}
|
||||
onChange={handleChange(
|
||||
"captcha_ReCaptchaSecret"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={
|
||||
"settings.siteSecretDes"
|
||||
}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://www.google.com/recaptcha/admin/create"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{options.captcha_type === "tcaptcha" && (
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("tencentCloudCaptcha")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("secretID")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={
|
||||
options.captcha_TCaptcha_SecretId
|
||||
}
|
||||
onChange={handleChange(
|
||||
"captcha_TCaptcha_SecretId"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={
|
||||
"settings.siteSecretDes"
|
||||
}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://console.cloud.tencent.com/capi"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("secretKey")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={
|
||||
options.captcha_TCaptcha_SecretKey
|
||||
}
|
||||
onChange={handleChange(
|
||||
"captcha_TCaptcha_SecretKey"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={
|
||||
"settings.secretKeyDes"
|
||||
}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://console.cloud.tencent.com/capi"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("tCaptchaAppID")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={
|
||||
options.captcha_TCaptcha_CaptchaAppId
|
||||
}
|
||||
onChange={handleChange(
|
||||
"captcha_TCaptcha_CaptchaAppId"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={
|
||||
"settings.tCaptchaAppIDDes"
|
||||
}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://console.cloud.tencent.com/captcha/graphical"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("tCaptchaSecretKey")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
required
|
||||
value={
|
||||
options.captcha_TCaptcha_AppSecretKey
|
||||
}
|
||||
onChange={handleChange(
|
||||
"captcha_TCaptcha_AppSecretKey"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={
|
||||
"settings.tCaptchaSecretKeyDes"
|
||||
}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://console.cloud.tencent.com/captcha/graphical"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
664
src/component/Admin/Setting/Image.js
Normal file
664
src/component/Admin/Setting/Image.js
Normal file
@@ -0,0 +1,664 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import SizeInput from "../Common/SizeInput";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import ThumbGenerators from "./ThumbGenerators";
|
||||
import PolicySelector from "../Common/PolicySelector";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function ImageSetting() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [options, setOptions] = useState({
|
||||
gravatar_server: "",
|
||||
avatar_path: "",
|
||||
avatar_size: "",
|
||||
avatar_size_l: "",
|
||||
avatar_size_m: "",
|
||||
avatar_size_s: "",
|
||||
thumb_width: "",
|
||||
thumb_height: "",
|
||||
office_preview_service: "",
|
||||
thumb_file_suffix: "",
|
||||
thumb_max_task_count: "",
|
||||
thumb_encode_method: "",
|
||||
thumb_gc_after_gen: "0",
|
||||
thumb_encode_quality: "",
|
||||
maxEditSize: "",
|
||||
wopi_enabled: "0",
|
||||
wopi_endpoint: "",
|
||||
wopi_session_timeout: "0",
|
||||
thumb_builtin_enabled: "0",
|
||||
thumb_vips_enabled: "0",
|
||||
thumb_vips_exts: "",
|
||||
thumb_ffmpeg_enabled: "0",
|
||||
thumb_vips_path: "",
|
||||
thumb_ffmpeg_path: "",
|
||||
thumb_ffmpeg_exts: "",
|
||||
thumb_ffmpeg_seek: "",
|
||||
thumb_libreoffice_path: "",
|
||||
thumb_libreoffice_enabled: "0",
|
||||
thumb_libreoffice_exts: "",
|
||||
thumb_proxy_enabled: "0",
|
||||
thumb_proxy_policy: [],
|
||||
thumb_max_src_size: "",
|
||||
});
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
API.post("/admin/setting", {
|
||||
keys: Object.keys(options),
|
||||
})
|
||||
.then((response) => {
|
||||
response.data.thumb_proxy_policy = JSON.parse(
|
||||
response.data.thumb_proxy_policy
|
||||
).map((v) => {
|
||||
return v.toString();
|
||||
});
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const reload = () => {
|
||||
API.get("/admin/reload/wopi")
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
.then(() => {})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
.then(() => {});
|
||||
};
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
let value = options[k];
|
||||
if (k === "thumb_proxy_policy") {
|
||||
value = JSON.stringify(value.map((v) => parseInt(v)));
|
||||
}
|
||||
|
||||
option.push({
|
||||
key: k,
|
||||
value,
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCheckChange = (name) => (event) => {
|
||||
const value = event.target.checked ? "1" : "0";
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("avatar")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("gravatarServer")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"url"}
|
||||
value={options.gravatar_server}
|
||||
onChange={handleChange("gravatar_server")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("gravatarServerDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("avatarFilePath")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.avatar_path}
|
||||
onChange={handleChange("avatar_path")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("avatarFilePathDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
{options.avatar_size !== "" && (
|
||||
<SizeInput
|
||||
value={options.avatar_size}
|
||||
onChange={handleChange("avatar_size")}
|
||||
required
|
||||
min={0}
|
||||
max={2147483647}
|
||||
label={t("avatarSize")}
|
||||
/>
|
||||
)}
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("avatarSizeDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smallAvatarSize")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.avatar_size_s}
|
||||
onChange={handleChange("avatar_size_s")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("mediumAvatarSize")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.avatar_size_m}
|
||||
onChange={handleChange("avatar_size_m")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("largeAvatarSize")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.avatar_size_l}
|
||||
onChange={handleChange("avatar_size_l")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("filePreview")}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("officePreviewService")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.office_preview_service}
|
||||
onChange={handleChange(
|
||||
"office_preview_service"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("officePreviewServiceDes")}
|
||||
<br />
|
||||
<code>{"{$src}"}</code> -{" "}
|
||||
{t("officePreviewServiceSrcDes")}
|
||||
<br />
|
||||
<code>{"{$srcB64}"}</code> -{" "}
|
||||
{t("officePreviewServiceSrcB64Des")}
|
||||
<br />
|
||||
<code>{"{$name}"}</code> -{" "}
|
||||
{t("officePreviewServiceName")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
{options.maxEditSize !== "" && (
|
||||
<SizeInput
|
||||
value={options.maxEditSize}
|
||||
onChange={handleChange("maxEditSize")}
|
||||
required
|
||||
min={0}
|
||||
max={2147483647}
|
||||
label={t("textEditMaxSize")}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("textEditMaxSizeDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("wopiClient")}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<Alert severity="info">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={"settings.wopiClientDes"}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
target={"_blank"}
|
||||
href={t("wopiDocLink")}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</Alert>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.wopi_enabled === "1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
"wopi_enabled"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("enableWopi")}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
{options.wopi_enabled === "1" && (
|
||||
<>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("wopiEndpoint")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.wopi_endpoint}
|
||||
onChange={handleChange(
|
||||
"wopi_endpoint"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("wopiEndpointDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("wopiSessionTtl")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
inputProps={{ min: 1, step: 1 }}
|
||||
type={"number"}
|
||||
value={options.wopi_session_timeout}
|
||||
onChange={handleChange(
|
||||
"wopi_session_timeout"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("wopiSessionTtlDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("thumbnails")}
|
||||
</Typography>
|
||||
<div className={classes.form}>
|
||||
<Alert severity="info">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={"settings.thumbnailDoc"}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
target={"_blank"}
|
||||
href={t("thumbnailDocLink")}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</Alert>
|
||||
</div>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{t("thumbnailBasic")}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("thumbWidth")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.thumb_width}
|
||||
onChange={handleChange("thumb_width")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("thumbHeight")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.thumb_height}
|
||||
onChange={handleChange("thumb_height")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("thumbSuffix")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"text"}
|
||||
value={options.thumb_file_suffix}
|
||||
onChange={handleChange("thumb_file_suffix")}
|
||||
required
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("thumbConcurrent")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: -1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.thumb_max_task_count}
|
||||
onChange={handleChange(
|
||||
"thumb_max_task_count"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("thumbConcurrentDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("thumbFormat")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"test"}
|
||||
value={options.thumb_encode_method}
|
||||
onChange={handleChange(
|
||||
"thumb_encode_method"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("thumbFormatDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("thumbQuality")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
max: 100,
|
||||
}}
|
||||
value={options.thumb_encode_quality}
|
||||
onChange={handleChange(
|
||||
"thumb_encode_quality"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("thumbQualityDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
{options.thumb_max_src_size !== "" && (
|
||||
<SizeInput
|
||||
value={options.thumb_max_src_size}
|
||||
onChange={handleChange(
|
||||
"thumb_max_src_size"
|
||||
)}
|
||||
required
|
||||
min={0}
|
||||
max={2147483647}
|
||||
label={t("thumbMaxSize")}
|
||||
/>
|
||||
)}
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("thumbMaxSizeDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.thumb_gc_after_gen ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
"thumb_gc_after_gen"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("thumbGC")}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{t("generators")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<ThumbGenerators
|
||||
options={options}
|
||||
setOptions={setOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{t("generatorProxy")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<Alert severity="info">
|
||||
{t("generatorProxyWarning")}
|
||||
</Alert>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.thumb_proxy_enabled ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
"thumb_proxy_enabled"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("enableThumbProxy")}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
{options.thumb_proxy_enabled === "1" && (
|
||||
<>
|
||||
<div className={classes.form}>
|
||||
<PolicySelector
|
||||
value={options.thumb_proxy_policy}
|
||||
onChange={handleChange(
|
||||
"thumb_proxy_policy"
|
||||
)}
|
||||
filter={(t) => t.Type !== "local"}
|
||||
label={t("proxyPolicyList")}
|
||||
helperText={t("proxyPolicyListDes")}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
450
src/component/Admin/Setting/Mail.js
Normal file
450
src/component/Admin/Setting/Mail.js
Normal file
@@ -0,0 +1,450 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import { useDispatch } from "react-redux";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
buttonMargin: {
|
||||
marginLeft: 8,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Mail() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const { t: tVas } = useTranslation("dashboard", { keyPrefix: "vas" });
|
||||
const { t: tGlobal } = useTranslation("common");
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [test, setTest] = useState(false);
|
||||
const [tesInput, setTestInput] = useState("");
|
||||
const [options, setOptions] = useState({
|
||||
fromName: "",
|
||||
fromAdress: "",
|
||||
smtpHost: "",
|
||||
smtpPort: "",
|
||||
replyTo: "",
|
||||
smtpUser: "",
|
||||
smtpPass: "",
|
||||
smtpEncryption: "",
|
||||
mail_keepalive: "30",
|
||||
over_used_template: "",
|
||||
mail_activation_template: "",
|
||||
mail_reset_pwd_template: "",
|
||||
});
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCheckChange = (name) => (event) => {
|
||||
let value = event.target.value;
|
||||
if (event.target.checked !== undefined) {
|
||||
value = event.target.checked ? "1" : "0";
|
||||
}
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
API.post("/admin/setting", {
|
||||
keys: Object.keys(options),
|
||||
})
|
||||
.then((response) => {
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const sendTestMail = () => {
|
||||
setLoading(true);
|
||||
API.post("/admin/test/mail", {
|
||||
to: tesInput,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("testMailSent"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
API.get("/admin/reload/email")
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
.then(() => {})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
.then(() => {});
|
||||
};
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
option.push({
|
||||
key: k,
|
||||
value: options[k],
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
open={test}
|
||||
onClose={() => setTest(false)}
|
||||
aria-labelledby="form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="form-dialog-title">
|
||||
{t("testSMTPSettings")}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
<Typography>{t("testSMTPTooltip")}</Typography>
|
||||
</DialogContentText>
|
||||
<TextField
|
||||
autoFocus
|
||||
margin="dense"
|
||||
id="name"
|
||||
label={t("recipient")}
|
||||
value={tesInput}
|
||||
onChange={(e) => setTestInput(e.target.value)}
|
||||
type="email"
|
||||
fullWidth
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setTest(false)} color="default">
|
||||
{tGlobal("cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => sendTestMail()}
|
||||
disabled={loading}
|
||||
color="primary"
|
||||
>
|
||||
{t("send")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("smtp")}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("senderName")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.fromName}
|
||||
onChange={handleChange("fromName")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("senderNameDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("senderAddress")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"email"}
|
||||
required
|
||||
value={options.fromAdress}
|
||||
onChange={handleChange("fromAdress")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("senderAddressDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smtpServer")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.smtpHost}
|
||||
onChange={handleChange("smtpHost")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("smtpServerDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smtpPort")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
inputProps={{ min: 1, step: 1 }}
|
||||
type={"number"}
|
||||
value={options.smtpPort}
|
||||
onChange={handleChange("smtpPort")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("smtpPortDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smtpUsername")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.smtpUser}
|
||||
onChange={handleChange("smtpUser")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("smtpUsernameDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smtpPassword")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"password"}
|
||||
value={options.smtpPass}
|
||||
onChange={handleChange("smtpPass")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("smtpPasswordDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("replyToAddress")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.replyTo}
|
||||
onChange={handleChange("replyTo")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("replyToAddressDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.smtpEncryption === "1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
"smtpEncryption"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("enforceSSL")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("enforceSSLDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smtpTTL")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
inputProps={{ min: 1, step: 1 }}
|
||||
type={"number"}
|
||||
value={options.mail_keepalive}
|
||||
onChange={handleChange("mail_keepalive")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("smtpTTLDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("emailTemplates")}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("activateNewUser")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.mail_activation_template}
|
||||
onChange={handleChange(
|
||||
"mail_activation_template"
|
||||
)}
|
||||
multiline
|
||||
rowsMax="10"
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("activateNewUserDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("overuseReminder")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.over_used_template}
|
||||
onChange={handleChange(
|
||||
"over_used_template"
|
||||
)}
|
||||
multiline
|
||||
rowsMax="10"
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("overuseReminderDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("resetPassword")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.mail_reset_pwd_template}
|
||||
onChange={handleChange(
|
||||
"mail_reset_pwd_template"
|
||||
)}
|
||||
multiline
|
||||
rowsMax="10"
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("resetPasswordDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
{" "}
|
||||
<Button
|
||||
className={classes.buttonMargin}
|
||||
variant={"outlined"}
|
||||
color={"primary"}
|
||||
onClick={() => setTest(true)}
|
||||
>
|
||||
{t("sendTestEmail")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
517
src/component/Admin/Setting/SiteInformation.js
Normal file
517
src/component/Admin/Setting/SiteInformation.js
Normal file
@@ -0,0 +1,517 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import { green } from "@material-ui/core/colors";
|
||||
import { Cancel, CheckCircle, Sync } from "@material-ui/icons";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function SiteInformation() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const { t: tVas } = useTranslation("dashboard", { keyPrefix: "vas" });
|
||||
const { t: tGlobal } = useTranslation("dashboard");
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [options, setOptions] = useState({
|
||||
siteURL: "",
|
||||
siteName: "",
|
||||
siteTitle: "",
|
||||
siteKeywords: "",
|
||||
siteDes: "",
|
||||
siteScript: "",
|
||||
siteNotice: "",
|
||||
pwa_small_icon: "",
|
||||
pwa_medium_icon: "",
|
||||
pwa_large_icon: "",
|
||||
pwa_display: "",
|
||||
pwa_theme_color: "",
|
||||
pwa_background_color: "",
|
||||
vol_content: "",
|
||||
show_app_promotion: "0",
|
||||
app_feedback_link: "",
|
||||
app_forum_link: "",
|
||||
});
|
||||
|
||||
const vol = useMemo(() => {
|
||||
if (options.vol_content) {
|
||||
const volJson = atob(options.vol_content);
|
||||
return JSON.parse(volJson);
|
||||
}
|
||||
}, [options]);
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleOptionChange = (name) => (event) => {
|
||||
let value = event.target.value;
|
||||
if (event.target.checked !== undefined) {
|
||||
value = event.target.checked ? "1" : "0";
|
||||
}
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const refresh = () =>
|
||||
API.post("/admin/setting", {
|
||||
keys: Object.keys(options),
|
||||
})
|
||||
.then((response) => {
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
option.push({
|
||||
key: k,
|
||||
value: options[k],
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const syncVol = () => {
|
||||
setLoading(true);
|
||||
API.get("/admin/vol/sync")
|
||||
.then(() => {
|
||||
refresh();
|
||||
ToggleSnackbar("top", "right", tVas("volSynced"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("basicInformation")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("mainTitle")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.siteName}
|
||||
onChange={handleChange("siteName")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("mainTitleDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("subTitle")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.siteTitle}
|
||||
onChange={handleChange("siteTitle")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("subTitleDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("siteKeywords")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.siteKeywords}
|
||||
onChange={handleChange("siteKeywords")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("siteKeywordsDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("siteDescription")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.siteDes}
|
||||
onChange={handleChange("siteDes")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("siteDescriptionDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("siteURL")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"url"}
|
||||
value={options.siteURL}
|
||||
onChange={handleChange("siteURL")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("siteURLDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("customFooterHTML")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
multiline
|
||||
value={options.siteScript}
|
||||
onChange={handleChange("siteScript")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("customFooterHTMLDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("announcement")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
placeholder={t("supportHTML")}
|
||||
multiline
|
||||
value={options.siteNotice}
|
||||
onChange={handleChange("siteNotice")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("announcementDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{tVas("mobileApp")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<Alert severity="info">
|
||||
<Typography variant="body2">
|
||||
<Trans
|
||||
ns={"dashboard"}
|
||||
i18nKey={"vas.volPurchase"}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={
|
||||
"https://cloudreve.org/login"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
<Link
|
||||
key={1}
|
||||
href={
|
||||
"https://cloudreve.org/ios"
|
||||
}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</Typography>
|
||||
</Alert>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("iosVol")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
{vol ? (
|
||||
<CheckCircle
|
||||
style={{
|
||||
color: green[500],
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Cancel color={"error"} />
|
||||
)}
|
||||
</InputAdornment>
|
||||
}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<Tooltip
|
||||
title={tVas("syncLicense")}
|
||||
>
|
||||
<IconButton
|
||||
disabled={loading}
|
||||
onClick={() => syncVol()}
|
||||
aria-label="toggle password visibility"
|
||||
>
|
||||
<Sync />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</InputAdornment>
|
||||
}
|
||||
readOnly
|
||||
value={
|
||||
vol ? vol.domain : tGlobal("share.none")
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.show_app_promotion ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleOptionChange(
|
||||
"show_app_promotion"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={tVas("showAppPromotion")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("showAppPromotionDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("appFeedback")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.app_feedback_link}
|
||||
onChange={handleChange("app_feedback_link")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("appLinkDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{tVas("appForum")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.app_forum_link}
|
||||
onChange={handleChange("app_forum_link")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{tVas("appLinkDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("pwa")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("smallIcon")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.pwa_small_icon}
|
||||
onChange={handleChange("pwa_small_icon")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("smallIconDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("mediumIcon")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.pwa_medium_icon}
|
||||
onChange={handleChange("pwa_medium_icon")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("mediumIconDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("largeIcon")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.pwa_large_icon}
|
||||
onChange={handleChange("pwa_large_icon")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("largeIconDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("displayMode")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.pwa_display}
|
||||
onChange={handleChange("pwa_display")}
|
||||
>
|
||||
<MenuItem value={"fullscreen"}>
|
||||
fullscreen
|
||||
</MenuItem>
|
||||
<MenuItem value={"standalone"}>
|
||||
standalone
|
||||
</MenuItem>
|
||||
<MenuItem value={"minimal-ui"}>
|
||||
minimal-ui
|
||||
</MenuItem>
|
||||
<MenuItem value={"browser"}>
|
||||
browser
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("displayModeDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("themeColor")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.pwa_theme_color}
|
||||
onChange={handleChange("pwa_theme_color")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("themeColorDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("backgroundColor")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.pwa_background_color}
|
||||
onChange={handleChange(
|
||||
"pwa_background_color"
|
||||
)}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("backgroundColorDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
465
src/component/Admin/Setting/Theme.js
Normal file
465
src/component/Admin/Setting/Theme.js
Normal file
@@ -0,0 +1,465 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import { Delete } from "@material-ui/icons";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import CreateTheme from "../Dialogs/CreateTheme";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 500,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
colorContainer: {
|
||||
display: "flex",
|
||||
},
|
||||
colorDot: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
borderRadius: "50%",
|
||||
marginLeft: 6,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Theme() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const { t: tApp } = useTranslation();
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [theme, setTheme] = useState({});
|
||||
const [options, setOptions] = useState({
|
||||
themes: "{}",
|
||||
defaultTheme: "",
|
||||
home_view_method: "icon",
|
||||
share_view_method: "list",
|
||||
});
|
||||
const [themeConfig, setThemeConfig] = useState({});
|
||||
const [themeConfigError, setThemeConfigError] = useState({});
|
||||
const [create, setCreate] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const deleteTheme = (color) => {
|
||||
if (color === options.defaultTheme) {
|
||||
ToggleSnackbar(
|
||||
"top",
|
||||
"right",
|
||||
t("cannotDeleteDefaultTheme"),
|
||||
"warning"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (Object.keys(theme).length <= 1) {
|
||||
ToggleSnackbar("top", "right", t("keepAtLeastOneTheme"), "warning");
|
||||
return;
|
||||
}
|
||||
const themeCopy = { ...theme };
|
||||
delete themeCopy[color];
|
||||
const resStr = JSON.stringify(themeCopy);
|
||||
setOptions({
|
||||
...options,
|
||||
themes: resStr,
|
||||
});
|
||||
};
|
||||
|
||||
const addTheme = (newTheme) => {
|
||||
setCreate(false);
|
||||
if (theme[newTheme.palette.primary.main] !== undefined) {
|
||||
ToggleSnackbar(
|
||||
"top",
|
||||
"right",
|
||||
t("duplicatedThemePrimaryColor"),
|
||||
"warning"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const res = {
|
||||
...theme,
|
||||
[newTheme.palette.primary.main]: newTheme,
|
||||
};
|
||||
const resStr = JSON.stringify(res);
|
||||
setOptions({
|
||||
...options,
|
||||
themes: resStr,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const res = JSON.parse(options.themes);
|
||||
const themeString = {};
|
||||
|
||||
Object.keys(res).map((k) => {
|
||||
themeString[k] = JSON.stringify(res[k]);
|
||||
});
|
||||
|
||||
setTheme(res);
|
||||
setThemeConfig(themeString);
|
||||
}, [options.themes]);
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
API.post("/admin/setting", {
|
||||
keys: Object.keys(options),
|
||||
})
|
||||
.then((response) => {
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
option.push({
|
||||
key: k,
|
||||
value: options[k],
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("themes")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<Table aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>{t("colors")}</TableCell>
|
||||
<TableCell>
|
||||
{t("themeConfig")}
|
||||
</TableCell>
|
||||
<TableCell>{t("actions")}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{Object.keys(theme).map((k) => (
|
||||
<TableRow key={k}>
|
||||
<TableCell
|
||||
component="th"
|
||||
scope="row"
|
||||
>
|
||||
<div
|
||||
className={
|
||||
classes.colorContainer
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor:
|
||||
theme[k].palette
|
||||
.primary
|
||||
.main,
|
||||
}}
|
||||
className={
|
||||
classes.colorDot
|
||||
}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor:
|
||||
theme[k].palette
|
||||
.secondary
|
||||
.main,
|
||||
}}
|
||||
className={
|
||||
classes.colorDot
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<TextField
|
||||
error={themeConfigError[k]}
|
||||
helperText={
|
||||
themeConfigError[k] &&
|
||||
t("wrongFormat")
|
||||
}
|
||||
fullWidth
|
||||
multiline
|
||||
onChange={(e) => {
|
||||
setThemeConfig({
|
||||
...themeConfig,
|
||||
[k]: e.target.value,
|
||||
});
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
try {
|
||||
const res = JSON.parse(
|
||||
e.target.value
|
||||
);
|
||||
if (
|
||||
!(
|
||||
"palette" in
|
||||
res
|
||||
) ||
|
||||
!(
|
||||
"primary" in
|
||||
res.palette
|
||||
) ||
|
||||
!(
|
||||
"main" in
|
||||
res.palette
|
||||
.primary
|
||||
) ||
|
||||
!(
|
||||
"secondary" in
|
||||
res.palette
|
||||
) ||
|
||||
!(
|
||||
"main" in
|
||||
res.palette
|
||||
.secondary
|
||||
)
|
||||
) {
|
||||
throw "error";
|
||||
}
|
||||
setTheme({
|
||||
...theme,
|
||||
[k]: res,
|
||||
});
|
||||
} catch (e) {
|
||||
setThemeConfigError(
|
||||
{
|
||||
...themeConfigError,
|
||||
[k]: true,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
setThemeConfigError({
|
||||
...themeConfigError,
|
||||
[k]: false,
|
||||
});
|
||||
}}
|
||||
value={themeConfig[k]}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
deleteTheme(k)
|
||||
}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
style={{ marginTop: 8 }}
|
||||
onClick={() => setCreate(true)}
|
||||
>
|
||||
{t("createNewTheme")}
|
||||
</Button>
|
||||
</div>
|
||||
<Alert severity="info" style={{ marginTop: 8 }}>
|
||||
<Typography variant="body2">
|
||||
<Trans
|
||||
i18nKey={"settings.themeConfigDes"}
|
||||
ns={"dashboard"}
|
||||
components={[
|
||||
<Link
|
||||
key={0}
|
||||
href={t("themeConfigDoc")}
|
||||
target={"_blank"}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</Typography>
|
||||
</Alert>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("defaultTheme")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.defaultTheme}
|
||||
onChange={handleChange("defaultTheme")}
|
||||
>
|
||||
{Object.keys(theme).map((k) => (
|
||||
<MenuItem key={k} value={k}>
|
||||
<div
|
||||
className={
|
||||
classes.colorContainer
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor:
|
||||
theme[k].palette
|
||||
.primary.main,
|
||||
}}
|
||||
className={classes.colorDot}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor:
|
||||
theme[k].palette
|
||||
.secondary.main,
|
||||
}}
|
||||
className={classes.colorDot}
|
||||
/>
|
||||
</div>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("defaultThemeDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("appearance")}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("personalFileListView")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.home_view_method}
|
||||
onChange={handleChange("home_view_method")}
|
||||
required
|
||||
>
|
||||
<MenuItem value={"icon"}>
|
||||
{tApp("fileManager.gridViewLarge")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"smallIcon"}>
|
||||
{tApp("fileManager.gridViewSmall")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"list"}>
|
||||
{tApp("fileManager.listView")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("personalFileListViewDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("sharedFileListView")}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={options.share_view_method}
|
||||
onChange={handleChange("share_view_method")}
|
||||
required
|
||||
>
|
||||
<MenuItem value={"icon"}>
|
||||
{tApp("fileManager.gridViewLarge")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"smallIcon"}>
|
||||
{tApp("fileManager.gridViewSmall")}
|
||||
</MenuItem>
|
||||
<MenuItem value={"list"}>
|
||||
{tApp("fileManager.listView")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("sharedFileListViewDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<CreateTheme
|
||||
onSubmit={addTheme}
|
||||
open={create}
|
||||
onClose={() => setCreate(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
248
src/component/Admin/Setting/ThumbGenerators.js
Normal file
248
src/component/Admin/Setting/ThumbGenerators.js
Normal file
@@ -0,0 +1,248 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Accordion from "@material-ui/core/Accordion";
|
||||
import AccordionSummary from "@material-ui/core/AccordionSummary";
|
||||
import AccordionDetails from "@material-ui/core/AccordionDetails";
|
||||
import Checkbox from "@material-ui/core/Checkbox";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import { Button, TextField } from "@material-ui/core";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import API from "../../../middleware/Api";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
width: "100%",
|
||||
},
|
||||
secondaryHeading: {
|
||||
fontSize: theme.typography.pxToRem(15),
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
column: {
|
||||
flexBasis: "33.33%",
|
||||
},
|
||||
details: {
|
||||
display: "block",
|
||||
},
|
||||
}));
|
||||
|
||||
const generators = [
|
||||
{
|
||||
name: "policyBuiltin",
|
||||
des: "policyBuiltinDes",
|
||||
readOnly: true,
|
||||
},
|
||||
{
|
||||
name: "libreOffice",
|
||||
des: "libreOfficeDes",
|
||||
enableFlag: "thumb_libreoffice_enabled",
|
||||
executableSetting: "thumb_libreoffice_path",
|
||||
inputs: [
|
||||
{
|
||||
name: "thumb_libreoffice_exts",
|
||||
label: "generatorExts",
|
||||
des: "generatorExtsDes",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "vips",
|
||||
des: "vipsDes",
|
||||
enableFlag: "thumb_vips_enabled",
|
||||
executableSetting: "thumb_vips_path",
|
||||
inputs: [
|
||||
{
|
||||
name: "thumb_vips_exts",
|
||||
label: "generatorExts",
|
||||
des: "generatorExtsDes",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "ffmpeg",
|
||||
des: "ffmpegDes",
|
||||
enableFlag: "thumb_ffmpeg_enabled",
|
||||
executableSetting: "thumb_ffmpeg_path",
|
||||
inputs: [
|
||||
{
|
||||
name: "thumb_ffmpeg_exts",
|
||||
label: "generatorExts",
|
||||
des: "generatorExtsDes",
|
||||
},
|
||||
{
|
||||
name: "thumb_ffmpeg_seek",
|
||||
label: "ffmpegSeek",
|
||||
des: "ffmpegSeekDes",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "cloudreveBuiltin",
|
||||
des: "cloudreveBuiltinDes",
|
||||
enableFlag: "thumb_builtin_enabled",
|
||||
},
|
||||
];
|
||||
|
||||
export default function ThumbGenerators({ options, setOptions }) {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const [loading, setLoading] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const testExecutable = (name, executable) => {
|
||||
setLoading(true);
|
||||
API.post("/admin/test/thumb", {
|
||||
name,
|
||||
executable,
|
||||
})
|
||||
.then((response) => {
|
||||
ToggleSnackbar(
|
||||
"top",
|
||||
"right",
|
||||
t("executableTestSuccess", { version: response.data }),
|
||||
"success"
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleEnableChange = (name) => (event) => {
|
||||
const newOpts = {
|
||||
...options,
|
||||
[name]: event.target.checked ? "1" : "0",
|
||||
};
|
||||
setOptions(newOpts);
|
||||
|
||||
if (
|
||||
newOpts["thumb_libreoffice_enabled"] === "1" &&
|
||||
newOpts["thumb_builtin_enabled"] === "0" &&
|
||||
newOpts["thumb_vips_enabled"] === "0"
|
||||
) {
|
||||
ToggleSnackbar(
|
||||
"top",
|
||||
"center",
|
||||
t("thumbDependencyWarning"),
|
||||
"warning"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
{generators.map((generator) => (
|
||||
<Accordion key={generator.name}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-label="Expand"
|
||||
aria-controls="additional-actions1-content"
|
||||
id="additional-actions1-header"
|
||||
>
|
||||
<FormControlLabel
|
||||
aria-label="Acknowledge"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onFocus={(event) => event.stopPropagation()}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={
|
||||
generator.readOnly ||
|
||||
options[generator.enableFlag] === "1"
|
||||
}
|
||||
onChange={handleEnableChange(
|
||||
generator.enableFlag
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t(generator.name)}
|
||||
disabled={generator.readOnly}
|
||||
/>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails className={classes.details}>
|
||||
<Typography color="textSecondary">
|
||||
{t(generator.des)}
|
||||
</Typography>
|
||||
{generator.executableSetting && (
|
||||
<FormControl margin="normal" fullWidth>
|
||||
<TextField
|
||||
label={t("executable")}
|
||||
variant="outlined"
|
||||
value={options[generator.executableSetting]}
|
||||
onChange={handleChange(
|
||||
generator.executableSetting
|
||||
)}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<Button
|
||||
disabled={loading}
|
||||
onClick={() =>
|
||||
testExecutable(
|
||||
generator.name,
|
||||
options[
|
||||
generator
|
||||
.executableSetting
|
||||
]
|
||||
)
|
||||
}
|
||||
color="primary"
|
||||
>
|
||||
{t("executableTest")}
|
||||
</Button>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("executableDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
)}
|
||||
{generator.inputs &&
|
||||
generator.inputs.map((input) => (
|
||||
<FormControl
|
||||
key={input.name}
|
||||
margin="normal"
|
||||
fullWidth
|
||||
>
|
||||
<TextField
|
||||
label={t(input.label)}
|
||||
variant="outlined"
|
||||
value={options[input.name]}
|
||||
onChange={handleChange(input.name)}
|
||||
required={!!input.required}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t(input.des)}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
))}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
407
src/component/Admin/Setting/UploadDownload.js
Normal file
407
src/component/Admin/Setting/UploadDownload.js
Normal file
@@ -0,0 +1,407 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Input from "@material-ui/core/Input";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import API from "../../../middleware/Api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import SizeInput from "../Common/SizeInput";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import { toggleSnackbar } from "../../../redux/explorer";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
marginLeft: 100,
|
||||
},
|
||||
marginBottom: 40,
|
||||
},
|
||||
form: {
|
||||
maxWidth: 400,
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
},
|
||||
formContainer: {
|
||||
[theme.breakpoints.up("md")]: {
|
||||
padding: "0px 24px 0 24px",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function UploadDownload() {
|
||||
const { t } = useTranslation("dashboard", { keyPrefix: "settings" });
|
||||
const classes = useStyles();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [options, setOptions] = useState({
|
||||
max_worker_num: "1",
|
||||
max_parallel_transfer: "1",
|
||||
temp_path: "",
|
||||
chunk_retries: "0",
|
||||
archive_timeout: "0",
|
||||
download_timeout: "0",
|
||||
preview_timeout: "0",
|
||||
doc_preview_timeout: "0",
|
||||
upload_credential_timeout: "0",
|
||||
upload_session_timeout: "0",
|
||||
slave_api_timeout: "0",
|
||||
onedrive_monitor_timeout: "0",
|
||||
share_download_session_timeout: "0",
|
||||
onedrive_callback_check: "0",
|
||||
reset_after_upload_failed: "0",
|
||||
onedrive_source_timeout: "0",
|
||||
slave_node_retry: "0",
|
||||
slave_ping_interval: "0",
|
||||
slave_recover_interval: "0",
|
||||
slave_transfer_timeout: "0",
|
||||
use_temp_chunk_buffer: "1",
|
||||
public_resource_maxage: "0",
|
||||
});
|
||||
|
||||
const handleCheckChange = (name) => (event) => {
|
||||
const value = event.target.checked ? "1" : "0";
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = (name) => (event) => {
|
||||
setOptions({
|
||||
...options,
|
||||
[name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
API.post("/admin/setting", {
|
||||
keys: Object.keys(options),
|
||||
})
|
||||
.then((response) => {
|
||||
setOptions(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const option = [];
|
||||
Object.keys(options).forEach((k) => {
|
||||
option.push({
|
||||
key: k,
|
||||
value: options[k],
|
||||
});
|
||||
});
|
||||
API.patch("/admin/setting", {
|
||||
options: option,
|
||||
})
|
||||
.then(() => {
|
||||
ToggleSnackbar("top", "right", t("saved"), "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={submit}>
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("transportation")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("workerNum")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.max_worker_num}
|
||||
onChange={handleChange("max_worker_num")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("workerNumDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("transitParallelNum")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.max_parallel_transfer}
|
||||
onChange={handleChange(
|
||||
"max_parallel_transfer"
|
||||
)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("transitParallelNumDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("tempFolder")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
value={options.temp_path}
|
||||
onChange={handleChange("temp_path")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("tempFolderDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t("failedChunkRetry")}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 0,
|
||||
step: 1,
|
||||
}}
|
||||
value={options.chunk_retries}
|
||||
onChange={handleChange("chunk_retries")}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("failedChunkRetryDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.use_temp_chunk_buffer ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
"use_temp_chunk_buffer"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("cacheChunks")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("cacheChunksDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div className={classes.form}>
|
||||
<FormControl fullWidth>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={
|
||||
options.reset_after_upload_failed ===
|
||||
"1"
|
||||
}
|
||||
onChange={handleCheckChange(
|
||||
"reset_after_upload_failed"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={t("resetConnection")}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t("resetConnectionDes")}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("expirationDuration")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
{[
|
||||
{
|
||||
name: "batchDownload",
|
||||
field: "archive_timeout",
|
||||
},
|
||||
{
|
||||
name: "downloadSession",
|
||||
field: "download_timeout",
|
||||
},
|
||||
{
|
||||
name: "previewURL",
|
||||
field: "preview_timeout",
|
||||
},
|
||||
{
|
||||
name: "docPreviewURL",
|
||||
field: "doc_preview_timeout",
|
||||
},
|
||||
{
|
||||
name: "staticResourceCache",
|
||||
field: "public_resource_maxage",
|
||||
des: "staticResourceCacheDes",
|
||||
},
|
||||
{
|
||||
name: "uploadSession",
|
||||
field: "upload_session_timeout",
|
||||
des: "uploadSessionDes",
|
||||
},
|
||||
{
|
||||
name: "downloadSessionForShared",
|
||||
field: "share_download_session_timeout",
|
||||
des: "downloadSessionForSharedDes",
|
||||
},
|
||||
{
|
||||
name: "onedriveMonitorInterval",
|
||||
field: "onedrive_monitor_timeout",
|
||||
des: "onedriveMonitorIntervalDes",
|
||||
},
|
||||
{
|
||||
name: "onedriveCallbackTolerance",
|
||||
field: "onedrive_callback_check",
|
||||
des: "onedriveCallbackToleranceDes",
|
||||
},
|
||||
{
|
||||
name: "onedriveDownloadURLCache",
|
||||
field: "onedrive_source_timeout",
|
||||
des: "onedriveDownloadURLCacheDes",
|
||||
},
|
||||
].map((input) => (
|
||||
<div key={input.name} className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t(input.name)}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options[input.field]}
|
||||
onChange={handleChange(input.field)}
|
||||
required
|
||||
/>
|
||||
{input.des && (
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t(input.des)}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t("nodesCommunication")}
|
||||
</Typography>
|
||||
<div className={classes.formContainer}>
|
||||
{[
|
||||
{
|
||||
name: "slaveAPIExpiration",
|
||||
field: "slave_api_timeout",
|
||||
des: "slaveAPIExpirationDes",
|
||||
},
|
||||
{
|
||||
name: "heartbeatInterval",
|
||||
field: "slave_ping_interval",
|
||||
des: "heartbeatIntervalDes",
|
||||
},
|
||||
{
|
||||
name: "heartbeatFailThreshold",
|
||||
field: "slave_node_retry",
|
||||
des: "heartbeatFailThresholdDes",
|
||||
},
|
||||
{
|
||||
name: "heartbeatRecoverModeInterval",
|
||||
field: "slave_recover_interval",
|
||||
des: "heartbeatRecoverModeIntervalDes",
|
||||
},
|
||||
{
|
||||
name: "slaveTransitExpiration",
|
||||
field: "slave_transfer_timeout",
|
||||
des: "slaveTransitExpirationDes",
|
||||
},
|
||||
].map((input) => (
|
||||
<div key={input.name} className={classes.form}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="component-helper">
|
||||
{t(input.name)}
|
||||
</InputLabel>
|
||||
<Input
|
||||
type={"number"}
|
||||
inputProps={{
|
||||
min: 1,
|
||||
step: 1,
|
||||
}}
|
||||
value={options[input.field]}
|
||||
onChange={handleChange(input.field)}
|
||||
required
|
||||
/>
|
||||
<FormHelperText id="component-helper-text">
|
||||
{t(input.des)}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
disabled={loading}
|
||||
type={"submit"}
|
||||
variant={"contained"}
|
||||
color={"primary"}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
1222
src/component/Admin/Setting/VAS.js
Normal file
1222
src/component/Admin/Setting/VAS.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user