Files
dark-shield/electron/main.ts
2025-11-11 12:09:49 -05:00

176 lines
4.1 KiB
TypeScript
Executable File

import { app, BrowserWindow, ipcMain, dialog } from 'electron';
import * as path from 'path';
import * as fs from 'fs';
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
const CONFIG_DIR = path.join(app.getPath('home'), '.config', 'wireguard-gui');
const CONFIG_FILE = path.join(CONFIG_DIR, 'current.conf');
const WG_INTERFACE = 'wg0';
const WG_CONF_PATH = `/etc/wireguard/${WG_INTERFACE}.conf`;
let mainWindow: BrowserWindow | null = null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 350,
height: 650,
resizable: false,
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
devTools: process.env.NODE_ENV === 'development',
},
});
// Remove menu bar completely
mainWindow.setMenuBarVisibility(false);
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
}
// Ensure config directory exists
function ensureConfigDir() {
if (!fs.existsSync(CONFIG_DIR)) {
fs.mkdirSync(CONFIG_DIR, { recursive: true });
}
}
// IPC Handlers
ipcMain.handle('select-config-file', async () => {
const result = await dialog.showOpenDialog(mainWindow!, {
properties: ['openFile'],
filters: [{ name: 'WireGuard Config', extensions: ['conf'] }],
});
if (result.canceled || result.filePaths.length === 0) {
return { success: false, error: 'No file selected' };
}
return { success: true, filePath: result.filePaths[0] };
});
ipcMain.handle('load-config', async (_event, filePath: string) => {
try {
ensureConfigDir();
// Copy config to our storage
fs.copyFileSync(filePath, CONFIG_FILE);
// Create symlink with sudo (non-interactive)
const symlinkCmd = `sudo -n ln -sf ${CONFIG_FILE} ${WG_CONF_PATH}`;
await execAsync(symlinkCmd);
return {
success: true,
configName: path.basename(filePath),
message: 'Config loaded successfully',
};
} catch (error: any) {
return {
success: false,
error: error.message || 'Failed to load config',
};
}
});
ipcMain.handle('connect-wireguard', async () => {
try {
const cmd = `sudo -n wg-quick up ${WG_INTERFACE}`;
const { stdout, stderr } = await execAsync(cmd);
return {
success: true,
message: 'Connected successfully',
output: stdout || stderr,
};
} catch (error: any) {
return {
success: false,
error: error.message || 'Failed to connect',
};
}
});
ipcMain.handle('disconnect-wireguard', async () => {
try {
const cmd = `sudo -n wg-quick down ${WG_INTERFACE}`;
const { stdout, stderr } = await execAsync(cmd);
return {
success: true,
message: 'Disconnected successfully',
output: stdout || stderr,
};
} catch (error: any) {
return {
success: false,
error: error.message || 'Failed to disconnect',
};
}
});
ipcMain.handle('get-connection-status', async () => {
try {
// Check if interface exists without requiring root
await execAsync(`ip link show ${WG_INTERFACE}`);
return {
success: true,
connected: true,
details: `${WG_INTERFACE} is up`,
};
} catch {
// ip link returns error if interface doesn't exist
return {
success: true,
connected: false,
details: '',
};
}
});
ipcMain.handle('get-current-config', async () => {
try {
if (fs.existsSync(CONFIG_FILE)) {
const configName = 'current.conf';
return {
success: true,
hasConfig: true,
configName,
};
}
return {
success: true,
hasConfig: false,
};
} catch (error: any) {
return {
success: false,
error: error.message,
};
}
});
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});