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/
|
||||
*.lcov
|
||||
.junit/
|
||||
/cores/
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"@fontsource/roboto": "5.2.7",
|
||||
"@mdi/font": "7.4.47",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-fs": "~2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-sql": "~2",
|
||||
"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_json = "1"
|
||||
tauri-plugin-sql = { version = "2", features = ["sqlite"] }
|
||||
tauri-plugin-fs = "2"
|
||||
|
||||
@@ -9,6 +9,23 @@
|
||||
"core:default",
|
||||
"opener: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::spary::spary_switch;
|
||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||
mod exe;
|
||||
mod group;
|
||||
mod spary;
|
||||
mod cores;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
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)",
|
||||
kind: MigrationKind::Up,
|
||||
},
|
||||
Migration{
|
||||
version:3,
|
||||
description:"add default group",
|
||||
sql:"INSERT INTO `group`(name) VALUES('default')",
|
||||
kind:MigrationKind::Up,
|
||||
}
|
||||
];
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(
|
||||
tauri_plugin_sql::Builder::default()
|
||||
.add_migrations("sqlite:spary.db", migrations)
|
||||
|
||||
53
src/App.vue
53
src/App.vue
@@ -1,51 +1,10 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<notification-provider />
|
||||
<notification-provider/>
|
||||
<v-main>
|
||||
<v-card class="fill-height">
|
||||
<v-layout class="fill-height">
|
||||
<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-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>
|
||||
|
||||
<main-drawer/>
|
||||
<v-main style="height: 100vh">
|
||||
<router-view/>
|
||||
</v-main>
|
||||
@@ -56,13 +15,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import LanguageSwitcher from './components/LanguageSwitcher.vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import notificationProvider from './components/notify/notificationProvider.vue'
|
||||
import MainDrawer from "@/components/index/mainDrawer.vue";
|
||||
|
||||
const drawer = ref(true)
|
||||
const router = useRouter()
|
||||
useI18n()
|
||||
</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']
|
||||
AddNode: typeof import('./components/nodeEdit/addNode.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']
|
||||
NodesFloatButton: typeof import('./components/nodeEdit/nodesFloatButton.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>
|
||||
@@ -2,9 +2,9 @@
|
||||
<v-container class="fill-height d-flex align-center justify-center">
|
||||
<div>
|
||||
<v-switch
|
||||
:label="functionStatus === 'On' ? $t('spary.functionStatus.on') : $t('spary.functionStatus.off')"
|
||||
:model-value="functionStatus === 'On'"
|
||||
@update:model-value="toggleFunctionStatus"
|
||||
:label="functionStatus === 'On' ? $t('spary.functionStatus.on') : $t('spary.functionStatus.off')"
|
||||
:model-value="functionStatus === 'On'"
|
||||
@update:model-value="toggleFunctionStatus"
|
||||
></v-switch>
|
||||
</div>
|
||||
</v-container>
|
||||
@@ -13,14 +13,22 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {genConfigFile, removeConfigFile} from "@/utils/bootArgs.ts";
|
||||
|
||||
const functionStatus = ref<String>("Off");
|
||||
|
||||
function toggleFunctionStatus() {
|
||||
functionStatus.value = functionStatus.value === "Off" ? "On" : "Off";
|
||||
spary_switch(functionStatus.value === "On")
|
||||
|
||||
if (functionStatus.value === "On") {
|
||||
genConfigFile()
|
||||
} else {
|
||||
removeConfigFile()
|
||||
}
|
||||
}
|
||||
async function spary_switch(status:boolean){
|
||||
await invoke("spary_switch",{status})
|
||||
|
||||
async function spary_switch(status: boolean) {
|
||||
await invoke("spary_switch", {status})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"app": {
|
||||
"mainPage": "Home",
|
||||
"title": "Spary",
|
||||
"nodes": "Nodes",
|
||||
"settings": "Settings",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"app": {
|
||||
"mainPage": "主页",
|
||||
"title": "Spary",
|
||||
"nodes": "节点",
|
||||
"settings": "设置",
|
||||
|
||||
@@ -10,7 +10,7 @@ const messages = {
|
||||
const i18n = createI18n({
|
||||
legacy: false, // Use composition API mode
|
||||
locale: 'en', // Default locale
|
||||
fallbackLocale: 'en', // Fallback locale
|
||||
fallbackLocale: 'zh', // Fallback locale
|
||||
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-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":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz"
|
||||
|
||||
Reference in New Issue
Block a user