packaging netbird with it
This commit is contained in:
@@ -13,13 +13,13 @@ const targets = [
|
|||||||
{
|
{
|
||||||
label: "macOS arm64",
|
label: "macOS arm64",
|
||||||
assetName: `netbird_${version}_darwin_arm64.tar.gz`,
|
assetName: `netbird_${version}_darwin_arm64.tar.gz`,
|
||||||
outputDir: join(import.meta.dirname, "resources"),
|
outputDir: join(import.meta.dirname, "..", "resources"),
|
||||||
files: [{ source: "netbird", destination: "netbird" }]
|
files: [{ source: "netbird", destination: "netbird" }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Windows amd64",
|
label: "Windows amd64",
|
||||||
assetName: `netbird_${version}_windows_amd64_signed.tar.gz`,
|
assetName: `netbird_${version}_windows_amd64_signed.tar.gz`,
|
||||||
outputDir: join(import.meta.dirname, "resources"),
|
outputDir: join(import.meta.dirname, "..", "resources"),
|
||||||
files: [
|
files: [
|
||||||
{ source: "netbird.exe", destination: "netbird.exe" },
|
{ source: "netbird.exe", destination: "netbird.exe" },
|
||||||
{ source: "wintun.dll", destination: "wintun.dll" }
|
{ source: "wintun.dll", destination: "wintun.dll" }
|
||||||
|
|||||||
+20
-5
@@ -55,6 +55,7 @@ app.on('activate', () => {
|
|||||||
// In this file you can include the rest of your app's specific main process
|
// In this file you can include the rest of your app's specific main process
|
||||||
// code. You can also put them in separate files and import them here.
|
// code. You can also put them in separate files and import them here.
|
||||||
let tray: Tray;
|
let tray: Tray;
|
||||||
|
let trayMenu: Menu;
|
||||||
let window: BrowserWindow;
|
let window: BrowserWindow;
|
||||||
let pollTimer: NodeJS.Timeout;
|
let pollTimer: NodeJS.Timeout;
|
||||||
let autoConnect = false;
|
let autoConnect = false;
|
||||||
@@ -103,7 +104,8 @@ function updateTray() {
|
|||||||
},
|
},
|
||||||
{ role: 'quit' }
|
{ role: 'quit' }
|
||||||
);
|
);
|
||||||
tray.setContextMenu(Menu.buildFromTemplate(template));
|
trayMenu = Menu.buildFromTemplate(template);
|
||||||
|
tray.setContextMenu(process.platform === 'darwin' ? null : trayMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStatus(status: NetworkStatus) {
|
function setStatus(status: NetworkStatus) {
|
||||||
@@ -141,11 +143,11 @@ async function connect() {
|
|||||||
|
|
||||||
const client = netbirdClient();
|
const client = netbirdClient();
|
||||||
const serviceReady = await client.ensureService();
|
const serviceReady = await client.ensureService();
|
||||||
if (!serviceReady) {
|
if (!serviceReady.ok) {
|
||||||
return setStatus({
|
return setStatus({
|
||||||
state: 'error',
|
state: 'error',
|
||||||
title: 'Connection failed',
|
title: 'Connection failed',
|
||||||
message: `${GATEWAY_HOST} helper did not become ready.`
|
message: serviceReady.message || `${GATEWAY_HOST} helper did not become ready.`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,11 +227,24 @@ function showWindow() {
|
|||||||
broadcastStatus();
|
broadcastStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleWindow() {
|
||||||
|
if (window.isVisible()) {
|
||||||
|
window.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showWindow();
|
||||||
|
}
|
||||||
|
|
||||||
function createTray() {
|
function createTray() {
|
||||||
const icon = nativeImage.createFromPath(resourcePath('VynteIcon.png')).resize({ width: 18, height: 18 });
|
const icon = nativeImage.createFromPath(resourcePath('VynteIcon.png')).resize({ width: 18, height: 18 });
|
||||||
tray = new Tray(icon);
|
tray = new Tray(icon);
|
||||||
updateTray();
|
updateTray();
|
||||||
tray.on('click', showWindow);
|
tray.on('click', toggleWindow);
|
||||||
|
tray.on('right-click', () => {
|
||||||
|
window.hide();
|
||||||
|
tray.popUpContextMenu(trayMenu);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.handle('status:get', () => withAutoConnect(cachedStatus));
|
ipcMain.handle('status:get', () => withAutoConnect(cachedStatus));
|
||||||
@@ -243,4 +258,4 @@ ipcMain.handle('auto:toggle', () => {
|
|||||||
return autoConnect;
|
return autoConnect;
|
||||||
});
|
});
|
||||||
ipcMain.handle('app:quit', () => app.quit());
|
ipcMain.handle('app:quit', () => app.quit());
|
||||||
ipcMain.handle('external:openGateway', () => shell.openExternal(MANAGEMENT_URL));
|
ipcMain.handle('external:openGateway', () => shell.openExternal(MANAGEMENT_URL));
|
||||||
|
|||||||
+98
-11
@@ -9,6 +9,20 @@ export interface CommandResult {
|
|||||||
error: Error | null;
|
error: Error | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ServiceReadyResult {
|
||||||
|
ok: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function commandDetails(label: string, result: CommandResult) {
|
||||||
|
const output = `${result.stdout}\n${result.stderr}\n${result.error?.message ?? ''}`.trim();
|
||||||
|
return output ? `${label}: ${output}` : `${label}: exited with code ${result.code ?? 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shellQuote(value: string) {
|
||||||
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
||||||
|
}
|
||||||
|
|
||||||
export class NetBirdClient {
|
export class NetBirdClient {
|
||||||
private executablePath: string;
|
private executablePath: string;
|
||||||
|
|
||||||
@@ -30,8 +44,30 @@ export class NetBirdClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deprecated???
|
runElevated(args: string[]): Promise<CommandResult> {
|
||||||
runElevated(args: string[]): Promise<Omit<CommandResult, 'code'>> {
|
if (process.platform === 'darwin') {
|
||||||
|
const command = [this.executablePath, ...args].map(shellQuote).join(' ');
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
execFile(
|
||||||
|
'osascript',
|
||||||
|
['-e', `do shell script ${JSON.stringify(command)} with administrator privileges`],
|
||||||
|
{
|
||||||
|
timeout: 180000,
|
||||||
|
},
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
resolve({
|
||||||
|
ok: !error,
|
||||||
|
code: error && typeof (error as NodeJS.ErrnoException).code === 'number' ? (error as NodeJS.ErrnoException).code : 0,
|
||||||
|
stdout: stdout || '',
|
||||||
|
stderr: stderr || '',
|
||||||
|
error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const quotedExe = this.executablePath.replace(/'/g, "''");
|
const quotedExe = this.executablePath.replace(/'/g, "''");
|
||||||
const quotedArgs = args.map(arg => `'${String(arg).replace(/'/g, "''")}'`).join(',');
|
const quotedArgs = args.map(arg => `'${String(arg).replace(/'/g, "''")}'`).join(',');
|
||||||
|
|
||||||
@@ -48,6 +84,7 @@ export class NetBirdClient {
|
|||||||
(error, stdout, stderr) => {
|
(error, stdout, stderr) => {
|
||||||
resolve({
|
resolve({
|
||||||
ok: !error,
|
ok: !error,
|
||||||
|
code: error && typeof (error as NodeJS.ErrnoException).code === 'number' ? (error as NodeJS.ErrnoException).code : 0,
|
||||||
stdout: stdout || '',
|
stdout: stdout || '',
|
||||||
stderr: stderr || '',
|
stderr: stderr || '',
|
||||||
error
|
error
|
||||||
@@ -63,32 +100,82 @@ export class NetBirdClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async ensureService(): Promise<boolean> {
|
async ensureService(): Promise<ServiceReadyResult> {
|
||||||
const status = await this.status({ timeout: 10000 });
|
const status = await this.status({ timeout: 10000 });
|
||||||
|
|
||||||
if (status.ok) return true;
|
if (status.ok) return { ok: true };
|
||||||
|
|
||||||
await this.run([
|
const serviceArgs = [
|
||||||
'service',
|
|
||||||
'install',
|
|
||||||
'--management-url',
|
'--management-url',
|
||||||
MANAGEMENT_URL,
|
MANAGEMENT_URL,
|
||||||
'--admin-url',
|
'--admin-url',
|
||||||
MANAGEMENT_URL,
|
MANAGEMENT_URL,
|
||||||
'--log-level',
|
'--log-level',
|
||||||
'info'
|
'info'
|
||||||
|
];
|
||||||
|
|
||||||
|
let install = await this.run([
|
||||||
|
'service',
|
||||||
|
'install',
|
||||||
|
...serviceArgs
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await this.run(['service', 'start']);
|
let installDetails = commandDetails('service install', install);
|
||||||
|
let serviceAlreadyInstalled = installDetails.includes('Init already exists');
|
||||||
|
if (!install.ok && !serviceAlreadyInstalled && process.platform === 'darwin') {
|
||||||
|
install = await this.runElevated([
|
||||||
|
'service',
|
||||||
|
'install',
|
||||||
|
...serviceArgs
|
||||||
|
]);
|
||||||
|
installDetails = commandDetails('service install', install);
|
||||||
|
serviceAlreadyInstalled = installDetails.includes('Init already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!install.ok && !serviceAlreadyInstalled) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
message: installDetails
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let reconfigureDetails = '';
|
||||||
|
if (serviceAlreadyInstalled) {
|
||||||
|
const reconfigure = await this.runElevated([
|
||||||
|
'service',
|
||||||
|
'reconfigure',
|
||||||
|
...serviceArgs
|
||||||
|
]);
|
||||||
|
if (!reconfigure.ok) {
|
||||||
|
reconfigureDetails = commandDetails('service reconfigure', reconfigure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = await this.run(['service', 'start']);
|
||||||
|
if (!start.ok && process.platform === 'darwin') {
|
||||||
|
start = await this.runElevated(['service', 'start']);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDetails = commandDetails('service start', start);
|
||||||
|
let lastStatus = commandDetails('status', status);
|
||||||
|
|
||||||
for (let index = 0; index < 20; index++) {
|
for (let index = 0; index < 20; index++) {
|
||||||
const check = await this.status({ timeout: 8000 });
|
const check = await this.status({ timeout: 8000 });
|
||||||
if (check.ok) return true;
|
if (check.ok) return { ok: true };
|
||||||
|
lastStatus = commandDetails('status', check);
|
||||||
|
|
||||||
await new Promise<void>(resolve => setTimeout(resolve, 500));
|
await new Promise<void>(resolve => setTimeout(resolve, 500));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return {
|
||||||
|
ok: false,
|
||||||
|
message: [
|
||||||
|
serviceAlreadyInstalled ? installDetails : '',
|
||||||
|
reconfigureDetails,
|
||||||
|
start.ok ? '' : startDetails,
|
||||||
|
lastStatus
|
||||||
|
].filter(Boolean).join('\n\n')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(): Promise<CommandResult> {
|
connect(): Promise<CommandResult> {
|
||||||
@@ -122,4 +209,4 @@ export class NetBirdClient {
|
|||||||
MANAGEMENT_URL
|
MANAGEMENT_URL
|
||||||
], { timeout: 60000 });
|
], { timeout: 60000 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user