base vpn android plugin
This commit is contained in:
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.cargo.features": ["mobile", "desktop"],
|
||||||
|
"rust-analyzer.cargo.allTargets": true,
|
||||||
|
"rust-analyzer.check.allTargets": true,
|
||||||
|
"rust-analyzer.check.targets": [
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"aarch64-linux-android",
|
||||||
|
"x86_64-linux-android"
|
||||||
|
],
|
||||||
|
"rust-analyzer.procMacro.enable": true
|
||||||
|
}
|
||||||
@@ -12,9 +12,14 @@ tauri-build = { version = "2.0.0", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jni = "0.22.3"
|
jni = "0.22.3"
|
||||||
tauri = { version = "2.0.0", features = [] }
|
tauri = { version = "2.10.3", features = [] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
ndk-context = "0.1"
|
ndk-context = "0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
android_logger = "0.15.1"
|
android_logger = "0.15.1"
|
||||||
tauri-plugin-vpn = { path = "./src/plugins/vpn-plugin" }
|
tauri-plugin-vpn = { path = "./src/plugins/vpn-plugin" }
|
||||||
|
|
||||||
|
|
||||||
|
[features]
|
||||||
|
mobile = []
|
||||||
|
desktop = []
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
"windows": ["main"],
|
"windows": ["main"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
{
|
"vpn:allow-start-tunnel",
|
||||||
"identifier": "core:app:default",
|
"vpn:allow-stop-tunnel"
|
||||||
"allow": ["start_vpn_android"]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ pub fn run() {
|
|||||||
.with_tag("NetrunnerRust"),
|
.with_tag("NetrunnerRust"),
|
||||||
);
|
);
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_vpn::init())
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,3 +20,7 @@ dist
|
|||||||
/android/.tauri
|
/android/.tauri
|
||||||
/android/.gradle
|
/android/.gradle
|
||||||
/android/build
|
/android/build
|
||||||
|
*jniLibs
|
||||||
|
*uniffi
|
||||||
|
*rollup.cache
|
||||||
|
/android/gradle
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ thiserror = "2"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-plugin = { version = "2.5.4", features = ["build"] }
|
tauri-plugin = { version = "2.5.4", features = ["build"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
mobile = []
|
||||||
|
desktop = []
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
|
id("kotlin-parcelize")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -10,6 +11,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
@@ -18,9 +20,19 @@ android {
|
|||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "17"
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
getByName("main") {
|
||||||
|
// Добавляем папку с исходным кодом (где лежат твои uniffi .kt файлы)
|
||||||
|
java.srcDirs("src/main/java")
|
||||||
|
// Оставляем jniLibs для .so файлов
|
||||||
|
jniLibs.srcDirs("src/main/jniLibs")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation("net.java.dev.jna:jna:5.13.0@aar")
|
||||||
implementation("androidx.core:core-ktx:1.9.0")
|
implementation("androidx.core:core-ktx:1.9.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.0")
|
implementation("androidx.appcompat:appcompat:1.6.0")
|
||||||
implementation(project(":tauri-android"))
|
implementation(project(":tauri-android"))
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="app.netrunner.vpn">
|
package="app.netrunner.vpn">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.BIND_VPN_SERVICE" />
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
<service
|
<service
|
||||||
android:name=".ExampleVpnService"
|
android:name="app.netrunner.vpn.VpnPlugin"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package app.netrunner.vpn;
|
|
||||||
import android.net.VpnService;
|
|
||||||
|
|
||||||
class VpnEngine : VpnService() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package app.netrunner.vpn
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import app.tauri.plugin.Plugin
|
||||||
|
import app.tauri.plugin.Invoke
|
||||||
|
import app.tauri.annotation.InvokeArg
|
||||||
|
import app.tauri.annotation.Command
|
||||||
|
import app.tauri.annotation.TauriPlugin
|
||||||
|
import app.tauri.annotation.Permission
|
||||||
|
import android.Manifest
|
||||||
|
import app.netrunner.vpn.VpnController
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.content.Context
|
||||||
|
import uniffi.netrunner_client.SessionManager
|
||||||
|
import uniffi.netrunner_client.Session
|
||||||
|
import uniffi.netrunner_client.NoHandle
|
||||||
|
import android.net.VpnService as AndroidVpnService
|
||||||
|
import app.netrunner.vpn.TunInterface
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class TunnelConfig(
|
||||||
|
val address: String,
|
||||||
|
val prefix: Int,
|
||||||
|
val dns: String,
|
||||||
|
val mtu: Int
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
|
|
||||||
|
class TunInterface : Closeable {
|
||||||
|
|
||||||
|
private var vpnInterface: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
|
fun establish(builder: AndroidVpnService.Builder, config: TunnelConfig): Int {
|
||||||
|
// Принудительно закрываем текущий, если вдруг вызываем повторно
|
||||||
|
close()
|
||||||
|
|
||||||
|
val descriptor = builder
|
||||||
|
.addAddress(config.address, config.prefix)
|
||||||
|
.addRoute("0.0.0.0", 0)
|
||||||
|
.setMtu(config.mtu)
|
||||||
|
.establish()
|
||||||
|
|
||||||
|
vpnInterface = descriptor
|
||||||
|
return descriptor?.fd ?: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
try {
|
||||||
|
vpnInterface?.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
vpnInterface = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VpnController(private val service: VpnService) {
|
||||||
|
init {
|
||||||
|
System.loadLibrary("netrunner_client")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var activeSession: Session? = null
|
||||||
|
private val tunInterface = TunInterface()
|
||||||
|
|
||||||
|
fun startVpn(config: TunnelConfig, ip: String, port: Int) {
|
||||||
|
val builder = service.Builder()
|
||||||
|
|
||||||
|
val fd = tunInterface.establish(builder, config)
|
||||||
|
|
||||||
|
if (fd != -1) {
|
||||||
|
val manager = SessionManager(NoHandle)
|
||||||
|
android.util.Log.d("VPN_DEBUG", "Starting session with FD: $fd")
|
||||||
|
activeSession = manager.start("$ip:$port", fd)
|
||||||
|
} else {
|
||||||
|
android.util.Log.d("VPN_DEBUG", "Starting session with FD: $fd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopVpn() {
|
||||||
|
activeSession?.stop()
|
||||||
|
activeSession = null
|
||||||
|
tunInterface.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VpnService : AndroidVpnService() {
|
||||||
|
private var controller: VpnController? = null
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
android.util.Log.d("VPN_DEBUG", "VpnService: onCreate() executed")
|
||||||
|
try {
|
||||||
|
controller = VpnController(this)
|
||||||
|
android.util.Log.d("VPN_DEBUG", "VpnService: VpnController initialized successfully")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.e("VPN_DEBUG", "VpnService: CRITICAL - VpnController init failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
val action = intent?.action
|
||||||
|
android.util.Log.d("VPN_DEBUG", "VpnService: onStartCommand() action='$action'")
|
||||||
|
|
||||||
|
when (action) {
|
||||||
|
"START_VPN" -> {
|
||||||
|
val ip = intent.getStringExtra("ip")
|
||||||
|
val port = intent.getIntExtra("port", -1)
|
||||||
|
android.util.Log.d("VPN_DEBUG", "VpnService: Extracting extras -> IP: '$ip', Port: $port")
|
||||||
|
|
||||||
|
val config = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
intent.getParcelableExtra("config", TunnelConfig::class.java)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
intent.getParcelableExtra("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config == null) {
|
||||||
|
android.util.Log.e("VPN_DEBUG", "VpnService: ERROR - TunnelConfig is NULL! Check if parcelable passed correctly.")
|
||||||
|
} else {
|
||||||
|
android.util.Log.d("VPN_DEBUG", "VpnService: Config received: $config. Calling controller.startVpn()...")
|
||||||
|
controller?.startVpn(config, ip ?: "", port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"STOP_VPN" -> {
|
||||||
|
android.util.Log.d("VPN_DEBUG", "VpnService: Stopping VPN...")
|
||||||
|
controller?.stopVpn()
|
||||||
|
stopSelf()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
android.util.Log.w("VPN_DEBUG", "VpnService: Unknown action received: '$action'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
controller?.stopVpn()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@InvokeArg
|
||||||
|
class VpnArgs {
|
||||||
|
var config: TunnelConfig? = null
|
||||||
|
var ip: String = ""
|
||||||
|
var port: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@TauriPlugin
|
||||||
|
class VpnPlugin(private val activity: Activity) : Plugin(activity) {
|
||||||
|
|
||||||
|
@Command
|
||||||
|
fun startVpn(invoke: Invoke) {
|
||||||
|
android.util.Log.d("VPN_DEBUG", "Plugin: startVpn invoked")
|
||||||
|
val args = invoke.parseArgs(VpnArgs::class.java)
|
||||||
|
|
||||||
|
|
||||||
|
val intent = Intent(activity, VpnService::class.java).apply {
|
||||||
|
action = "START_VPN"
|
||||||
|
putExtra("ip", args.ip)
|
||||||
|
putExtra("port", args.port)
|
||||||
|
putExtra("config", args.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
android.util.Log.d("VPN_DEBUG", "Plugin: Starting VpnService...")
|
||||||
|
val result = activity.startService(intent)
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
android.util.Log.e("VPN_DEBUG", "Plugin: startService returned null (Service not found?)")
|
||||||
|
} else {
|
||||||
|
android.util.Log.d("VPN_DEBUG", "Plugin: startService triggered successfully")
|
||||||
|
}
|
||||||
|
invoke.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command
|
||||||
|
fun stopVpn(invoke: Invoke) {
|
||||||
|
val intent = Intent(activity, VpnService::class.java).apply {
|
||||||
|
action = "STOP_VPN"
|
||||||
|
}
|
||||||
|
activity.startService(intent)
|
||||||
|
invoke.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
const COMMANDS: &[&str] = &["ping"];
|
// src-tauri/src/plugins/vpn-plugin/build.rs
|
||||||
|
|
||||||
|
const COMMANDS: &[&str] = &["start_tunnel", "stop_tunnel"];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri_plugin::Builder::new(COMMANDS)
|
tauri_plugin::Builder::new(COMMANDS)
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
export async function ping(value: string): Promise<string | null> {
|
export interface TunnelConfig {
|
||||||
return await invoke<{value?: string}>('plugin:vpn|ping', {
|
address: string;
|
||||||
payload: {
|
prefix: number;
|
||||||
value,
|
dns: string;
|
||||||
},
|
mtu: number;
|
||||||
}).then((r) => (r.value ? r.value : null));
|
}
|
||||||
|
|
||||||
|
export async function startVpn(config: TunnelConfig, ip: string, port: number) {
|
||||||
|
return await invoke("plugin:vpn|start_vpn", {
|
||||||
|
config,
|
||||||
|
ip,
|
||||||
|
port,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stopVpn() {
|
||||||
|
return await invoke("plugin:vpn|stop_vpn");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# Automatically generated - DO NOT EDIT!
|
|
||||||
|
|
||||||
"$schema" = "../../schemas/schema.json"
|
|
||||||
|
|
||||||
[[permission]]
|
|
||||||
identifier = "allow-ping"
|
|
||||||
description = "Enables the ping command without any pre-configured scope."
|
|
||||||
commands.allow = ["ping"]
|
|
||||||
|
|
||||||
[[permission]]
|
|
||||||
identifier = "deny-ping"
|
|
||||||
description = "Denies the ping command without any pre-configured scope."
|
|
||||||
commands.deny = ["ping"]
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-start-tunnel"
|
||||||
|
description = "Enables the start_tunnel command without any pre-configured scope."
|
||||||
|
commands.allow = ["start_tunnel"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-start-tunnel"
|
||||||
|
description = "Denies the start_tunnel command without any pre-configured scope."
|
||||||
|
commands.deny = ["start_tunnel"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-stop-tunnel"
|
||||||
|
description = "Enables the stop_tunnel command without any pre-configured scope."
|
||||||
|
commands.allow = ["stop_tunnel"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-stop-tunnel"
|
||||||
|
description = "Denies the stop_tunnel command without any pre-configured scope."
|
||||||
|
commands.deny = ["stop_tunnel"]
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
`vpn:allow-ping`
|
`vpn:allow-start-tunnel`
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Enables the ping command without any pre-configured scope.
|
Enables the start_tunnel command without any pre-configured scope.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -23,12 +23,12 @@ Enables the ping command without any pre-configured scope.
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
`vpn:deny-ping`
|
`vpn:deny-start-tunnel`
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Denies the ping command without any pre-configured scope.
|
Denies the start_tunnel command without any pre-configured scope.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -36,12 +36,38 @@ Denies the ping command without any pre-configured scope.
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
`vpn:vpn-control`
|
`vpn:allow-stop-tunnel`
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Allows access to VPN Service
|
Enables the stop_tunnel command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`vpn:deny-stop-tunnel`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Denies the stop_tunnel command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`vpn:allow-vpn-control`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Allows Vpn Control
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[[permission]]
|
[[permission]]
|
||||||
identifier = "vpn-control"
|
identifier = "allow-vpn-control"
|
||||||
description = "Allows access to VPN Service"
|
description = "Allows Vpn Control"
|
||||||
name = "allow-vpn-control"
|
|
||||||
commands = { allow = ["ping", "prepare_vpn", "start_vpn", "stop_vpn"] }
|
[permission.commands]
|
||||||
|
allow = ["start_vpn", "stop_vpn"]
|
||||||
@@ -295,22 +295,34 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"description": "Enables the ping command without any pre-configured scope.",
|
"description": "Enables the start_tunnel command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "allow-ping",
|
"const": "allow-start-tunnel",
|
||||||
"markdownDescription": "Enables the ping command without any pre-configured scope."
|
"markdownDescription": "Enables the start_tunnel command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the ping command without any pre-configured scope.",
|
"description": "Denies the start_tunnel command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "deny-ping",
|
"const": "deny-start-tunnel",
|
||||||
"markdownDescription": "Denies the ping command without any pre-configured scope."
|
"markdownDescription": "Denies the start_tunnel command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Allows access to VPN Service",
|
"description": "Enables the stop_tunnel command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "vpn-control",
|
"const": "allow-stop-tunnel",
|
||||||
"markdownDescription": "Allows access to VPN Service"
|
"markdownDescription": "Enables the stop_tunnel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the stop_tunnel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "deny-stop-tunnel",
|
||||||
|
"markdownDescription": "Denies the stop_tunnel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Allows Vpn Control",
|
||||||
|
"type": "string",
|
||||||
|
"const": "allow-vpn-control",
|
||||||
|
"markdownDescription": "Allows Vpn Control"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+402
@@ -0,0 +1,402 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
'@tauri-apps/api':
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.10.1
|
||||||
|
devDependencies:
|
||||||
|
'@rollup/plugin-typescript':
|
||||||
|
specifier: ^12.0.0
|
||||||
|
version: 12.3.0(rollup@4.59.0)(tslib@2.8.1)(typescript@5.9.3)
|
||||||
|
rollup:
|
||||||
|
specifier: ^4.9.6
|
||||||
|
version: 4.59.0
|
||||||
|
tslib:
|
||||||
|
specifier: ^2.6.2
|
||||||
|
version: 2.8.1
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.3.3
|
||||||
|
version: 5.9.3
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@rollup/plugin-typescript@12.3.0':
|
||||||
|
resolution: {integrity: sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
rollup: ^2.14.0||^3.0.0||^4.0.0
|
||||||
|
tslib: '*'
|
||||||
|
typescript: '>=3.7.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
rollup:
|
||||||
|
optional: true
|
||||||
|
tslib:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/pluginutils@5.3.0':
|
||||||
|
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
rollup:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||||
|
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-arm64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-x64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-arm64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-x64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
|
||||||
|
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
||||||
|
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
||||||
|
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
||||||
|
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
||||||
|
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
||||||
|
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-musl@4.59.0':
|
||||||
|
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-openbsd-x64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-openharmony-arm64@4.59.0':
|
||||||
|
resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-arm64-msvc@4.59.0':
|
||||||
|
resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-ia32-msvc@4.59.0':
|
||||||
|
resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-gnu@4.59.0':
|
||||||
|
resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-msvc@4.59.0':
|
||||||
|
resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@tauri-apps/api@2.10.1':
|
||||||
|
resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==}
|
||||||
|
|
||||||
|
'@types/estree@1.0.8':
|
||||||
|
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||||
|
|
||||||
|
estree-walker@2.0.2:
|
||||||
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
function-bind@1.1.2:
|
||||||
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
hasown@2.0.2:
|
||||||
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
is-core-module@2.16.1:
|
||||||
|
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
path-parse@1.0.7:
|
||||||
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
|
||||||
|
picomatch@4.0.3:
|
||||||
|
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
resolve@1.22.11:
|
||||||
|
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
rollup@4.59.0:
|
||||||
|
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
|
||||||
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
supports-preserve-symlinks-flag@1.0.0:
|
||||||
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
tslib@2.8.1:
|
||||||
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
typescript@5.9.3:
|
||||||
|
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@rollup/plugin-typescript@12.3.0(rollup@4.59.0)(tslib@2.8.1)(typescript@5.9.3)':
|
||||||
|
dependencies:
|
||||||
|
'@rollup/pluginutils': 5.3.0(rollup@4.59.0)
|
||||||
|
resolve: 1.22.11
|
||||||
|
typescript: 5.9.3
|
||||||
|
optionalDependencies:
|
||||||
|
rollup: 4.59.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
'@rollup/pluginutils@5.3.0(rollup@4.59.0)':
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
picomatch: 4.0.3
|
||||||
|
optionalDependencies:
|
||||||
|
rollup: 4.59.0
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-arm64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-x64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-arm64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-x64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-musl@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-openbsd-x64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-openharmony-arm64@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-arm64-msvc@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-ia32-msvc@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-gnu@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-msvc@4.59.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@tauri-apps/api@2.10.1': {}
|
||||||
|
|
||||||
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
|
estree-walker@2.0.2: {}
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
|
hasown@2.0.2:
|
||||||
|
dependencies:
|
||||||
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
is-core-module@2.16.1:
|
||||||
|
dependencies:
|
||||||
|
hasown: 2.0.2
|
||||||
|
|
||||||
|
path-parse@1.0.7: {}
|
||||||
|
|
||||||
|
picomatch@4.0.3: {}
|
||||||
|
|
||||||
|
resolve@1.22.11:
|
||||||
|
dependencies:
|
||||||
|
is-core-module: 2.16.1
|
||||||
|
path-parse: 1.0.7
|
||||||
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
|
rollup@4.59.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
optionalDependencies:
|
||||||
|
'@rollup/rollup-android-arm-eabi': 4.59.0
|
||||||
|
'@rollup/rollup-android-arm64': 4.59.0
|
||||||
|
'@rollup/rollup-darwin-arm64': 4.59.0
|
||||||
|
'@rollup/rollup-darwin-x64': 4.59.0
|
||||||
|
'@rollup/rollup-freebsd-arm64': 4.59.0
|
||||||
|
'@rollup/rollup-freebsd-x64': 4.59.0
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf': 4.59.0
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf': 4.59.0
|
||||||
|
'@rollup/rollup-linux-arm64-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-linux-arm64-musl': 4.59.0
|
||||||
|
'@rollup/rollup-linux-loong64-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-linux-loong64-musl': 4.59.0
|
||||||
|
'@rollup/rollup-linux-ppc64-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-linux-ppc64-musl': 4.59.0
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-linux-riscv64-musl': 4.59.0
|
||||||
|
'@rollup/rollup-linux-s390x-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-linux-x64-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-linux-x64-musl': 4.59.0
|
||||||
|
'@rollup/rollup-openbsd-x64': 4.59.0
|
||||||
|
'@rollup/rollup-openharmony-arm64': 4.59.0
|
||||||
|
'@rollup/rollup-win32-arm64-msvc': 4.59.0
|
||||||
|
'@rollup/rollup-win32-ia32-msvc': 4.59.0
|
||||||
|
'@rollup/rollup-win32-x64-gnu': 4.59.0
|
||||||
|
'@rollup/rollup-win32-x64-msvc': 4.59.0
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
supports-preserve-symlinks-flag@1.0.0: {}
|
||||||
|
|
||||||
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
typescript@5.9.3: {}
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
use tauri::{AppHandle, command, Runtime};
|
use crate::{models::*, Vpn, VpnInterface};
|
||||||
|
use tauri::{command, AppHandle, Manager, Runtime};
|
||||||
use crate::models::*;
|
|
||||||
use crate::Result;
|
|
||||||
use crate::VpnExt;
|
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub(crate) async fn ping<R: Runtime>(
|
pub(crate) fn start_vpn<R: Runtime>(
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
payload: PingRequest,
|
config: TunnelConfig,
|
||||||
) -> Result<PingResponse> {
|
ip: String,
|
||||||
app.vpn().ping(payload)
|
port: i32,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
// Получаем состояние Vpn<R> и вызываем метод интерфейса
|
||||||
|
eprintln!("--- [DEBUG] START_VPN COMMAND CALLED! ---");
|
||||||
|
app.state::<Vpn<R>>().start_vpn(config, ip, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub(crate) fn stop_vpn<R: Runtime>(app: AppHandle<R>) -> crate::Result<()> {
|
||||||
|
// Получаем состояние Vpn<R> и вызываем метод интерфейса
|
||||||
|
app.state::<Vpn<R>>().stop_vpn()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
|
use crate::models::*;
|
||||||
|
use crate::VpnInterface;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use tauri::{plugin::PluginApi, AppHandle, Runtime};
|
use tauri::{plugin::PluginApi, AppHandle, Runtime};
|
||||||
|
|
||||||
use crate::models::*;
|
// Инициализация для ПК
|
||||||
|
|
||||||
pub fn init<R: Runtime, C: DeserializeOwned>(
|
pub fn init<R: Runtime, C: DeserializeOwned>(
|
||||||
app: &AppHandle<R>,
|
app: &AppHandle<R>,
|
||||||
_api: PluginApi<R, C>,
|
_api: PluginApi<R, C>,
|
||||||
) -> crate::Result<Vpn<R>> {
|
) -> crate::Result<Vpn<R>> {
|
||||||
Ok(Vpn(app.clone()))
|
Ok(Vpn(app.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access to the vpn APIs.
|
|
||||||
pub struct Vpn<R: Runtime>(AppHandle<R>);
|
pub struct Vpn<R: Runtime>(AppHandle<R>);
|
||||||
|
|
||||||
impl<R: Runtime> Vpn<R> {
|
impl<R: Runtime> VpnInterface<R> for Vpn<R> {
|
||||||
pub fn ping(&self, payload: PingRequest) -> crate::Result<PingResponse> {
|
fn start_vpn(&self, _config: TunnelConfig, _ip: String, _port: i32) -> crate::Result<()> {
|
||||||
Ok(PingResponse {
|
// Логика для ПК: например, запуск системного процесса
|
||||||
value: payload.value,
|
Ok(())
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
fn stop_vpn(&self) -> crate::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
plugin::{Builder, TauriPlugin},
|
plugin::{Builder, TauriPlugin},
|
||||||
Manager, Runtime,
|
Manager, Runtime,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use models::*;
|
pub use models::*;
|
||||||
@@ -16,33 +17,40 @@ mod models;
|
|||||||
|
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
|
||||||
#[cfg(desktop)]
|
// В lib.rs
|
||||||
use desktop::Vpn;
|
pub trait VpnInterface<R: Runtime>: Send + Sync + 'static {
|
||||||
#[cfg(mobile)]
|
fn start_vpn(&self, config: TunnelConfig, ip: String, port: i32) -> crate::Result<()>;
|
||||||
use mobile::Vpn;
|
fn stop_vpn(&self) -> crate::Result<()>;
|
||||||
|
}
|
||||||
|
pub struct Vpn<R: Runtime>(Box<dyn VpnInterface<R>>);
|
||||||
|
|
||||||
/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the vpn APIs.
|
impl<R: Runtime> Vpn<R> {
|
||||||
pub trait VpnExt<R: Runtime> {
|
pub fn new<I: VpnInterface<R>>(inner: I) -> Self {
|
||||||
fn vpn(&self) -> &Vpn<R>;
|
Self(Box::new(inner))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Runtime, T: Manager<R>> crate::VpnExt<R> for T {
|
impl<R: Runtime> Deref for Vpn<R> {
|
||||||
fn vpn(&self) -> &Vpn<R> {
|
type Target = dyn VpnInterface<R>;
|
||||||
self.state::<Vpn<R>>().inner()
|
|
||||||
}
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the plugin.
|
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||||
Builder::new("vpn")
|
Builder::new("vpn")
|
||||||
.invoke_handler(tauri::generate_handler![commands::ping])
|
.invoke_handler(tauri::generate_handler![
|
||||||
.setup(|app, api| {
|
commands::start_vpn,
|
||||||
#[cfg(mobile)]
|
commands::stop_vpn
|
||||||
let vpn = mobile::init(app, api)?;
|
])
|
||||||
#[cfg(desktop)]
|
.setup(|app, api| {
|
||||||
let vpn = desktop::init(app, api)?;
|
#[cfg(mobile)]
|
||||||
app.manage(vpn);
|
let vpn_impl = mobile::init(app, api)?;
|
||||||
Ok(())
|
#[cfg(desktop)]
|
||||||
})
|
let vpn_impl = desktop::init(app, api)?;
|
||||||
.build()
|
app.manage(Vpn::new(vpn_impl));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,43 @@
|
|||||||
|
use crate::models::*;
|
||||||
|
use crate::VpnInterface;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
plugin::{PluginApi, PluginHandle},
|
plugin::{PluginApi, PluginHandle},
|
||||||
AppHandle, Runtime,
|
AppHandle, Runtime,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::models::*;
|
#[cfg(target_os = "android")]
|
||||||
|
const PLUGIN_IDENTIFIER: &str = "app.netrunner.vpn";
|
||||||
|
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
tauri::ios_plugin_binding!(init_plugin_vpn);
|
tauri::ios_plugin_binding!(init_plugin_vpn);
|
||||||
|
|
||||||
// initializes the Kotlin or Swift plugin classes
|
|
||||||
pub fn init<R: Runtime, C: DeserializeOwned>(
|
pub fn init<R: Runtime, C: DeserializeOwned>(
|
||||||
_app: &AppHandle<R>,
|
_app: &AppHandle<R>,
|
||||||
api: PluginApi<R, C>,
|
api: PluginApi<R, C>,
|
||||||
) -> crate::Result<Vpn<R>> {
|
) -> crate::Result<Vpn<R>> {
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
let handle = api.register_android_plugin("", "ExamplePlugin")?;
|
let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "VpnPlugin")?;
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
let handle = api.register_ios_plugin(init_plugin_vpn)?;
|
let handle = api.register_ios_plugin(init_plugin_vpn)?;
|
||||||
Ok(Vpn(handle))
|
Ok(Vpn(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access to the vpn APIs.
|
|
||||||
pub struct Vpn<R: Runtime>(PluginHandle<R>);
|
pub struct Vpn<R: Runtime>(PluginHandle<R>);
|
||||||
|
|
||||||
impl<R: Runtime> Vpn<R> {
|
impl<R: Runtime> VpnInterface<R> for Vpn<R> {
|
||||||
pub fn ping(&self, payload: PingRequest) -> crate::Result<PingResponse> {
|
fn start_vpn(&self, config: TunnelConfig, ip: String, port: i32) -> crate::Result<()> {
|
||||||
self
|
self.send("startVpn", StartVpnRequest { config, ip, port })
|
||||||
.0
|
}
|
||||||
.run_mobile_plugin("ping", payload)
|
|
||||||
.map_err(Into::into)
|
fn stop_vpn(&self) -> crate::Result<()> {
|
||||||
}
|
self.send("stopVpn", StopVpnRequest {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Приватный метод для уменьшения дублирования кода
|
||||||
|
impl<R: Runtime> Vpn<R> {
|
||||||
|
fn send<S: serde::Serialize>(&self, cmd: &str, payload: S) -> crate::Result<()> {
|
||||||
|
self.0.run_mobile_plugin(cmd, payload).map_err(Into::into)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TunnelConfig {
|
||||||
|
pub address: String,
|
||||||
|
pub prefix: i32, // Соответствует Int в Kotlin
|
||||||
|
pub dns: String,
|
||||||
|
pub mtu: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PingRequest {
|
pub struct StartVpnRequest {
|
||||||
pub value: Option<String>,
|
pub config: TunnelConfig,
|
||||||
|
pub ip: String,
|
||||||
|
pub port: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
pub struct StopVpnRequest {}
|
||||||
pub struct PingResponse {
|
|
||||||
pub value: Option<String>,
|
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -4,26 +4,37 @@ import { Power } from "lucide-react";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { TunnelConfig, startVpn, stopVpn } from "vpn-plugin";
|
||||||
|
|
||||||
export function VpnControl() {
|
export function VpnControl() {
|
||||||
const [status, setStatus] = useState<"idle" | "connecting" | "connected">(
|
const [status, setStatus] = useState<"idle" | "connecting" | "connected">(
|
||||||
"idle",
|
"idle",
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleToggle = async () => {
|
const handleToggle = async () => {
|
||||||
try {
|
try {
|
||||||
if (status === "idle") {
|
if (status === "idle") {
|
||||||
setStatus("connecting");
|
setStatus("connecting");
|
||||||
await invoke("start_vpn_android", {
|
|
||||||
remoteAddress: "62.60.244.156",
|
// Подготавливаем конфигурацию (замените на нужные вам значения)
|
||||||
});
|
const config: TunnelConfig = {
|
||||||
|
address: "10.0.0.2",
|
||||||
|
prefix: 24,
|
||||||
|
dns: "8.8.8.8",
|
||||||
|
mtu: 1400,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Вызываем вашу функцию-обертку
|
||||||
|
// ip и port берем из вашего примера
|
||||||
|
await startVpn(config, "62.60.244.156", 443);
|
||||||
|
|
||||||
setStatus("connected");
|
setStatus("connected");
|
||||||
} else {
|
} else {
|
||||||
|
// Вызываем функцию для остановки
|
||||||
|
await stopVpn();
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Ошибка VPN:", error);
|
console.error("Ошибка управления VPN:", error);
|
||||||
setStatus("idle");
|
setStatus("idle");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -93,6 +104,7 @@ export function VpnControl() {
|
|||||||
</svg>
|
</svg>
|
||||||
<button
|
<button
|
||||||
onClick={handleToggle}
|
onClick={handleToggle}
|
||||||
|
disabled={status === "connecting"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative size-40 rounded-full transition-all duration-700 flex items-center justify-center",
|
"relative size-40 rounded-full transition-all duration-700 flex items-center justify-center",
|
||||||
isActive
|
isActive
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@
|
|||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"],
|
||||||
|
"vpn-plugin": ["./src-tauri/src/plugins/vpn-plugin/guest-js"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export default defineConfig({
|
|||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
"vpn-plugin": path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"./src-tauri/src/plugins/vpn-plugin/guest-js",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user