fedora
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property var connections: ({})
|
||||
property bool refreshing: false
|
||||
property bool connecting: false
|
||||
property bool disconnecting: false
|
||||
property string connectingUuid: ""
|
||||
property string disconnectingUuid: ""
|
||||
property string lastError: ""
|
||||
property bool refreshPending: false
|
||||
|
||||
readonly property var activeConnections: {
|
||||
const result = [];
|
||||
const map = connections;
|
||||
for (const key in map) {
|
||||
const conn = map[key];
|
||||
if (conn && conn.active) {
|
||||
result.push(conn);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
readonly property var inactiveConnections: {
|
||||
const result = [];
|
||||
const map = connections;
|
||||
for (const key in map) {
|
||||
const conn = map[key];
|
||||
if (conn && !conn.active) {
|
||||
result.push(conn);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
readonly property bool hasActiveConnection: activeConnections.length > 0
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 5000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: refresh()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedRefreshTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: refresh()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("VPN", "Service started");
|
||||
refresh();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (refreshing) {
|
||||
refreshPending = true;
|
||||
return;
|
||||
}
|
||||
refreshing = true;
|
||||
lastError = "";
|
||||
refreshProcess.running = true;
|
||||
}
|
||||
|
||||
function connect(uuid) {
|
||||
if (connecting || !uuid) {
|
||||
return;
|
||||
}
|
||||
const conn = connections[uuid];
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
connecting = true;
|
||||
connectingUuid = uuid;
|
||||
lastError = "";
|
||||
connectProcess.uuid = uuid;
|
||||
connectProcess.name = conn.name;
|
||||
connectProcess.running = true;
|
||||
}
|
||||
|
||||
function disconnect(uuid) {
|
||||
if (disconnecting || !uuid) {
|
||||
return;
|
||||
}
|
||||
const conn = connections[uuid];
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
disconnecting = true;
|
||||
disconnectingUuid = uuid;
|
||||
lastError = "";
|
||||
disconnectProcess.uuid = uuid;
|
||||
disconnectProcess.name = conn.name;
|
||||
disconnectProcess.running = true;
|
||||
}
|
||||
|
||||
function toggle(uuid) {
|
||||
const conn = connections[uuid];
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
if (conn.active) {
|
||||
disconnect(uuid);
|
||||
} else {
|
||||
connect(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
function setConnection(uuid, data) {
|
||||
if (!uuid) {
|
||||
return;
|
||||
}
|
||||
const map = Object.assign({}, connections);
|
||||
if (map[uuid]) {
|
||||
map[uuid] = Object.assign({}, map[uuid], data);
|
||||
connections = map;
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleRefresh(interval) {
|
||||
delayedRefreshTimer.interval = interval;
|
||||
delayedRefreshTimer.restart();
|
||||
}
|
||||
|
||||
Process {
|
||||
id: refreshProcess
|
||||
running: false
|
||||
command: ["nmcli", "-t", "-f", "NAME,UUID,TYPE,DEVICE", "connection", "show"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.split("\n");
|
||||
const map = {};
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
const line = lines[i].trim();
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
const lastColonIdx = line.lastIndexOf(":");
|
||||
if (lastColonIdx === -1) {
|
||||
continue;
|
||||
}
|
||||
const device = line.substring(lastColonIdx + 1);
|
||||
const remaining = line.substring(0, lastColonIdx);
|
||||
const secondLastColonIdx = remaining.lastIndexOf(":");
|
||||
if (secondLastColonIdx === -1) {
|
||||
continue;
|
||||
}
|
||||
const type = remaining.substring(secondLastColonIdx + 1);
|
||||
if (type !== "vpn" && type !== "wireguard") {
|
||||
continue;
|
||||
}
|
||||
const remaining2 = remaining.substring(0, secondLastColonIdx);
|
||||
const thirdLastColonIdx = remaining2.lastIndexOf(":");
|
||||
if (thirdLastColonIdx === -1) {
|
||||
continue;
|
||||
}
|
||||
const uuid = remaining2.substring(thirdLastColonIdx + 1);
|
||||
const name = remaining2.substring(0, thirdLastColonIdx);
|
||||
if (!uuid || !name) {
|
||||
continue;
|
||||
}
|
||||
const active = device && device !== "--";
|
||||
map[uuid] = {
|
||||
"uuid": uuid,
|
||||
"name": name,
|
||||
"device": device,
|
||||
"active": active
|
||||
};
|
||||
}
|
||||
connections = map;
|
||||
const pending = refreshPending;
|
||||
refreshing = false;
|
||||
refreshPending = false;
|
||||
if (pending) {
|
||||
scheduleRefresh(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const pending = refreshPending;
|
||||
refreshing = false;
|
||||
refreshPending = false;
|
||||
if (text.trim()) {
|
||||
lastError = text.split("\n")[0].trim();
|
||||
Logger.w("VPN", "Refresh error: " + text);
|
||||
}
|
||||
if (pending) {
|
||||
scheduleRefresh(2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: connectProcess
|
||||
property string uuid: ""
|
||||
property string name: ""
|
||||
running: false
|
||||
command: ["nmcli", "connection", "up", "uuid", uuid]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const output = text.trim();
|
||||
if (!output || (!output.includes("successfully activated") && !output.includes("Connection successfully"))) {
|
||||
return;
|
||||
}
|
||||
setConnection(connectProcess.uuid, {
|
||||
"active": true
|
||||
});
|
||||
connecting = false;
|
||||
connectingUuid = "";
|
||||
lastError = "";
|
||||
Logger.i("VPN", "Connected to " + connectProcess.name);
|
||||
ToastService.showNotice(connectProcess.name, I18n.tr("toast.vpn.connected", {
|
||||
"name": connectProcess.name
|
||||
}), "shield-lock");
|
||||
scheduleRefresh(1000);
|
||||
}
|
||||
}
|
||||
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const trimmed = text.trim();
|
||||
if (trimmed) {
|
||||
lastError = trimmed.split("\n")[0].trim();
|
||||
Logger.w("VPN", "Connect error: " + trimmed);
|
||||
ToastService.showWarning(connectProcess.name, lastError);
|
||||
}
|
||||
connecting = false;
|
||||
connectingUuid = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: disconnectProcess
|
||||
property string uuid: ""
|
||||
property string name: ""
|
||||
running: false
|
||||
command: ["nmcli", "connection", "down", "uuid", uuid]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
Logger.i("VPN", "Disconnected from " + disconnectProcess.name);
|
||||
setConnection(disconnectProcess.uuid, {
|
||||
"active": false,
|
||||
"device": ""
|
||||
});
|
||||
disconnecting = false;
|
||||
disconnectingUuid = "";
|
||||
lastError = "";
|
||||
ToastService.showNotice(disconnectProcess.name, I18n.tr("toast.vpn.disconnected", {
|
||||
"name": disconnectProcess.name
|
||||
}), "shield-off");
|
||||
scheduleRefresh(1000);
|
||||
}
|
||||
}
|
||||
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const trimmed = text.trim();
|
||||
if (trimmed) {
|
||||
lastError = trimmed.split("\n")[0].trim();
|
||||
Logger.w("VPN", "Disconnect error: " + trimmed);
|
||||
ToastService.showWarning(disconnectProcess.name, lastError);
|
||||
}
|
||||
disconnecting = false;
|
||||
disconnectingUuid = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user