import { ReactNode, useEffect, useState, useRef, useCallback } from "react"; import { useLockFn } from "ahooks"; import yaml from "js-yaml"; import { useTranslation } from "react-i18next"; import { Autocomplete, Button, Dialog, DialogActions, DialogContent, DialogTitle, List, ListItem, ListItemText, MenuItem, Select, TextField, styled, } from "@mui/material"; import { useThemeMode } from "@/services/states"; import { readProfileFile, saveProfileFile } from "@/services/cmds"; import { Notice } from "@/components/base"; import getSystem from "@/utils/get-system"; import MonacoEditor from "react-monaco-editor"; import * as monaco from "monaco-editor"; import { nanoid } from "nanoid"; interface Props { title?: string | ReactNode; property: string; open: boolean; onClose: () => void; onChange?: (prev?: string, curr?: string) => void; } const RuleTypeList = [ "DOMAIN", "DOMAIN-SUFFIX", "DOMAIN-KEYWORD", "DOMAIN-REGEX", "GEOSITE", "IP-CIDR", "IP-SUFFIX", "IP-ASN", "GEOIP", "SRC-GEOIP", "SRC-IP-ASN", "SRC-IP-CIDR", "SRC-IP-SUFFIX", "DST-PORT", "SRC-PORT", "IN-PORT", "IN-TYPE", "IN-USER", "IN-NAME", "PROCESS-PATH", "PROCESS-PATH-REGEX", "PROCESS-NAME", "PROCESS-NAME-REGEX", "UID", "NETWORK", "DSCP", "RULE-SET", "SUB-RULE", "MATCH", ]; export const RulesEditorViewer = (props: Props) => { const { title, property, open, onClose, onChange } = props; const { t } = useTranslation(); const editorRef = useRef(); // 编辑器实例 const monacoRef = useRef(); // monaco 实例 const monacoHoverProviderRef = useRef(); // monaco 注册缓存 const monacoCompletionItemProviderRef = useRef(); // monaco 注册缓存 // 获取编辑器实例 const editorDidMountHandle = useCallback( (editor: monaco.editor.IStandaloneCodeEditor, monacoIns: typeof monaco) => { editorRef.current = editor; monacoRef.current = monacoIns; }, [] ); const themeMode = useThemeMode(); const [prevData, setPrevData] = useState(""); const [currData, setCurrData] = useState(""); const [method, setMethod] = useState("append"); const [ruleType, setRuleType] = useState("DOMAIN"); const [ruleContent, setRuleContent] = useState(""); const [proxyPolicy, setProxyPolicy] = useState(""); const uri = monaco.Uri.parse(`${nanoid()}`); const model = monaco.editor.createModel(prevData, "yaml", uri); const fetchContent = async () => { let data = await readProfileFile(property); setCurrData(data); setPrevData(data); }; const addSeq = async () => { let obj = yaml.load(currData) as ISeqProfileConfig; if (!obj.prepend) { obj = { prepend: [], append: [], delete: [] }; } switch (method) { case "append": { obj.append.push(`${ruleType},${ruleContent},${proxyPolicy}`); break; } case "prepend": { obj.prepend.push(`${ruleType},${ruleContent},${proxyPolicy}`); break; } case "delete": { obj.delete.push(`${ruleType},${ruleContent},${proxyPolicy}`); break; } } let raw = yaml.dump(obj); await saveProfileFile(property, raw); setCurrData(raw); }; useEffect(() => { fetchContent(); }, []); useEffect(() => { return () => { if (editorRef.current) { editorRef.current.dispose(); } monacoCompletionItemProviderRef.current?.dispose(); monacoHoverProviderRef.current?.dispose(); }; }, [open]); const onSave = useLockFn(async () => { try { await saveProfileFile(property, currData); onChange?.(prevData, currData); onClose(); } catch (err: any) { Notice.error(err.message || err.toString()); } }); return ( {title ?? t("Edit File")}
{ if (v) setRuleType(v); }} renderInput={(params) => } /> { setRuleContent(e.target.value); }} /> { setProxyPolicy(e.target.value); }} />
); }; const Item = styled(ListItem)(() => ({ padding: "5px 2px", }));