mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-04-02 18:31:50 -04:00
refactor: support edit the gp.conf
This commit is contained in:
parent
601f422863
commit
a10539e9c3
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -14,14 +14,17 @@
|
|||||||
"humantime",
|
"humantime",
|
||||||
"Immer",
|
"Immer",
|
||||||
"jnlp",
|
"jnlp",
|
||||||
|
"notistack",
|
||||||
"oneshot",
|
"oneshot",
|
||||||
"openconnect",
|
"openconnect",
|
||||||
|
"pkexec",
|
||||||
"prelogin",
|
"prelogin",
|
||||||
"prelogon",
|
"prelogon",
|
||||||
"prelogonuserauthcookie",
|
"prelogonuserauthcookie",
|
||||||
"repr",
|
"repr",
|
||||||
"rustc",
|
"rustc",
|
||||||
"tauri",
|
"tauri",
|
||||||
|
"tempdir",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"unlisten",
|
"unlisten",
|
||||||
"userauthcookie",
|
"userauthcookie",
|
||||||
|
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -2300,6 +2300,17 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_info"
|
||||||
|
version = "3.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -3398,6 +3409,7 @@ dependencies = [
|
|||||||
"objc",
|
"objc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"open",
|
"open",
|
||||||
|
"os_info",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/yuezk/GlobalProtect-openconnect/assets/3297602/9242df9c-217d-42ab-8c21-8f9f69cd4eb5">
|
||||||
|
</p>
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Build the service
|
### Build the service
|
||||||
|
19
com.yuezk.gp.policy
Normal file
19
com.yuezk.gp.policy
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||||
|
<policyconfig>
|
||||||
|
<vendor>The GlobalProtect-openconnect Project</vendor>
|
||||||
|
<vendor_url>https://github.com/yuezk/GlobalProtect-openconnect</vendor_url>
|
||||||
|
<icon_name>gpgui</icon_name>
|
||||||
|
<action id="com.yuezk.gp.update-gpconf">
|
||||||
|
<description>Update the /etc/gpservice/gp.conf</description>
|
||||||
|
<message>Authentication is required to update the GlobalProtect service configuration</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin</allow_any>
|
||||||
|
<allow_inactive>auth_admin</allow_inactive>
|
||||||
|
<allow_active>auth_admin</allow_active>
|
||||||
|
</defaults>
|
||||||
|
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/tee</annotate>
|
||||||
|
<annotate key="org.freedesktop.policykit.exec.argv1">/etc/gpservice/gp.conf</annotate>
|
||||||
|
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||||
|
</action>
|
||||||
|
</policyconfig>
|
@ -19,6 +19,7 @@
|
|||||||
"jotai": "^2.2.1",
|
"jotai": "^2.2.1",
|
||||||
"jotai-immer": "^0.2.0",
|
"jotai-immer": "^0.2.0",
|
||||||
"jotai-optics": "^0.3.0",
|
"jotai-optics": "^0.3.0",
|
||||||
|
"notistack": "^3.0.1",
|
||||||
"optics-ts": "^2.4.0",
|
"optics-ts": "^2.4.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
26
gpgui/pnpm-lock.yaml
generated
26
gpgui/pnpm-lock.yaml
generated
@ -35,6 +35,9 @@ dependencies:
|
|||||||
jotai-optics:
|
jotai-optics:
|
||||||
specifier: ^0.3.0
|
specifier: ^0.3.0
|
||||||
version: 0.3.0(jotai@2.2.1)(optics-ts@2.4.0)
|
version: 0.3.0(jotai@2.2.1)(optics-ts@2.4.0)
|
||||||
|
notistack:
|
||||||
|
specifier: ^3.0.1
|
||||||
|
version: 3.0.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0)
|
||||||
optics-ts:
|
optics-ts:
|
||||||
specifier: ^2.4.0
|
specifier: ^2.4.0
|
||||||
version: 2.4.0
|
version: 2.4.0
|
||||||
@ -1160,6 +1163,14 @@ packages:
|
|||||||
/function-bind@1.1.1:
|
/function-bind@1.1.1:
|
||||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
|
|
||||||
|
/goober@2.1.13(csstype@3.1.2):
|
||||||
|
resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==}
|
||||||
|
peerDependencies:
|
||||||
|
csstype: ^3.0.10
|
||||||
|
dependencies:
|
||||||
|
csstype: 3.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/has-flag@3.0.0:
|
/has-flag@3.0.0:
|
||||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -1257,6 +1268,21 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/notistack@3.0.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==}
|
||||||
|
engines: {node: '>=12.0.0', npm: '>=6.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
dependencies:
|
||||||
|
clsx: 1.2.1
|
||||||
|
goober: 2.1.13(csstype@3.1.2)
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- csstype
|
||||||
|
dev: false
|
||||||
|
|
||||||
/object-assign@4.1.1:
|
/object-assign@4.1.1:
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -16,7 +16,7 @@ tauri-build = { version = "1.3", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpcommon = { path = "../../gpcommon" }
|
gpcommon = { path = "../../gpcommon" }
|
||||||
tauri = { version = "1.3", features = ["http-all", "process-exit", "shell-open", "window-all", "window-data-url"] }
|
tauri = { version = "1.3", features = ["fs-write-file", "http-all", "os-all", "process-exit", "shell-open", "window-all", "window-data-url"] }
|
||||||
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [
|
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [
|
||||||
"colored",
|
"colored",
|
||||||
] }
|
] }
|
||||||
|
@ -6,9 +6,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use gpcommon::{Client, ServerApiError, VpnStatus};
|
use gpcommon::{Client, ServerApiError, VpnStatus};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::sync::Arc;
|
use std::{process::Stdio, sync::Arc};
|
||||||
use tauri::{AppHandle, State};
|
use tauri::{AppHandle, State};
|
||||||
use tokio::fs;
|
use tokio::{fs, io::AsyncWriteExt, process::Command};
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn service_online<'a>(client: State<'a, Arc<Client>>) -> Result<bool, ()> {
|
pub(crate) async fn service_online<'a>(client: State<'a, Arc<Client>>) -> Result<bool, ()> {
|
||||||
@ -62,8 +62,8 @@ pub(crate) fn os_version() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) fn openssl_config() -> String {
|
pub(crate) async fn openssl_config() -> Result<String, ()> {
|
||||||
get_openssl_conf()
|
Ok(get_openssl_conf())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -75,6 +75,40 @@ pub(crate) async fn update_openssl_config(app_handle: AppHandle) -> tauri::Resul
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub(crate) async fn openconnect_config() -> tauri::Result<String> {
|
||||||
|
let file = "/etc/gpservice/gp.conf";
|
||||||
|
let content = fs::read_to_string(file).await?;
|
||||||
|
Ok(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub(crate) async fn update_openconnect_config(content: String) -> tauri::Result<i32> {
|
||||||
|
let file = "/etc/gpservice/gp.conf";
|
||||||
|
let mut child = Command::new("pkexec")
|
||||||
|
.arg("tee")
|
||||||
|
.arg(file)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
let mut stdin = child.stdin.take().unwrap();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
stdin.write_all(content.as_bytes()).await.unwrap();
|
||||||
|
drop(stdin);
|
||||||
|
});
|
||||||
|
|
||||||
|
let exit_status = child.wait().await?;
|
||||||
|
|
||||||
|
exit_status.code().ok_or_else(|| {
|
||||||
|
tauri::Error::Io(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"Process exited without a code",
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn store_get<'a>(
|
pub(crate) async fn store_get<'a>(
|
||||||
hint: KeyHint<'_>,
|
hint: KeyHint<'_>,
|
||||||
|
@ -31,6 +31,8 @@ fn main() {
|
|||||||
commands::os_version,
|
commands::os_version,
|
||||||
commands::openssl_config,
|
commands::openssl_config,
|
||||||
commands::update_openssl_config,
|
commands::update_openssl_config,
|
||||||
|
commands::openconnect_config,
|
||||||
|
commands::update_openconnect_config,
|
||||||
commands::store_get,
|
commands::store_get,
|
||||||
commands::store_set,
|
commands::store_set,
|
||||||
commands::store_save,
|
commands::store_save,
|
||||||
|
@ -25,6 +25,13 @@
|
|||||||
},
|
},
|
||||||
"process": {
|
"process": {
|
||||||
"exit": true
|
"exit": true
|
||||||
|
},
|
||||||
|
"fs": {
|
||||||
|
"scope": ["$TEMP/gp.conf"],
|
||||||
|
"writeFile": true
|
||||||
|
},
|
||||||
|
"os": {
|
||||||
|
"all": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
@ -79,6 +79,12 @@ export const opensslConfigAtom = atomWithDefault(async () => {
|
|||||||
return settingsService.getOpenSSLConfig();
|
return settingsService.getOpenSSLConfig();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const openconnectConfigAtom = atomWithDefault<string | Promise<string>>(
|
||||||
|
() => {
|
||||||
|
return settingsService.getOpenconnectConfig();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const saveSettingsAtom = atom(null, async (get, set) => {
|
export const saveSettingsAtom = atom(null, async (get, set) => {
|
||||||
const clientOS = get(clientOSAtom);
|
const clientOS = get(clientOSAtom);
|
||||||
const osVersion = get(osVersionAtom);
|
const osVersion = get(osVersionAtom);
|
||||||
@ -95,4 +101,11 @@ export const saveSettingsAtom = atom(null, async (get, set) => {
|
|||||||
if (customOpenSSL) {
|
if (customOpenSSL) {
|
||||||
await settingsService.updateOpenSSLConfig();
|
await settingsService.updateOpenSSLConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialOpenconnectConfig = await settingsService.getOpenconnectConfig();
|
||||||
|
const openconnectConfig = await get(openconnectConfigAtom);
|
||||||
|
|
||||||
|
if (initialOpenconnectConfig !== openconnectConfig) {
|
||||||
|
await settingsService.updateOpenconnectConfig(openconnectConfig);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
|
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
|
||||||
|
import { SnackbarProvider } from "notistack";
|
||||||
import React, { Suspense } from "react";
|
import React, { Suspense } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
@ -27,8 +28,10 @@ function AppShell({ children }: { children: React.ReactNode }) {
|
|||||||
return (
|
return (
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<SnackbarProvider>
|
||||||
<Suspense fallback={<Loading />}>{children}</Suspense>
|
<CssBaseline />
|
||||||
|
<Suspense fallback={<Loading />}>{children}</Suspense>
|
||||||
|
</SnackbarProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,7 @@ export default function useGlobalTheme() {
|
|||||||
() =>
|
() =>
|
||||||
createTheme({
|
createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
mode: prefersDarkMode ? "dark" : "light",
|
mode: prefersDarkMode ? "light" : "light",
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
MuiButton: {
|
MuiButton: {
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
LockReset,
|
LockReset,
|
||||||
Menu as MenuIcon,
|
Menu as MenuIcon,
|
||||||
Settings,
|
Settings,
|
||||||
VpnLock,
|
SyncAlt,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { Box, Divider, IconButton, Menu, MenuItem } from "@mui/material";
|
import { Box, Divider, IconButton, Menu, MenuItem } from "@mui/material";
|
||||||
import { alpha, styled } from "@mui/material/styles";
|
import { alpha, styled } from "@mui/material/styles";
|
||||||
@ -73,7 +73,7 @@ export default function MainMenu() {
|
|||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={openGatewaySwitcher}>
|
<MenuItem onClick={openGatewaySwitcher}>
|
||||||
<VpnLock />
|
<SyncAlt />
|
||||||
Switch Gateway
|
Switch Gateway
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => openSettings()}>
|
<MenuItem onClick={() => openSettings()}>
|
||||||
|
59
gpgui/src/components/settings/OpenConnect.tsx
Normal file
59
gpgui/src/components/settings/OpenConnect.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { TabPanel } from "@mui/lab";
|
||||||
|
import { Alert, Box, Link, TextField, Typography } from "@mui/material";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { openconnectConfigAtom } from "../../atoms/settings";
|
||||||
|
|
||||||
|
export default function OpenConnect() {
|
||||||
|
const [openconnectConfig, setOpenconnectConfig] = useAtom(
|
||||||
|
openconnectConfigAtom
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabPanel
|
||||||
|
value="openconnect"
|
||||||
|
sx={{ flex: 1, display: "flex", flexDirection: "column" }}
|
||||||
|
>
|
||||||
|
<Alert severity="info">
|
||||||
|
You can edit the OpenConnect parameters here. More information can be
|
||||||
|
found{" "}
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/yuezk/GlobalProtect-openconnect/wiki/Configuration"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<Box mt={2} sx={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
||||||
|
<Typography variant="subtitle1">
|
||||||
|
File location: /etc/gpservice/gp.conf
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
value={openconnectConfig}
|
||||||
|
onChange={(event) => setOpenconnectConfig(event.target.value)}
|
||||||
|
sx={{
|
||||||
|
flex: 1,
|
||||||
|
display: "flex",
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
flex: "1 1 auto",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: 0,
|
||||||
|
|
||||||
|
"& textarea": {
|
||||||
|
whiteSpace: "pre",
|
||||||
|
fontFamily: "monospace",
|
||||||
|
overflow: "auto !important",
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</TabPanel>
|
||||||
|
);
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
import { Devices, Https } from "@mui/icons-material";
|
import { Devices, Https, VpnLock } from "@mui/icons-material";
|
||||||
import { TabContext, TabList } from "@mui/lab";
|
import { TabContext, TabList } from "@mui/lab";
|
||||||
import { Box, Button, DialogActions, Tab } from "@mui/material";
|
import { Box, Button, DialogActions, Tab } from "@mui/material";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
|
import { useSnackbar } from "notistack";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { saveSettingsAtom } from "../../atoms/settings";
|
import { saveSettingsAtom } from "../../atoms/settings";
|
||||||
import settingsService, { TabValue } from "../../services/settingsService";
|
import settingsService, { TabValue } from "../../services/settingsService";
|
||||||
|
import OpenConnect from "./OpenConnect";
|
||||||
import OpenSSL from "./OpenSSL";
|
import OpenSSL from "./OpenSSL";
|
||||||
import Simulation from "./Simulation";
|
import Simulation from "./Simulation";
|
||||||
|
|
||||||
@ -15,6 +17,7 @@ const activeTab = new URLSearchParams(window.location.search).get(
|
|||||||
export default function SettingsPanel() {
|
export default function SettingsPanel() {
|
||||||
const [value, setValue] = useState<TabValue>(activeTab);
|
const [value, setValue] = useState<TabValue>(activeTab);
|
||||||
const saveSettings = useSetAtom(saveSettingsAtom);
|
const saveSettings = useSetAtom(saveSettingsAtom);
|
||||||
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
|
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
|
||||||
setValue(newValue as TabValue);
|
setValue(newValue as TabValue);
|
||||||
@ -25,8 +28,14 @@ export default function SettingsPanel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
await saveSettings();
|
try {
|
||||||
await closeWindow();
|
await saveSettings();
|
||||||
|
enqueueSnackbar("Settings saved", { variant: "success" });
|
||||||
|
await closeWindow();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to save settings", err);
|
||||||
|
enqueueSnackbar("Failed to save settings", { variant: "error" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -43,16 +52,33 @@ export default function SettingsPanel() {
|
|||||||
value="simulation"
|
value="simulation"
|
||||||
icon={<Devices />}
|
icon={<Devices />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
|
sx={{ justifyContent: "start" }}
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
label="OpenConnect"
|
||||||
|
value="openconnect"
|
||||||
|
icon={<VpnLock />}
|
||||||
|
iconPosition="start"
|
||||||
|
sx={{ justifyContent: "start" }}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="OpenSSL"
|
label="OpenSSL"
|
||||||
value="openssl"
|
value="openssl"
|
||||||
icon={<Https />}
|
icon={<Https />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
|
sx={{ justifyContent: "start" }}
|
||||||
/>
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
<Box sx={{ flex: 1 }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
flex: 1,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
"& .MuiTabPanel-root[hidden]": { display: "none" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Simulation />
|
<Simulation />
|
||||||
|
<OpenConnect />
|
||||||
<OpenSSL />
|
<OpenSSL />
|
||||||
</Box>
|
</Box>
|
||||||
</TabContext>
|
</TabContext>
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import { tempdir } from "@tauri-apps/api/os";
|
||||||
import { UserAttentionType, WebviewWindow } from "@tauri-apps/api/window";
|
import { UserAttentionType, WebviewWindow } from "@tauri-apps/api/window";
|
||||||
import invokeCommand from "../utils/invokeCommand";
|
import invokeCommand from "../utils/invokeCommand";
|
||||||
import { appStorage } from "./storageService";
|
import { appStorage } from "./storageService";
|
||||||
|
import { fs } from "@tauri-apps/api";
|
||||||
|
import { Command } from "@tauri-apps/api/shell";
|
||||||
|
|
||||||
export type TabValue = "simulation" | "openssl";
|
export type TabValue = "simulation" | "openssl" | "openconnect";
|
||||||
const SETTINGS_WINDOW_LABEL = "settings";
|
const SETTINGS_WINDOW_LABEL = "settings";
|
||||||
|
|
||||||
async function openSettings(options?: { tab?: TabValue }) {
|
async function openSettings(options?: { tab?: TabValue }) {
|
||||||
@ -17,7 +20,7 @@ async function openSettings(options?: { tab?: TabValue }) {
|
|||||||
new WebviewWindow(SETTINGS_WINDOW_LABEL, {
|
new WebviewWindow(SETTINGS_WINDOW_LABEL, {
|
||||||
url: `pages/settings/index.html?tab=${tab}`,
|
url: `pages/settings/index.html?tab=${tab}`,
|
||||||
title: "GlobalProtect Settings",
|
title: "GlobalProtect Settings",
|
||||||
width: 650,
|
width: 680,
|
||||||
height: 480,
|
height: 480,
|
||||||
center: true,
|
center: true,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
@ -128,6 +131,23 @@ async function updateOpenSSLConfig() {
|
|||||||
return invokeCommand("update_openssl_config");
|
return invokeCommand("update_openssl_config");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getOpenconnectConfig(): Promise<string> {
|
||||||
|
try {
|
||||||
|
const content = await invokeCommand<string>("openconnect_config");
|
||||||
|
return content;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return "# Failed to read /etc/gpservice/gp.conf";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateOpenconnectConfig(content: string) {
|
||||||
|
const exitCode = await invokeCommand("update_openconnect_config", { content });
|
||||||
|
if (exitCode) {
|
||||||
|
throw new Error(`Failed to update openconnect config: ${exitCode}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
openSettings,
|
openSettings,
|
||||||
closeSettings,
|
closeSettings,
|
||||||
@ -137,4 +157,6 @@ export default {
|
|||||||
determineOsVersion,
|
determineOsVersion,
|
||||||
getOpenSSLConfig,
|
getOpenSSLConfig,
|
||||||
updateOpenSSLConfig,
|
updateOpenSSLConfig,
|
||||||
|
getOpenconnectConfig,
|
||||||
|
updateOpenconnectConfig,
|
||||||
};
|
};
|
||||||
|
BIN
screenshot-light.png
Normal file
BIN
screenshot-light.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
Loading…
Reference in New Issue
Block a user