|
@@ -16,11 +16,11 @@ import {
|
|
TextField,
|
|
TextField,
|
|
styled,
|
|
styled,
|
|
} from "@mui/material";
|
|
} from "@mui/material";
|
|
-import { useThemeMode } from "@/services/states";
|
|
|
|
|
|
+
|
|
import { readProfileFile, saveProfileFile } from "@/services/cmds";
|
|
import { readProfileFile, saveProfileFile } from "@/services/cmds";
|
|
import { Notice, Switch } from "@/components/base";
|
|
import { Notice, Switch } from "@/components/base";
|
|
import getSystem from "@/utils/get-system";
|
|
import getSystem from "@/utils/get-system";
|
|
-import MonacoEditor from "react-monaco-editor";
|
|
|
|
|
|
+import { RuleItem } from "@/components/profile/rule-item";
|
|
|
|
|
|
interface Props {
|
|
interface Props {
|
|
profileUid: string;
|
|
profileUid: string;
|
|
@@ -119,10 +119,7 @@ export const RulesEditorViewer = (props: Props) => {
|
|
const { title, profileUid, property, open, onClose, onChange } = props;
|
|
const { title, profileUid, property, open, onClose, onChange } = props;
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
|
|
|
|
- const themeMode = useThemeMode();
|
|
|
|
const [prevData, setPrevData] = useState("");
|
|
const [prevData, setPrevData] = useState("");
|
|
- const [currData, setCurrData] = useState("");
|
|
|
|
- const [rule, setRule] = useState("");
|
|
|
|
const [ruleType, setRuleType] =
|
|
const [ruleType, setRuleType] =
|
|
useState<(typeof RuleTypeList)[number]>("DOMAIN");
|
|
useState<(typeof RuleTypeList)[number]>("DOMAIN");
|
|
const [ruleContent, setRuleContent] = useState("");
|
|
const [ruleContent, setRuleContent] = useState("");
|
|
@@ -131,28 +128,17 @@ export const RulesEditorViewer = (props: Props) => {
|
|
const [proxyPolicyList, setProxyPolicyList] = useState<string[]>([]);
|
|
const [proxyPolicyList, setProxyPolicyList] = useState<string[]>([]);
|
|
const [ruleList, setRuleList] = useState<string[]>([]);
|
|
const [ruleList, setRuleList] = useState<string[]>([]);
|
|
|
|
|
|
- const editorOptions = {
|
|
|
|
- tabSize: 2,
|
|
|
|
- minimap: { enabled: false },
|
|
|
|
- mouseWheelZoom: true,
|
|
|
|
- quickSuggestions: {
|
|
|
|
- strings: true,
|
|
|
|
- comments: true,
|
|
|
|
- other: true,
|
|
|
|
- },
|
|
|
|
- padding: {
|
|
|
|
- top: 33,
|
|
|
|
- },
|
|
|
|
- fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${
|
|
|
|
- getSystem() === "windows" ? ", twemoji mozilla" : ""
|
|
|
|
- }`,
|
|
|
|
- fontLigatures: true,
|
|
|
|
- smoothScrolling: true,
|
|
|
|
- };
|
|
|
|
|
|
+ const [prependSeq, setPrependSeq] = useState<string[]>([]);
|
|
|
|
+ const [appendSeq, setAppendSeq] = useState<string[]>([]);
|
|
|
|
+ const [deleteSeq, setDeleteSeq] = useState<string[]>([]);
|
|
|
|
|
|
const fetchContent = async () => {
|
|
const fetchContent = async () => {
|
|
let data = await readProfileFile(property);
|
|
let data = await readProfileFile(property);
|
|
- setCurrData(data);
|
|
|
|
|
|
+ let obj = yaml.load(data) as { prepend: []; append: []; delete: [] };
|
|
|
|
+
|
|
|
|
+ setPrependSeq(obj.prepend || []);
|
|
|
|
+ setAppendSeq(obj.append || []);
|
|
|
|
+ setDeleteSeq(obj.delete || []);
|
|
setPrevData(data);
|
|
setPrevData(data);
|
|
};
|
|
};
|
|
|
|
|
|
@@ -170,42 +156,6 @@ export const RulesEditorViewer = (props: Props) => {
|
|
setRuleList(obj.rules);
|
|
setRuleList(obj.rules);
|
|
};
|
|
};
|
|
|
|
|
|
- const addSeq = async (method: "prepend" | "append" | "delete") => {
|
|
|
|
- let obj = yaml.load(currData) as ISeqProfileConfig;
|
|
|
|
- if (!obj.prepend) {
|
|
|
|
- obj = { prepend: [], append: [], delete: [] };
|
|
|
|
- }
|
|
|
|
- switch (method) {
|
|
|
|
- case "append": {
|
|
|
|
- obj.append.push(
|
|
|
|
- `${ruleType}${
|
|
|
|
- ruleType === "MATCH" ? "" : "," + ruleContent
|
|
|
|
- },${proxyPolicy}${
|
|
|
|
- NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : ""
|
|
|
|
- }`
|
|
|
|
- );
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- case "prepend": {
|
|
|
|
- obj.prepend.push(
|
|
|
|
- `${ruleType}${
|
|
|
|
- ruleType === "MATCH" ? "" : "," + ruleContent
|
|
|
|
- },${proxyPolicy}${
|
|
|
|
- NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : ""
|
|
|
|
- }`
|
|
|
|
- );
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- case "delete": {
|
|
|
|
- obj.delete.push(rule);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- let raw = yaml.dump(obj);
|
|
|
|
-
|
|
|
|
- setCurrData(raw);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
fetchContent();
|
|
fetchContent();
|
|
fetchProfile();
|
|
fetchProfile();
|
|
@@ -213,6 +163,11 @@ export const RulesEditorViewer = (props: Props) => {
|
|
|
|
|
|
const onSave = useLockFn(async () => {
|
|
const onSave = useLockFn(async () => {
|
|
try {
|
|
try {
|
|
|
|
+ let currData = yaml.dump({
|
|
|
|
+ prepend: prependSeq,
|
|
|
|
+ append: appendSeq,
|
|
|
|
+ delete: deleteSeq,
|
|
|
|
+ });
|
|
await saveProfileFile(property, currData);
|
|
await saveProfileFile(property, currData);
|
|
onChange?.(prevData, currData);
|
|
onChange?.(prevData, currData);
|
|
onClose();
|
|
onClose();
|
|
@@ -288,7 +243,15 @@ export const RulesEditorViewer = (props: Props) => {
|
|
fullWidth
|
|
fullWidth
|
|
variant="contained"
|
|
variant="contained"
|
|
onClick={() => {
|
|
onClick={() => {
|
|
- addSeq("prepend");
|
|
|
|
|
|
+ let raw = `${ruleType}${
|
|
|
|
+ ruleType === "MATCH" ? "" : "," + ruleContent
|
|
|
|
+ },${proxyPolicy}${
|
|
|
|
+ NoResolveList.includes(ruleType) && noResolve
|
|
|
|
+ ? ",no-resolve"
|
|
|
|
+ : ""
|
|
|
|
+ }`;
|
|
|
|
+ if (prependSeq.includes(raw)) return;
|
|
|
|
+ setPrependSeq([...prependSeq, raw]);
|
|
}}
|
|
}}
|
|
>
|
|
>
|
|
{t("Add Prepend Rule")}
|
|
{t("Add Prepend Rule")}
|
|
@@ -299,55 +262,82 @@ export const RulesEditorViewer = (props: Props) => {
|
|
fullWidth
|
|
fullWidth
|
|
variant="contained"
|
|
variant="contained"
|
|
onClick={() => {
|
|
onClick={() => {
|
|
- addSeq("append");
|
|
|
|
|
|
+ let raw = `${ruleType}${
|
|
|
|
+ ruleType === "MATCH" ? "" : "," + ruleContent
|
|
|
|
+ },${proxyPolicy}${
|
|
|
|
+ NoResolveList.includes(ruleType) && noResolve
|
|
|
|
+ ? ",no-resolve"
|
|
|
|
+ : ""
|
|
|
|
+ }`;
|
|
|
|
+ if (appendSeq.includes(raw)) return;
|
|
|
|
+ setAppendSeq([...appendSeq, raw]);
|
|
}}
|
|
}}
|
|
>
|
|
>
|
|
{t("Add Append Rule")}
|
|
{t("Add Append Rule")}
|
|
</Button>
|
|
</Button>
|
|
</Item>
|
|
</Item>
|
|
- <Item>
|
|
|
|
- <Autocomplete
|
|
|
|
- fullWidth
|
|
|
|
- size="small"
|
|
|
|
- sx={{ minWidth: "240px" }}
|
|
|
|
- value={rule}
|
|
|
|
- options={ruleList}
|
|
|
|
- onChange={(_, v) => {
|
|
|
|
- if (v) setRule(v);
|
|
|
|
- }}
|
|
|
|
- renderInput={(params) => <TextField {...params} />}
|
|
|
|
- />
|
|
|
|
- </Item>
|
|
|
|
- <Item>
|
|
|
|
- <Button
|
|
|
|
- fullWidth
|
|
|
|
- variant="contained"
|
|
|
|
- onClick={() => {
|
|
|
|
- addSeq("delete");
|
|
|
|
- }}
|
|
|
|
- >
|
|
|
|
- {t("Delete Rule")}
|
|
|
|
- </Button>
|
|
|
|
- </Item>
|
|
|
|
</div>
|
|
</div>
|
|
<div
|
|
<div
|
|
style={{
|
|
style={{
|
|
display: "inline-block",
|
|
display: "inline-block",
|
|
width: "50%",
|
|
width: "50%",
|
|
height: "100%",
|
|
height: "100%",
|
|
|
|
+ overflow: "auto",
|
|
marginLeft: "10px",
|
|
marginLeft: "10px",
|
|
}}
|
|
}}
|
|
>
|
|
>
|
|
- <MonacoEditor
|
|
|
|
- language="yaml"
|
|
|
|
- theme={themeMode === "light" ? "vs" : "vs-dark"}
|
|
|
|
- height="100%"
|
|
|
|
- value={currData}
|
|
|
|
- onChange={(value, _) => {
|
|
|
|
- if (value) setCurrData(value);
|
|
|
|
- }}
|
|
|
|
- options={editorOptions}
|
|
|
|
- />
|
|
|
|
|
|
+ {prependSeq.length > 0 && (
|
|
|
|
+ <List sx={{ borderBottom: "solid 1px var(--divider-color)" }}>
|
|
|
|
+ {prependSeq.map((item, index) => {
|
|
|
|
+ return (
|
|
|
|
+ <RuleItem
|
|
|
|
+ key={`${item}-${index}`}
|
|
|
|
+ type="prepend"
|
|
|
|
+ ruleRaw={item}
|
|
|
|
+ onDelete={() => {
|
|
|
|
+ setPrependSeq(prependSeq.filter((v) => v !== item));
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ );
|
|
|
|
+ })}
|
|
|
|
+ </List>
|
|
|
|
+ )}
|
|
|
|
+
|
|
|
|
+ <List>
|
|
|
|
+ {ruleList.map((item, index) => {
|
|
|
|
+ return (
|
|
|
|
+ <RuleItem
|
|
|
|
+ key={`${item}-${index}`}
|
|
|
|
+ type={deleteSeq.includes(item) ? "delete" : "original"}
|
|
|
|
+ ruleRaw={item}
|
|
|
|
+ onDelete={() => {
|
|
|
|
+ if (deleteSeq.includes(item)) {
|
|
|
|
+ setDeleteSeq(deleteSeq.filter((v) => v !== item));
|
|
|
|
+ } else {
|
|
|
|
+ setDeleteSeq([...deleteSeq, item]);
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ );
|
|
|
|
+ })}
|
|
|
|
+ </List>
|
|
|
|
+
|
|
|
|
+ {appendSeq.length > 0 && (
|
|
|
|
+ <List sx={{ borderTop: "solid 1px var(--divider-color)" }}>
|
|
|
|
+ {appendSeq.map((item, index) => {
|
|
|
|
+ return (
|
|
|
|
+ <RuleItem
|
|
|
|
+ key={`${item}-${index}`}
|
|
|
|
+ type="append"
|
|
|
|
+ ruleRaw={item}
|
|
|
|
+ onDelete={() => {
|
|
|
|
+ setAppendSeq(appendSeq.filter((v) => v !== item));
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ );
|
|
|
|
+ })}
|
|
|
|
+ </List>
|
|
|
|
+ )}
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</DialogContent>
|
|
|
|
|