feat(core): Implements core configuration file generation functionality
- Adds the bootArgs.ts utility module for generating and deleting configuration files - Integrates configuration file generation and cleanup logic into spary.vue - Integrates the tauri-plugin-fs plugin to support file system operations - Adds a Linux system proxy configuration script
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -58,3 +58,4 @@ Desktop.ini
|
|||||||
coverage/
|
coverage/
|
||||||
*.lcov
|
*.lcov
|
||||||
.junit/
|
.junit/
|
||||||
|
/cores/
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@fontsource/roboto": "5.2.7",
|
"@fontsource/roboto": "5.2.7",
|
||||||
"@mdi/font": "7.4.47",
|
"@mdi/font": "7.4.47",
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
|
"@tauri-apps/plugin-fs": "~2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"@tauri-apps/plugin-sql": "~2",
|
"@tauri-apps/plugin-sql": "~2",
|
||||||
"vue": "^3.5.21",
|
"vue": "^3.5.21",
|
||||||
|
|||||||
159
scripts/set_linux_proxy.sh
Normal file
159
scripts/set_linux_proxy.sh
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# fork from https://github.com/2dust/v2rayN/blob/899b3fc97bc5024cd514bdbc8626a5d3c98170a7/v2rayN/ServiceLib/Sample/proxy_set_linux_sh
|
||||||
|
|
||||||
|
# Function to set proxy for GNOME
|
||||||
|
set_gnome_proxy() {
|
||||||
|
local MODE=$1
|
||||||
|
local PROXY_IP=$2
|
||||||
|
local PROXY_PORT=$3
|
||||||
|
local IGNORE_HOSTS=$4
|
||||||
|
|
||||||
|
# Set the proxy mode
|
||||||
|
gsettings set org.gnome.system.proxy mode "$MODE"
|
||||||
|
|
||||||
|
if [ "$MODE" == "manual" ]; then
|
||||||
|
# List of protocols
|
||||||
|
local PROTOCOLS=("http" "https" "ftp" "socks")
|
||||||
|
|
||||||
|
# Loop through protocols to set the proxy
|
||||||
|
for PROTOCOL in "${PROTOCOLS[@]}"; do
|
||||||
|
gsettings set org.gnome.system.proxy.$PROTOCOL host "$PROXY_IP"
|
||||||
|
gsettings set org.gnome.system.proxy.$PROTOCOL port "$PROXY_PORT"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Set ignored hosts
|
||||||
|
gsettings set org.gnome.system.proxy ignore-hosts "['$IGNORE_HOSTS']"
|
||||||
|
|
||||||
|
echo "GNOME: Manual proxy settings applied."
|
||||||
|
echo "Proxy IP: $PROXY_IP"
|
||||||
|
echo "Proxy Port: $PROXY_PORT"
|
||||||
|
echo "Ignored Hosts: $IGNORE_HOSTS"
|
||||||
|
elif [ "$MODE" == "none" ]; then
|
||||||
|
echo "GNOME: Proxy disabled."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to set proxy for KDE
|
||||||
|
set_kde_proxy() {
|
||||||
|
local MODE=$1
|
||||||
|
local PROXY_IP=$2
|
||||||
|
local PROXY_PORT=$3
|
||||||
|
local IGNORE_HOSTS=$4
|
||||||
|
|
||||||
|
# Determine the correct kwriteconfig command based on KDE_SESSION_VERSION
|
||||||
|
if [ "$KDE_SESSION_VERSION" == "6" ]; then
|
||||||
|
KWRITECONFIG="kwriteconfig6"
|
||||||
|
else
|
||||||
|
KWRITECONFIG="kwriteconfig5"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# KDE uses kwriteconfig to modify proxy settings
|
||||||
|
if [ "$MODE" == "manual" ]; then
|
||||||
|
# Set proxy for all protocols
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 1
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key httpProxy "http://$PROXY_IP:$PROXY_PORT"
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key httpsProxy "http://$PROXY_IP:$PROXY_PORT"
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ftpProxy "http://$PROXY_IP:$PROXY_PORT"
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key socksProxy "http://$PROXY_IP:$PROXY_PORT"
|
||||||
|
|
||||||
|
# Set ignored hosts
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key NoProxyFor "$IGNORE_HOSTS"
|
||||||
|
|
||||||
|
echo "KDE: Manual proxy settings applied."
|
||||||
|
echo "Proxy IP: $PROXY_IP"
|
||||||
|
echo "Proxy Port: $PROXY_PORT"
|
||||||
|
echo "Ignored Hosts: $IGNORE_HOSTS"
|
||||||
|
elif [ "$MODE" == "none" ]; then
|
||||||
|
# Disable proxy
|
||||||
|
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 0
|
||||||
|
echo "KDE: Proxy disabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply changes by restarting KDE's network settings
|
||||||
|
dbus-send --type=signal /KIO/Scheduler org.kde.KIO.Scheduler.reparseSlaveConfiguration string:""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect the current desktop environment
|
||||||
|
detect_desktop_environment() {
|
||||||
|
if [[ "$XDG_CURRENT_DESKTOP" == *"GNOME"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"GNOME"* ]]; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$XDG_CURRENT_DESKTOP" == *"XFCE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"XFCE"* ]]; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$XDG_CURRENT_DESKTOP" == *"X-Cinnamon"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"cinnamon"* ]]; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$XDG_CURRENT_DESKTOP" == *"UKUI"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"ukui"* ]]; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$XDG_CURRENT_DESKTOP" == *"DDE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"dde"* ]]; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$XDG_CURRENT_DESKTOP" == *"MATE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"mate"* ]]; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local KDE_ENVIRONMENTS=("KDE" "plasma")
|
||||||
|
for ENV in "${KDE_ENVIRONMENTS[@]}"; do
|
||||||
|
if [ "$XDG_CURRENT_DESKTOP" == "$ENV" ] || [ "$XDG_SESSION_DESKTOP" == "$ENV" ]; then
|
||||||
|
echo "kde"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Fallback to GNOME method if CLI utility is available. This solves the
|
||||||
|
# proxy configuration issues on minimal installation systems, like setups
|
||||||
|
# with only window managers, that borrow some parts from big DEs.
|
||||||
|
if command -v gsettings >/dev/null 2>&1; then
|
||||||
|
echo "gnome"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "unsupported"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
if [ "$#" -lt 1 ]; then
|
||||||
|
echo "Usage: $0 <mode> [proxy_ip proxy_port ignore_hosts]"
|
||||||
|
echo " mode: 'none' or 'manual'"
|
||||||
|
echo " If mode is 'manual', provide proxy IP, port, and ignore hosts."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the mode
|
||||||
|
MODE=$1
|
||||||
|
PROXY_IP=$2
|
||||||
|
PROXY_PORT=$3
|
||||||
|
IGNORE_HOSTS=$4
|
||||||
|
|
||||||
|
if ! [[ "$MODE" =~ ^(manual|none)$ ]]; then
|
||||||
|
echo "Invalid mode. Use 'none' or 'manual'." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect desktop environment
|
||||||
|
DE=$(detect_desktop_environment)
|
||||||
|
|
||||||
|
# Apply settings based on the desktop environment
|
||||||
|
if [ "$DE" == "gnome" ]; then
|
||||||
|
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
|
||||||
|
elif [ "$DE" == "kde" ]; then
|
||||||
|
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
|
||||||
|
set_kde_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
|
||||||
|
else
|
||||||
|
echo "Unsupported desktop environment: $DE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -23,3 +23,4 @@ tauri-plugin-opener = "2"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tauri-plugin-sql = { version = "2", features = ["sqlite"] }
|
tauri-plugin-sql = { version = "2", features = ["sqlite"] }
|
||||||
|
tauri-plugin-fs = "2"
|
||||||
|
|||||||
@@ -9,6 +9,23 @@
|
|||||||
"core:default",
|
"core:default",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
"sql:default",
|
"sql:default",
|
||||||
"sql:allow-execute"
|
"sql:allow-execute",
|
||||||
|
"fs:default",
|
||||||
|
{
|
||||||
|
"identifier": "fs:allow-write-text-file",
|
||||||
|
"allow": [
|
||||||
|
{
|
||||||
|
"path": "$APPCONFIG/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "fs:allow-remove",
|
||||||
|
"allow": [
|
||||||
|
{
|
||||||
|
"path": "$APPCONFIG/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
3
src-tauri/src/cores.rs
Normal file
3
src-tauri/src/cores.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
enum Core{
|
||||||
|
XRAY(String),
|
||||||
|
}
|
||||||
130
src-tauri/src/exe.rs
Normal file
130
src-tauri/src/exe.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::{Child, Command, Stdio};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
pub struct Exe {
|
||||||
|
path: PathBuf,
|
||||||
|
args: Vec<String>,
|
||||||
|
child: Arc<Mutex<Option<Child>>>,
|
||||||
|
pub pid: Arc<Mutex<Option<u32>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Exe {
|
||||||
|
pub fn new<P: Into<PathBuf>, S: Into<String>>(path: P, args: Vec<S>) -> Self {
|
||||||
|
Self {
|
||||||
|
path: path.into(),
|
||||||
|
args: args.into_iter().map(|s| s.into()).collect(),
|
||||||
|
child: Arc::new(Mutex::new(None)),
|
||||||
|
pid: Arc::new(Mutex::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start<F, E, X>(
|
||||||
|
&mut self,
|
||||||
|
mut on_stdout: F,
|
||||||
|
mut on_stderr: E,
|
||||||
|
mut on_exit: X,
|
||||||
|
) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
F: FnMut(String) + Send + 'static,
|
||||||
|
E: FnMut(String) + Send + 'static,
|
||||||
|
X: FnMut(i32) + Send + 'static,
|
||||||
|
{
|
||||||
|
let mut cmd = Command::new(&self.path);
|
||||||
|
cmd.args(&self.args)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped());
|
||||||
|
|
||||||
|
let mut child = cmd.spawn()?;
|
||||||
|
|
||||||
|
// ✅ 保存 PID
|
||||||
|
let pid = child.id();
|
||||||
|
{
|
||||||
|
*self.pid.lock().unwrap() = Some(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 异步读取 stdout
|
||||||
|
if let Some(out) = child.stdout.take() {
|
||||||
|
let mut reader = BufReader::new(out);
|
||||||
|
let mut line = String::new();
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(n) = reader.read_line(&mut line) {
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
on_stdout(line.clone());
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(err) = child.stderr.take() {
|
||||||
|
let mut reader = BufReader::new(err);
|
||||||
|
let mut line = String::new();
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(n) = reader.read_line(&mut line) {
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
on_stderr(line.clone());
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let child_arc = self.child.clone();
|
||||||
|
*child_arc.lock().unwrap() = Some(child);
|
||||||
|
|
||||||
|
let pid_clone = self.pid.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Some(mut c) = child_arc.lock().unwrap().take() {
|
||||||
|
match c.wait() {
|
||||||
|
Ok(status) => {
|
||||||
|
let code = status.code().unwrap_or(-1);
|
||||||
|
*pid_clone.lock().unwrap() = None;
|
||||||
|
on_exit(code);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error waiting for process: {e}");
|
||||||
|
*pid_clone.lock().unwrap() = None;
|
||||||
|
on_exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kill(&self) -> std::io::Result<()> {
|
||||||
|
let mut guard = self.child.lock().unwrap();
|
||||||
|
if let Some(child) = guard.as_mut() {
|
||||||
|
child.kill()?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"No running process",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_alive(&self) -> bool {
|
||||||
|
let mut guard = self.child.lock().unwrap();
|
||||||
|
if let Some(child) = guard.as_mut() {
|
||||||
|
match child.try_wait() {
|
||||||
|
Ok(Some(_)) => false, // 已退出
|
||||||
|
Ok(None) => true, // 仍在运行
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pid(&self) -> Option<u32> {
|
||||||
|
*self.pid.lock().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,10 @@
|
|||||||
use crate::group::add_group;
|
use crate::group::add_group;
|
||||||
use crate::spary::spary_switch;
|
use crate::spary::spary_switch;
|
||||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||||
|
mod exe;
|
||||||
mod group;
|
mod group;
|
||||||
mod spary;
|
mod spary;
|
||||||
|
mod cores;
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
@@ -21,8 +23,15 @@ pub fn run() {
|
|||||||
sql: "CREATE TABLE node(id INTEGER PRIMARY KEY AUTOINCREMENT, alias VARCHAR(60) NOT NULL,arguments JSON NOT NULL default '{}', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, group_id INTEGER NOT NULL)",
|
sql: "CREATE TABLE node(id INTEGER PRIMARY KEY AUTOINCREMENT, alias VARCHAR(60) NOT NULL,arguments JSON NOT NULL default '{}', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, group_id INTEGER NOT NULL)",
|
||||||
kind: MigrationKind::Up,
|
kind: MigrationKind::Up,
|
||||||
},
|
},
|
||||||
|
Migration{
|
||||||
|
version:3,
|
||||||
|
description:"add default group",
|
||||||
|
sql:"INSERT INTO `group`(name) VALUES('default')",
|
||||||
|
kind:MigrationKind::Up,
|
||||||
|
}
|
||||||
];
|
];
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_fs::init())
|
||||||
.plugin(
|
.plugin(
|
||||||
tauri_plugin_sql::Builder::default()
|
tauri_plugin_sql::Builder::default()
|
||||||
.add_migrations("sqlite:spary.db", migrations)
|
.add_migrations("sqlite:spary.db", migrations)
|
||||||
|
|||||||
49
src/App.vue
49
src/App.vue
@@ -4,48 +4,7 @@
|
|||||||
<v-main>
|
<v-main>
|
||||||
<v-card class="fill-height">
|
<v-card class="fill-height">
|
||||||
<v-layout class="fill-height">
|
<v-layout class="fill-height">
|
||||||
<v-navigation-drawer
|
<main-drawer/>
|
||||||
expand-on-hover
|
|
||||||
permanent
|
|
||||||
rail
|
|
||||||
v-model="drawer"
|
|
||||||
>
|
|
||||||
<v-list>
|
|
||||||
<v-list-item
|
|
||||||
prepend-avatar="/src/assets/logo.svg"
|
|
||||||
subtitle=""
|
|
||||||
:title="$t('app.title')"
|
|
||||||
></v-list-item>
|
|
||||||
</v-list>
|
|
||||||
|
|
||||||
<v-divider></v-divider>
|
|
||||||
|
|
||||||
<v-list density="compact" nav>
|
|
||||||
<v-list-item
|
|
||||||
prepend-icon="mdi-just-nothing"
|
|
||||||
title="🌊"
|
|
||||||
value="spary"
|
|
||||||
@click="router.push('/')"
|
|
||||||
></v-list-item>
|
|
||||||
<v-list-item
|
|
||||||
prepend-icon="mdi-airport"
|
|
||||||
:title="$t('app.nodes')"
|
|
||||||
value="nodes"
|
|
||||||
@click="router.push('/nodes')"
|
|
||||||
></v-list-item>
|
|
||||||
<v-list-item
|
|
||||||
prepend-icon="mdi-cog"
|
|
||||||
:title="$t('app.settings')"
|
|
||||||
value="settings"
|
|
||||||
@click="router.push('/settings')"
|
|
||||||
>
|
|
||||||
<template v-slot:append>
|
|
||||||
<language-switcher />
|
|
||||||
</template>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-navigation-drawer>
|
|
||||||
|
|
||||||
<v-main style="height: 100vh">
|
<v-main style="height: 100vh">
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</v-main>
|
</v-main>
|
||||||
@@ -56,13 +15,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import {useI18n} from 'vue-i18n'
|
import {useI18n} from 'vue-i18n'
|
||||||
import LanguageSwitcher from './components/LanguageSwitcher.vue'
|
|
||||||
import notificationProvider from './components/notify/notificationProvider.vue'
|
import notificationProvider from './components/notify/notificationProvider.vue'
|
||||||
|
import MainDrawer from "@/components/index/mainDrawer.vue";
|
||||||
|
|
||||||
const drawer = ref(true)
|
|
||||||
const router = useRouter()
|
|
||||||
useI18n()
|
useI18n()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -11,6 +11,7 @@ declare module 'vue' {
|
|||||||
AddGroup: typeof import('./components/nodeEdit/addGroup.vue')['default']
|
AddGroup: typeof import('./components/nodeEdit/addGroup.vue')['default']
|
||||||
AddNode: typeof import('./components/nodeEdit/addNode.vue')['default']
|
AddNode: typeof import('./components/nodeEdit/addNode.vue')['default']
|
||||||
LanguageSwitcher: typeof import('./components/LanguageSwitcher.vue')['default']
|
LanguageSwitcher: typeof import('./components/LanguageSwitcher.vue')['default']
|
||||||
|
MainDrawer: typeof import('./components/index/mainDrawer.vue')['default']
|
||||||
NodeList: typeof import('./components/nodeEdit/nodeList.vue')['default']
|
NodeList: typeof import('./components/nodeEdit/nodeList.vue')['default']
|
||||||
NodesFloatButton: typeof import('./components/nodeEdit/nodesFloatButton.vue')['default']
|
NodesFloatButton: typeof import('./components/nodeEdit/nodesFloatButton.vue')['default']
|
||||||
NotificationProvider: typeof import('./components/notify/notificationProvider.vue')['default']
|
NotificationProvider: typeof import('./components/notify/notificationProvider.vue')['default']
|
||||||
|
|||||||
60
src/components/index/mainDrawer.vue
Normal file
60
src/components/index/mainDrawer.vue
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {ref} from 'vue'
|
||||||
|
import {useRouter} from 'vue-router'
|
||||||
|
import LanguageSwitcher from '../LanguageSwitcher.vue'
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
|
const drawer = ref(true)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useI18n()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-navigation-drawer
|
||||||
|
expand-on-hover
|
||||||
|
permanent
|
||||||
|
rail
|
||||||
|
v-model="drawer"
|
||||||
|
>
|
||||||
|
<v-list>
|
||||||
|
<v-list-item
|
||||||
|
prepend-avatar="/src/assets/logo.svg"
|
||||||
|
subtitle=""
|
||||||
|
:title="$t('app.title')"
|
||||||
|
></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
|
||||||
|
<v-divider></v-divider>
|
||||||
|
|
||||||
|
<v-list density="compact" nav>
|
||||||
|
<v-list-item
|
||||||
|
prepend-icon="mdi-home"
|
||||||
|
:title="$t('app.mainPage')"
|
||||||
|
value="spary"
|
||||||
|
@click="router.push('/')"
|
||||||
|
></v-list-item>
|
||||||
|
<v-list-item
|
||||||
|
prepend-icon="mdi-airport"
|
||||||
|
:title="$t('app.nodes')"
|
||||||
|
value="nodes"
|
||||||
|
@click="router.push('/nodes')"
|
||||||
|
></v-list-item>
|
||||||
|
<v-list-item
|
||||||
|
prepend-icon="mdi-cog"
|
||||||
|
:title="$t('app.settings')"
|
||||||
|
value="settings"
|
||||||
|
@click="router.push('/settings')"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<language-switcher/>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-navigation-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -13,13 +13,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {invoke} from "@tauri-apps/api/core";
|
import {invoke} from "@tauri-apps/api/core";
|
||||||
|
import {genConfigFile, removeConfigFile} from "@/utils/bootArgs.ts";
|
||||||
|
|
||||||
const functionStatus = ref<String>("Off");
|
const functionStatus = ref<String>("Off");
|
||||||
|
|
||||||
function toggleFunctionStatus() {
|
function toggleFunctionStatus() {
|
||||||
functionStatus.value = functionStatus.value === "Off" ? "On" : "Off";
|
functionStatus.value = functionStatus.value === "Off" ? "On" : "Off";
|
||||||
spary_switch(functionStatus.value === "On")
|
spary_switch(functionStatus.value === "On")
|
||||||
|
|
||||||
|
if (functionStatus.value === "On") {
|
||||||
|
genConfigFile()
|
||||||
|
} else {
|
||||||
|
removeConfigFile()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function spary_switch(status: boolean) {
|
async function spary_switch(status: boolean) {
|
||||||
await invoke("spary_switch", {status})
|
await invoke("spary_switch", {status})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
|
"mainPage": "Home",
|
||||||
"title": "Spary",
|
"title": "Spary",
|
||||||
"nodes": "Nodes",
|
"nodes": "Nodes",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
|
"mainPage": "主页",
|
||||||
"title": "Spary",
|
"title": "Spary",
|
||||||
"nodes": "节点",
|
"nodes": "节点",
|
||||||
"settings": "设置",
|
"settings": "设置",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const messages = {
|
|||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
legacy: false, // Use composition API mode
|
legacy: false, // Use composition API mode
|
||||||
locale: 'en', // Default locale
|
locale: 'en', // Default locale
|
||||||
fallbackLocale: 'en', // Fallback locale
|
fallbackLocale: 'zh', // Fallback locale
|
||||||
messages
|
messages
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
321
src/utils/bootArgs.ts
Normal file
321
src/utils/bootArgs.ts
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
import {writeTextFile, BaseDirectory, remove} from '@tauri-apps/plugin-fs';
|
||||||
|
export async function removeConfigFile(){
|
||||||
|
try {
|
||||||
|
await remove('config.json', { baseDir: BaseDirectory.AppConfig });
|
||||||
|
} catch (error) {
|
||||||
|
// ignore
|
||||||
|
console.warn(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function genConfigFile() {
|
||||||
|
await removeConfigFile()
|
||||||
|
const configContent = `
|
||||||
|
{
|
||||||
|
"log": {
|
||||||
|
"loglevel": "warning"
|
||||||
|
},
|
||||||
|
"dns": {
|
||||||
|
"hosts": {
|
||||||
|
"dns.google": [
|
||||||
|
"8.8.8.8",
|
||||||
|
"8.8.4.4",
|
||||||
|
"2001:4860:4860::8888",
|
||||||
|
"2001:4860:4860::8844"
|
||||||
|
],
|
||||||
|
"dns.alidns.com": [
|
||||||
|
"223.5.5.5",
|
||||||
|
"223.6.6.6",
|
||||||
|
"2400:3200::1",
|
||||||
|
"2400:3200:baba::1"
|
||||||
|
],
|
||||||
|
"one.one.one.one": [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001"
|
||||||
|
],
|
||||||
|
"1dot1dot1dot1.cloudflare-dns.com": [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001"
|
||||||
|
],
|
||||||
|
"cloudflare-dns.com": [
|
||||||
|
"104.16.249.249",
|
||||||
|
"104.16.248.249",
|
||||||
|
"2606:4700::6810:f8f9",
|
||||||
|
"2606:4700::6810:f9f9"
|
||||||
|
],
|
||||||
|
"dns.cloudflare.com": [
|
||||||
|
"104.16.132.229",
|
||||||
|
"104.16.133.229",
|
||||||
|
"2606:4700::6810:84e5",
|
||||||
|
"2606:4700::6810:85e5"
|
||||||
|
],
|
||||||
|
"dot.pub": [
|
||||||
|
"1.12.12.12",
|
||||||
|
"120.53.53.53"
|
||||||
|
],
|
||||||
|
"doh.pub": [
|
||||||
|
"1.12.12.12",
|
||||||
|
"120.53.53.53"
|
||||||
|
],
|
||||||
|
"dns.quad9.net": [
|
||||||
|
"9.9.9.9",
|
||||||
|
"149.112.112.112",
|
||||||
|
"2620:fe::fe",
|
||||||
|
"2620:fe::9"
|
||||||
|
],
|
||||||
|
"dns.yandex.net": [
|
||||||
|
"77.88.8.8",
|
||||||
|
"77.88.8.1",
|
||||||
|
"2a02:6b8::feed:0ff",
|
||||||
|
"2a02:6b8:0:1::feed:0ff"
|
||||||
|
],
|
||||||
|
"dns.sb": [
|
||||||
|
"185.222.222.222",
|
||||||
|
"2a09::"
|
||||||
|
],
|
||||||
|
"dns.umbrella.com": [
|
||||||
|
"208.67.220.220",
|
||||||
|
"208.67.222.222",
|
||||||
|
"2620:119:35::35",
|
||||||
|
"2620:119:53::53"
|
||||||
|
],
|
||||||
|
"dns.sse.cisco.com": [
|
||||||
|
"208.67.220.220",
|
||||||
|
"208.67.222.222",
|
||||||
|
"2620:119:35::35",
|
||||||
|
"2620:119:53::53"
|
||||||
|
],
|
||||||
|
"engage.cloudflareclient.com": [
|
||||||
|
"162.159.192.1",
|
||||||
|
"2606:4700:d0::a29f:c001"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"address": "https://cloudflare-dns.com/dns-query",
|
||||||
|
"domains": [
|
||||||
|
"domain:googleapis.cn",
|
||||||
|
"domain:gstatic.com"
|
||||||
|
],
|
||||||
|
"skipFallback": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "https://dns.alidns.com/dns-query",
|
||||||
|
"domains": [
|
||||||
|
"domain:alidns.com",
|
||||||
|
"domain:doh.pub",
|
||||||
|
"domain:dot.pub",
|
||||||
|
"domain:360.cn",
|
||||||
|
"domain:onedns.net",
|
||||||
|
"pioneer.kakakuai.top"
|
||||||
|
],
|
||||||
|
"skipFallback": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "https://dns.alidns.com/dns-query",
|
||||||
|
"domains": [
|
||||||
|
"geosite:private",
|
||||||
|
"geosite:cn"
|
||||||
|
],
|
||||||
|
"skipFallback": true
|
||||||
|
},
|
||||||
|
"https://cloudflare-dns.com/dns-query"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"tag": "socks",
|
||||||
|
"port": 10808,
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"protocol": "mixed",
|
||||||
|
"sniffing": {
|
||||||
|
"enabled": true,
|
||||||
|
"destOverride": [
|
||||||
|
"http",
|
||||||
|
"tls"
|
||||||
|
],
|
||||||
|
"routeOnly": false
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"auth": "noauth",
|
||||||
|
"udp": true,
|
||||||
|
"allowTransparent": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "api",
|
||||||
|
"port": 10812,
|
||||||
|
"listen": "127.0.0.1",
|
||||||
|
"protocol": "dokodemo-door",
|
||||||
|
"settings": {
|
||||||
|
"address": "127.0.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"tag": "proxy",
|
||||||
|
"protocol": "trojan",
|
||||||
|
"settings": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"address": "pioneer.kakakuai.top",
|
||||||
|
"method": "",
|
||||||
|
"ota": false,
|
||||||
|
"password": "0FUz1P7obM",
|
||||||
|
"port": 443,
|
||||||
|
"level": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "ws",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"allowInsecure": false
|
||||||
|
},
|
||||||
|
"wsSettings": {
|
||||||
|
"path": "/dsf",
|
||||||
|
"headers": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mux": {
|
||||||
|
"enabled": false,
|
||||||
|
"concurrency": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "direct",
|
||||||
|
"protocol": "freedom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "block",
|
||||||
|
"protocol": "blackhole"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routing": {
|
||||||
|
"domainStrategy": "AsIs",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"inboundTag": [
|
||||||
|
"api"
|
||||||
|
],
|
||||||
|
"outboundTag": "api"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "proxy",
|
||||||
|
"domain": [
|
||||||
|
"domain:googleapis.cn",
|
||||||
|
"domain:gstatic.com"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"port": "443",
|
||||||
|
"network": "udp",
|
||||||
|
"outboundTag": "block"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "direct",
|
||||||
|
"ip": [
|
||||||
|
"geoip:private"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "direct",
|
||||||
|
"domain": [
|
||||||
|
"geosite:private"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "direct",
|
||||||
|
"ip": [
|
||||||
|
"223.5.5.5",
|
||||||
|
"223.6.6.6",
|
||||||
|
"2400:3200::1",
|
||||||
|
"2400:3200:baba::1",
|
||||||
|
"119.29.29.29",
|
||||||
|
"1.12.12.12",
|
||||||
|
"120.53.53.53",
|
||||||
|
"2402:4e00::",
|
||||||
|
"2402:4e00:1::",
|
||||||
|
"180.76.76.76",
|
||||||
|
"2400:da00::6666",
|
||||||
|
"114.114.114.114",
|
||||||
|
"114.114.115.115",
|
||||||
|
"114.114.114.119",
|
||||||
|
"114.114.115.119",
|
||||||
|
"114.114.114.110",
|
||||||
|
"114.114.115.110",
|
||||||
|
"180.184.1.1",
|
||||||
|
"180.184.2.2",
|
||||||
|
"101.226.4.6",
|
||||||
|
"218.30.118.6",
|
||||||
|
"123.125.81.6",
|
||||||
|
"140.207.198.6",
|
||||||
|
"1.2.4.8",
|
||||||
|
"210.2.4.8",
|
||||||
|
"52.80.66.66",
|
||||||
|
"117.50.22.22",
|
||||||
|
"2400:7fc0:849e:200::4",
|
||||||
|
"2404:c2c0:85d8:901::4",
|
||||||
|
"117.50.10.10",
|
||||||
|
"52.80.52.52",
|
||||||
|
"2400:7fc0:849e:200::8",
|
||||||
|
"2404:c2c0:85d8:901::8",
|
||||||
|
"117.50.60.30",
|
||||||
|
"52.80.60.30"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "direct",
|
||||||
|
"domain": [
|
||||||
|
"domain:alidns.com",
|
||||||
|
"domain:doh.pub",
|
||||||
|
"domain:dot.pub",
|
||||||
|
"domain:360.cn",
|
||||||
|
"domain:onedns.net"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "direct",
|
||||||
|
"ip": [
|
||||||
|
"geoip:cn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"outboundTag": "direct",
|
||||||
|
"domain": [
|
||||||
|
"geosite:cn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"tag": "api"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"system": {
|
||||||
|
"statsOutboundUplink": true,
|
||||||
|
"statsOutboundDownlink": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stats": {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const finalContent = JSON.stringify(JSON.parse(configContent), null, 4);
|
||||||
|
await writeTextFile('config.json', finalContent, {
|
||||||
|
baseDir: BaseDirectory.AppConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -683,6 +683,13 @@
|
|||||||
"@tauri-apps/cli-win32-ia32-msvc" "2.8.4"
|
"@tauri-apps/cli-win32-ia32-msvc" "2.8.4"
|
||||||
"@tauri-apps/cli-win32-x64-msvc" "2.8.4"
|
"@tauri-apps/cli-win32-x64-msvc" "2.8.4"
|
||||||
|
|
||||||
|
"@tauri-apps/plugin-fs@~2":
|
||||||
|
version "2.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-fs/-/plugin-fs-2.4.2.tgz#cce4ea16dc3614f7a476988457ec4cf0024d81f6"
|
||||||
|
integrity sha512-YGhmYuTgXGsi6AjoV+5mh2NvicgWBfVJHHheuck6oHD+HC9bVWPaHvCP0/Aw4pHDejwrvT8hE3+zZAaWf+hrig==
|
||||||
|
dependencies:
|
||||||
|
"@tauri-apps/api" "^2.8.0"
|
||||||
|
|
||||||
"@tauri-apps/plugin-opener@^2":
|
"@tauri-apps/plugin-opener@^2":
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz"
|
resolved "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user