feat: profile enhanced mode
This commit is contained in:
parent
a43dab8057
commit
ef47a74920
@ -8,7 +8,7 @@ use crate::{
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::{path::PathBuf, process::Command};
|
use std::{path::PathBuf, process::Command};
|
||||||
use tauri::{api, State};
|
use tauri::{api, Manager, State};
|
||||||
|
|
||||||
/// get all profiles from `profiles.yaml`
|
/// get all profiles from `profiles.yaml`
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -100,6 +100,43 @@ pub fn select_profile(
|
|||||||
wrap_err!(clash.activate(&profiles))
|
wrap_err!(clash.activate(&profiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// change the profile chain
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn change_profile_chain(
|
||||||
|
chain: Option<Vec<String>>,
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
|
clash_state: State<'_, ClashState>,
|
||||||
|
profiles_state: State<'_, ProfilesState>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let clash = clash_state.0.lock().unwrap();
|
||||||
|
let mut profiles = profiles_state.0.lock().unwrap();
|
||||||
|
|
||||||
|
profiles.put_chain(chain);
|
||||||
|
|
||||||
|
app_handle
|
||||||
|
.get_window("main")
|
||||||
|
.map(|win| wrap_err!(clash.activate_enhanced(&profiles, win, false)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// manually exec enhanced profile
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn enhance_profiles(
|
||||||
|
app_handle: tauri::AppHandle,
|
||||||
|
clash_state: State<'_, ClashState>,
|
||||||
|
profiles_state: State<'_, ProfilesState>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let clash = clash_state.0.lock().unwrap();
|
||||||
|
let profiles = profiles_state.0.lock().unwrap();
|
||||||
|
|
||||||
|
app_handle
|
||||||
|
.get_window("main")
|
||||||
|
.map(|win| wrap_err!(clash.activate_enhanced(&profiles, win, false)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// delete profile item
|
/// delete profile item
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn delete_profile(
|
pub fn delete_profile(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{Profiles, Verge};
|
use super::{PrfEnhancedResult, Profiles, Verge};
|
||||||
use crate::utils::{config, dirs, help};
|
use crate::utils::{config, dirs, help};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
@ -260,6 +260,7 @@ impl Clash {
|
|||||||
let mut data = HashMap::new();
|
let mut data = HashMap::new();
|
||||||
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
||||||
|
|
||||||
|
// retry 5 times
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
match reqwest::ClientBuilder::new().no_proxy().build() {
|
match reqwest::ClientBuilder::new().no_proxy().build() {
|
||||||
Ok(client) => match client
|
Ok(client) => match client
|
||||||
@ -269,11 +270,18 @@ impl Clash {
|
|||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => break,
|
Ok(resp) => {
|
||||||
|
if resp.status() != 204 {
|
||||||
|
log::error!("failed to activate clash for status \"{}\"", resp.status());
|
||||||
|
}
|
||||||
|
// do not retry
|
||||||
|
break;
|
||||||
|
}
|
||||||
Err(err) => log::error!("failed to activate for `{err}`"),
|
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||||
},
|
},
|
||||||
Err(err) => log::error!("failed to activate for `{err}`"),
|
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||||
}
|
}
|
||||||
|
sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -294,29 +302,43 @@ impl Clash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// enhanced profiles mode
|
/// enhanced profiles mode
|
||||||
pub fn activate_enhanced(&self, profiles: &Profiles, win: tauri::Window) -> Result<()> {
|
pub fn activate_enhanced(
|
||||||
|
&self,
|
||||||
|
profiles: &Profiles,
|
||||||
|
win: tauri::Window,
|
||||||
|
delay: bool,
|
||||||
|
) -> Result<()> {
|
||||||
let event_name = help::get_uid("e");
|
let event_name = help::get_uid("e");
|
||||||
let event_name = format!("script-cb-{event_name}");
|
let event_name = format!("enhanced-cb-{event_name}");
|
||||||
|
|
||||||
let info = self.info.clone();
|
let info = self.info.clone();
|
||||||
let mut config = self.config.clone();
|
let mut config = self.config.clone();
|
||||||
|
|
||||||
// generate the payload
|
// generate the payload
|
||||||
let payload = profiles.gen_enhanced()?;
|
let payload = profiles.gen_enhanced(event_name.clone())?;
|
||||||
|
|
||||||
win.once(&event_name, move |event| {
|
win.once(&event_name, move |event| {
|
||||||
if let Some(result) = event.payload() {
|
if let Some(result) = event.payload() {
|
||||||
let gen_map: Mapping = serde_json::from_str(result).unwrap();
|
let result: PrfEnhancedResult = serde_json::from_str(result).unwrap();
|
||||||
|
|
||||||
for (key, value) in gen_map.into_iter() {
|
if let Some(data) = result.data {
|
||||||
config.insert(key, value);
|
for (key, value) in data.into_iter() {
|
||||||
|
config.insert(key, value);
|
||||||
|
}
|
||||||
|
Self::_activate(info, config).unwrap();
|
||||||
}
|
}
|
||||||
Self::_activate(info, config).unwrap();
|
|
||||||
|
log::info!("profile enhanced status {}", result.status);
|
||||||
|
|
||||||
|
result.error.map(|error| log::error!("{error}"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
sleep(Duration::from_secs(5)).await;
|
// wait the window setup during resolve app
|
||||||
|
if delay {
|
||||||
|
sleep(Duration::from_secs(2)).await;
|
||||||
|
}
|
||||||
win.emit("script-handler", payload).unwrap();
|
win.emit("script-handler", payload).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -314,6 +314,11 @@ impl Profiles {
|
|||||||
bail!("invalid uid \"{uid}\"");
|
bail!("invalid uid \"{uid}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// just change the `chain`
|
||||||
|
pub fn put_chain(&mut self, chain: Option<Vec<String>>) {
|
||||||
|
self.chain = chain;
|
||||||
|
}
|
||||||
|
|
||||||
/// find the item by the uid
|
/// find the item by the uid
|
||||||
pub fn get_item(&self, uid: &String) -> Result<&PrfItem> {
|
pub fn get_item(&self, uid: &String) -> Result<&PrfItem> {
|
||||||
if self.items.is_some() {
|
if self.items.is_some() {
|
||||||
@ -519,7 +524,7 @@ impl Profiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// gen the enhanced profiles
|
/// gen the enhanced profiles
|
||||||
pub fn gen_enhanced(&self) -> Result<PrfEnhanced> {
|
pub fn gen_enhanced(&self, callback: String) -> Result<PrfEnhanced> {
|
||||||
let current = self.gen_activate()?;
|
let current = self.gen_activate()?;
|
||||||
|
|
||||||
let chain = match self.chain.as_ref() {
|
let chain = match self.chain.as_ref() {
|
||||||
@ -535,7 +540,11 @@ impl Profiles {
|
|||||||
None => vec![],
|
None => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(PrfEnhanced { current, chain })
|
Ok(PrfEnhanced {
|
||||||
|
current,
|
||||||
|
chain,
|
||||||
|
callback,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,6 +553,17 @@ pub struct PrfEnhanced {
|
|||||||
current: Mapping,
|
current: Mapping,
|
||||||
|
|
||||||
chain: Vec<PrfData>,
|
chain: Vec<PrfData>,
|
||||||
|
|
||||||
|
callback: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PrfEnhancedResult {
|
||||||
|
pub data: Option<Mapping>,
|
||||||
|
|
||||||
|
pub status: String,
|
||||||
|
|
||||||
|
pub error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -94,6 +94,8 @@ fn main() -> std::io::Result<()> {
|
|||||||
cmds::select_profile,
|
cmds::select_profile,
|
||||||
cmds::get_profiles,
|
cmds::get_profiles,
|
||||||
cmds::sync_profiles,
|
cmds::sync_profiles,
|
||||||
|
cmds::enhance_profiles,
|
||||||
|
cmds::change_profile_chain
|
||||||
]);
|
]);
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -26,9 +26,10 @@ pub fn resolve_setup(app: &App) {
|
|||||||
*profiles = Profiles::read_file();
|
*profiles = Profiles::read_file();
|
||||||
log_if_err!(clash.activate(&profiles));
|
log_if_err!(clash.activate(&profiles));
|
||||||
|
|
||||||
app
|
match app.get_window("main") {
|
||||||
.get_window("main")
|
Some(win) => log_if_err!(clash.activate_enhanced(&profiles, win, true)),
|
||||||
.map(|win| log_if_err!(clash.activate_enhanced(&profiles, win)));
|
None => log::error!("failed to get window for enhanced profiles"),
|
||||||
|
};
|
||||||
|
|
||||||
verge.init_sysproxy(clash.info.port.clone());
|
verge.init_sysproxy(clash.info.port.clone());
|
||||||
// enable tun mode
|
// enable tun mode
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { useLockFn } from "ahooks";
|
|
||||||
import { useSWRConfig } from "swr";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
alpha,
|
alpha,
|
||||||
@ -12,7 +10,7 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { CmdType } from "../../services/types";
|
import { CmdType } from "../../services/types";
|
||||||
import { deleteProfile, viewProfile } from "../../services/cmds";
|
import { viewProfile } from "../../services/cmds";
|
||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
import ProfileEdit from "./profile-edit";
|
import ProfileEdit from "./profile-edit";
|
||||||
import Notice from "../base/base-notice";
|
import Notice from "../base/base-notice";
|
||||||
@ -37,6 +35,7 @@ interface Props {
|
|||||||
onDisable: () => void;
|
onDisable: () => void;
|
||||||
onMoveTop: () => void;
|
onMoveTop: () => void;
|
||||||
onMoveEnd: () => void;
|
onMoveEnd: () => void;
|
||||||
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// profile enhanced item
|
// profile enhanced item
|
||||||
@ -48,10 +47,10 @@ const ProfileMore = (props: Props) => {
|
|||||||
onDisable,
|
onDisable,
|
||||||
onMoveTop,
|
onMoveTop,
|
||||||
onMoveEnd,
|
onMoveEnd,
|
||||||
|
onDelete,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { type } = itemData;
|
const { type } = itemData;
|
||||||
const { mutate } = useSWRConfig();
|
|
||||||
const [anchorEl, setAnchorEl] = useState<any>(null);
|
const [anchorEl, setAnchorEl] = useState<any>(null);
|
||||||
const [position, setPosition] = useState({ left: 0, top: 0 });
|
const [position, setPosition] = useState({ left: 0, top: 0 });
|
||||||
const [editOpen, setEditOpen] = useState(false);
|
const [editOpen, setEditOpen] = useState(false);
|
||||||
@ -70,30 +69,25 @@ const ProfileMore = (props: Props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = useLockFn(async () => {
|
const closeWrapper = (fn: () => void) => () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
try {
|
return fn();
|
||||||
await deleteProfile(itemData.uid);
|
};
|
||||||
mutate("getProfiles");
|
|
||||||
} catch (err: any) {
|
|
||||||
Notice.error(err?.message || err.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const enableMenu = [
|
const enableMenu = [
|
||||||
{ label: "Disable", handler: onDisable },
|
{ label: "Disable", handler: closeWrapper(onDisable) },
|
||||||
{ label: "Edit", handler: onEdit },
|
{ label: "Edit", handler: onEdit },
|
||||||
{ label: "View File", handler: onView },
|
{ label: "View File", handler: onView },
|
||||||
{ label: "To Top", handler: onMoveTop },
|
{ label: "To Top", handler: closeWrapper(onMoveTop) },
|
||||||
{ label: "To End", handler: onMoveEnd },
|
{ label: "To End", handler: closeWrapper(onMoveEnd) },
|
||||||
{ label: "Delete", handler: onDelete },
|
{ label: "Delete", handler: closeWrapper(onDelete) },
|
||||||
];
|
];
|
||||||
|
|
||||||
const disableMenu = [
|
const disableMenu = [
|
||||||
{ label: "Enable", handler: onEnable },
|
{ label: "Enable", handler: closeWrapper(onEnable) },
|
||||||
{ label: "Edit", handler: onEdit },
|
{ label: "Edit", handler: onEdit },
|
||||||
{ label: "View File", handler: onView },
|
{ label: "View File", handler: onView },
|
||||||
{ label: "Delete", handler: onDelete },
|
{ label: "Delete", handler: closeWrapper(onDelete) },
|
||||||
];
|
];
|
||||||
|
|
||||||
const boxStyle = {
|
const boxStyle = {
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import useSWR, { useSWRConfig } from "swr";
|
import useSWR, { useSWRConfig } from "swr";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Box, Button, Grid, TextField } from "@mui/material";
|
import { Box, Button, Grid, TextField } from "@mui/material";
|
||||||
import {
|
import {
|
||||||
getProfiles,
|
getProfiles,
|
||||||
selectProfile,
|
|
||||||
patchProfile,
|
patchProfile,
|
||||||
|
deleteProfile,
|
||||||
|
selectProfile,
|
||||||
importProfile,
|
importProfile,
|
||||||
|
changeProfileChain,
|
||||||
} from "../services/cmds";
|
} from "../services/cmds";
|
||||||
import { getProxies, updateProxy } from "../services/api";
|
import { getProxies, updateProxy } from "../services/api";
|
||||||
import Notice from "../components/base/base-notice";
|
import Notice from "../components/base/base-notice";
|
||||||
@ -25,13 +27,20 @@ const ProfilePage = () => {
|
|||||||
const { data: profiles = {} } = useSWR("getProfiles", getProfiles);
|
const { data: profiles = {} } = useSWR("getProfiles", getProfiles);
|
||||||
|
|
||||||
const { regularItems, enhanceItems } = useMemo(() => {
|
const { regularItems, enhanceItems } = useMemo(() => {
|
||||||
const { items = [] } = profiles;
|
const items = profiles.items || [];
|
||||||
const regularItems = items.filter((i) =>
|
const chain = profiles.chain || [];
|
||||||
["local", "remote"].includes(i.type!)
|
|
||||||
);
|
const type1 = ["local", "remote"];
|
||||||
const enhanceItems = items.filter((i) =>
|
const type2 = ["merge", "script"];
|
||||||
["merge", "script"].includes(i.type!)
|
|
||||||
);
|
const regularItems = items.filter((i) => type1.includes(i.type!));
|
||||||
|
const restItems = items.filter((i) => type2.includes(i.type!));
|
||||||
|
|
||||||
|
const restMap = Object.fromEntries(restItems.map((i) => [i.uid, i]));
|
||||||
|
|
||||||
|
const enhanceItems = chain
|
||||||
|
.map((i) => restMap[i]!)
|
||||||
|
.concat(restItems.filter((i) => !chain.includes(i.uid)));
|
||||||
|
|
||||||
return { regularItems, enhanceItems };
|
return { regularItems, enhanceItems };
|
||||||
}, [profiles]);
|
}, [profiles]);
|
||||||
@ -113,10 +122,51 @@ const ProfilePage = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onEnhanceEnable = useLockFn(async (uid: string) => {});
|
/** enhanced profile mode */
|
||||||
const onEnhanceDisable = useLockFn(async (uid: string) => {});
|
|
||||||
const onMoveTop = useLockFn(async (uid: string) => {});
|
const chain = profiles.chain || [];
|
||||||
const onMoveEnd = useLockFn(async (uid: string) => {});
|
|
||||||
|
const onEnhanceEnable = useLockFn(async (uid: string) => {
|
||||||
|
if (chain.includes(uid)) return;
|
||||||
|
|
||||||
|
const newChain = [...chain, uid];
|
||||||
|
await changeProfileChain(newChain);
|
||||||
|
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onEnhanceDisable = useLockFn(async (uid: string) => {
|
||||||
|
if (!chain.includes(uid)) return;
|
||||||
|
|
||||||
|
const newChain = chain.filter((i) => i !== uid);
|
||||||
|
await changeProfileChain(newChain);
|
||||||
|
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onEnhanceDelete = useLockFn(async (uid: string) => {
|
||||||
|
try {
|
||||||
|
await onEnhanceDisable(uid);
|
||||||
|
await deleteProfile(uid);
|
||||||
|
mutate("getProfiles");
|
||||||
|
} catch (err: any) {
|
||||||
|
Notice.error(err?.message || err.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const onMoveTop = useLockFn(async (uid: string) => {
|
||||||
|
if (!chain.includes(uid)) return;
|
||||||
|
|
||||||
|
const newChain = [uid].concat(chain.filter((i) => i !== uid));
|
||||||
|
await changeProfileChain(newChain);
|
||||||
|
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onMoveEnd = useLockFn(async (uid: string) => {
|
||||||
|
if (!chain.includes(uid)) return;
|
||||||
|
|
||||||
|
const newChain = chain.filter((i) => i !== uid).concat([uid]);
|
||||||
|
await changeProfileChain(newChain);
|
||||||
|
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage title="Profiles">
|
<BasePage title="Profiles">
|
||||||
@ -164,6 +214,7 @@ const ProfilePage = () => {
|
|||||||
itemData={item}
|
itemData={item}
|
||||||
onEnable={() => onEnhanceEnable(item.uid)}
|
onEnable={() => onEnhanceEnable(item.uid)}
|
||||||
onDisable={() => onEnhanceDisable(item.uid)}
|
onDisable={() => onEnhanceDisable(item.uid)}
|
||||||
|
onDelete={() => onEnhanceDelete(item.uid)}
|
||||||
onMoveTop={() => onMoveTop(item.uid)}
|
onMoveTop={() => onMoveTop(item.uid)}
|
||||||
onMoveEnd={() => onMoveEnd(item.uid)}
|
onMoveEnd={() => onMoveEnd(item.uid)}
|
||||||
/>
|
/>
|
||||||
|
@ -9,6 +9,10 @@ export async function syncProfiles() {
|
|||||||
return invoke<void>("sync_profiles");
|
return invoke<void>("sync_profiles");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function enhanceProfiles() {
|
||||||
|
return invoke<void>("enhance_profiles");
|
||||||
|
}
|
||||||
|
|
||||||
export async function createProfile(item: Partial<CmdType.ProfileItem>) {
|
export async function createProfile(item: Partial<CmdType.ProfileItem>) {
|
||||||
return invoke<void>("create_profile", { item });
|
return invoke<void>("create_profile", { item });
|
||||||
}
|
}
|
||||||
@ -40,8 +44,8 @@ export async function selectProfile(index: string) {
|
|||||||
return invoke<void>("select_profile", { index });
|
return invoke<void>("select_profile", { index });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restartSidecar() {
|
export async function changeProfileChain(chain?: string[]) {
|
||||||
return invoke<void>("restart_sidecar");
|
return invoke<void>("change_profile_chain", { chain });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getClashInfo() {
|
export async function getClashInfo() {
|
||||||
@ -64,6 +68,10 @@ export async function getSystemProxy() {
|
|||||||
return invoke<any>("get_sys_proxy");
|
return invoke<any>("get_sys_proxy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function restartSidecar() {
|
||||||
|
return invoke<void>("restart_sidecar");
|
||||||
|
}
|
||||||
|
|
||||||
export async function killSidecars() {
|
export async function killSidecars() {
|
||||||
return invoke<any>("kill_sidecars");
|
return invoke<any>("kill_sidecars");
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,98 @@
|
|||||||
import { emit, listen } from "@tauri-apps/api/event";
|
import { emit, listen } from "@tauri-apps/api/event";
|
||||||
import { CmdType } from "./types";
|
import { CmdType } from "./types";
|
||||||
|
|
||||||
|
function toMerge(
|
||||||
|
merge: CmdType.ProfileMerge,
|
||||||
|
data: CmdType.ProfileData
|
||||||
|
): CmdType.ProfileData {
|
||||||
|
if (!merge) return data;
|
||||||
|
|
||||||
|
const newData = { ...data };
|
||||||
|
|
||||||
|
// rules
|
||||||
|
if (Array.isArray(merge["prepend-rules"])) {
|
||||||
|
if (!newData.rules) newData.rules = [];
|
||||||
|
newData.rules.unshift(...merge["prepend-rules"]);
|
||||||
|
}
|
||||||
|
if (Array.isArray(merge["append-rules"])) {
|
||||||
|
if (!newData.rules) newData.rules = [];
|
||||||
|
newData.rules.push(...merge["append-rules"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxies
|
||||||
|
if (Array.isArray(merge["prepend-proxies"])) {
|
||||||
|
if (!newData.proxies) newData.proxies = [];
|
||||||
|
newData.proxies.unshift(...merge["prepend-proxies"]);
|
||||||
|
}
|
||||||
|
if (Array.isArray(merge["append-proxies"])) {
|
||||||
|
if (!newData.proxies) newData.proxies = [];
|
||||||
|
newData.proxies.push(...merge["append-proxies"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxy-groups
|
||||||
|
if (Array.isArray(merge["prepend-proxy-groups"])) {
|
||||||
|
if (!newData["proxy-groups"]) newData["proxy-groups"] = [];
|
||||||
|
newData["proxy-groups"].unshift(...merge["prepend-proxy-groups"]);
|
||||||
|
}
|
||||||
|
if (Array.isArray(merge["append-proxy-groups"])) {
|
||||||
|
if (!newData["proxy-groups"]) newData["proxy-groups"] = [];
|
||||||
|
newData["proxy-groups"].push(...merge["append-proxy-groups"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toScript(
|
||||||
|
script: string,
|
||||||
|
data: CmdType.ProfileData
|
||||||
|
): Promise<CmdType.ProfileData> {
|
||||||
|
if (!script) {
|
||||||
|
throw new Error("miss the main function");
|
||||||
|
}
|
||||||
|
|
||||||
|
const paramsName = `__verge${Math.floor(Math.random() * 1000)}`;
|
||||||
|
const code = `'use strict';${script};return main(${paramsName});`;
|
||||||
|
const func = new Function(paramsName, code);
|
||||||
|
return func(data); // support async main function
|
||||||
|
}
|
||||||
|
|
||||||
export default function setup() {
|
export default function setup() {
|
||||||
listen("script-handler", (event) => {
|
listen("script-handler", async (event) => {
|
||||||
const payload = event.payload as CmdType.EnhancedPayload;
|
const payload = event.payload as CmdType.EnhancedPayload;
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
|
|
||||||
// setTimeout(() => {
|
let pdata = payload.current || {};
|
||||||
// try {
|
|
||||||
// const fn = eval(payload.script + "\n\nmixin");
|
|
||||||
// console.log(fn);
|
|
||||||
|
|
||||||
// const result = fn(payload.params || {});
|
for (const each of payload.chain) {
|
||||||
// console.log("result", result);
|
try {
|
||||||
// emit(payload.callback, JSON.stringify(result)).catch(console.error);
|
// process script
|
||||||
// } catch (err) {
|
if (each.item.type === "script") {
|
||||||
// console.error(err);
|
pdata = await toScript(each.script!, pdata);
|
||||||
// }
|
}
|
||||||
// }, 3000);
|
|
||||||
|
// process merge
|
||||||
|
else if (each.item.type === "merge") {
|
||||||
|
pdata = toMerge(each.merge!, pdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid type
|
||||||
|
else {
|
||||||
|
throw new Error(`invalid enhanced profile type "${each.item.type}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("step", pdata);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: CmdType.EnhancedResult = {
|
||||||
|
data: pdata,
|
||||||
|
status: "success",
|
||||||
|
};
|
||||||
|
|
||||||
|
emit(payload.callback, JSON.stringify(result)).catch(console.error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// enhanceProfiles();
|
||||||
}
|
}
|
||||||
|
@ -124,14 +124,32 @@ export namespace CmdType {
|
|||||||
system_proxy_bypass?: string;
|
system_proxy_bypass?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProfileMerge = Record<string, any>;
|
||||||
|
|
||||||
|
// partial of the clash config
|
||||||
|
export type ProfileData = Partial<{
|
||||||
|
rules: any[];
|
||||||
|
proxies: any[];
|
||||||
|
"proxy-groups": any[];
|
||||||
|
"proxy-providers": any[];
|
||||||
|
"rule-providers": any[];
|
||||||
|
}>;
|
||||||
|
|
||||||
export interface ChainItem {
|
export interface ChainItem {
|
||||||
item: ProfileItem;
|
item: ProfileItem;
|
||||||
merge?: object;
|
merge?: ProfileMerge;
|
||||||
script?: string;
|
script?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnhancedPayload {
|
export interface EnhancedPayload {
|
||||||
chain: ChainItem[];
|
chain: ChainItem[];
|
||||||
current: object;
|
current: ProfileData;
|
||||||
|
callback: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnhancedResult {
|
||||||
|
data: ProfileData;
|
||||||
|
status: string;
|
||||||
|
error?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user