Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/common/types/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export interface UserSettings {
installId?: string; // Unique install identifier for analytics
machineId?: string; // @deprecated - legacy machine identifier retained for migrations
pendingWhatsNewVersion?: string | null; // Version whose What's New should be shown after restart (auto-update)
httpProxy?: string; // HTTP proxy URL (e.g. http://user:pass@host:port), empty string means no proxy
// Sort preferences
installedToolsSort?: InstalledToolsSortOption;
connectionsSort?: ConnectionsSortOption;
Expand Down
40 changes: 39 additions & 1 deletion src/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { app, BrowserWindow, clipboard, dialog, ipcMain, Menu, MenuItemConstructorOptions, nativeTheme, shell } from "electron";
import { app, BrowserWindow, clipboard, dialog, ipcMain, Menu, MenuItemConstructorOptions, nativeTheme, session, shell } from "electron";
import * as fs from "fs";
import { createWriteStream } from "fs";
import * as http from "http";
Expand Down Expand Up @@ -396,6 +396,10 @@ class ToolBoxApp {
ipcMain.handle(SETTINGS_CHANNELS.UPDATE_USER_SETTINGS, (_, settings) => {
this.settingsManager.updateUserSettings(settings);
this.api.emitEvent(ToolBoxEvent.SETTINGS_UPDATED, settings);
// Apply proxy immediately if the httpProxy setting changed
if ("httpProxy" in settings) {
this.applyProxySettings(settings.httpProxy ?? "");
}
});

ipcMain.handle(SETTINGS_CHANNELS.GET_SETTING, (_, key) => {
Expand Down Expand Up @@ -2420,6 +2424,34 @@ class ToolBoxApp {
this.sendWhatsNewRequest("auto-update", currentVersion);
}

/**
* Apply HTTP proxy settings to the Electron session and Node.js environment variables.
* @param proxyUrl - Proxy URL (e.g. "http://user:pass@host:port") or empty string to clear.
*/
private applyProxySettings(proxyUrl: string): void {
const trimmed = proxyUrl.trim();
Comment thread
Power-Maverick marked this conversation as resolved.
if (trimmed) {
// Redact credentials from URL before logging
const redacted = trimmed.replace(/:\/\/([^:@]+:[^@]+@)/, "://<redacted>@");
// Configure Chromium-based network requests (renderer, BrowserViews)
session.defaultSession.setProxy({ proxyRules: trimmed }).catch((err) => {
logError(err instanceof Error ? err : new Error(String(err)));
});
// Configure Node.js native network requests (main process fetch, http, https)
process.env.HTTP_PROXY = trimmed;
process.env.HTTPS_PROXY = trimmed;
logInfo(`[Proxy] HTTP proxy configured: ${redacted}`);
Comment on lines +2438 to +2443
} else {
// Clear proxy — use direct connection
session.defaultSession.setProxy({ proxyRules: "" }).catch((err) => {
logError(err instanceof Error ? err : new Error(String(err)));
});
delete process.env.HTTP_PROXY;
delete process.env.HTTPS_PROXY;
logInfo("[Proxy] HTTP proxy cleared — using direct connection");
}
}

/**
* Check Supabase connectivity
* Tests if the Supabase API is accessible
Expand Down Expand Up @@ -2776,6 +2808,12 @@ class ToolBoxApp {
// Register protocol handler after app is ready
this.browserviewProtocolManager.registerHandler();

// Apply saved proxy settings before any network requests (apply even if empty to clear any env vars)
const savedProxy = this.settingsManager.getSetting("httpProxy");
if (savedProxy !== undefined) {
this.applyProxySettings(savedProxy);
}

this.createWindow();
logCheckpoint("Main window created");

Expand Down
1 change: 1 addition & 0 deletions src/main/managers/settingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class SettingsManager {
toolConnections: {}, // Map of toolId to connectionId
toolSecondaryConnections: {}, // Map of toolId to secondary connectionId
connectionsSort: "last-used",
httpProxy: "", // HTTP proxy URL, empty means no proxy
},
});

Expand Down
14 changes: 14 additions & 0 deletions src/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,20 @@ <h2 class="sidebar-title">SETTINGS</h2>
</div>
</section>

<section class="settings-section-card">
<div class="settings-section-header">
<p class="settings-section-eyebrow">Network</p>
<p class="settings-section-description">Configure a proxy server for all internet connections made by ToolBox.</p>
</div>
<div class="settings-section-body">
<div class="settings-field">
<label class="setting-label" for="sidebar-http-proxy-input">HTTP Proxy</label>
<input type="text" id="sidebar-http-proxy-input" class="fluent-input" placeholder="http://user:password@host:port" />
<span class="setting-hint">Leave empty for a direct connection. Format: <code>http://user:password@host:port</code>. Credentials are stored in plain text in local settings. Applies to all ToolBox network requests on save.</span>
Comment on lines +389 to +390
</div>
</div>
</section>

<section class="settings-section-card">
<div class="settings-section-header">
<p class="settings-section-eyebrow">Updates</p>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/modules/globalSearchManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const SETTINGS_ENTRIES: Array<{ name: string; description: string; focusId?: str
{ name: "Terminal Font", description: "Customize the integrated terminal font", focusId: "sidebar-terminal-font-select" },
{ name: "Deprecated Tools", description: "Control visibility of deprecated tools", focusId: "sidebar-deprecated-tools-select" },
{ name: "Tool Display Mode", description: "Choose standard or compact tool display", focusId: "sidebar-tool-display-mode-select" },
{ name: "HTTP Proxy", description: "Configure a proxy server for internet connections", focusId: "sidebar-http-proxy-input" },
{ name: "Connections", description: "Manage Dataverse connections" },
{ name: "Installed Tools", description: "Browse installed tools" },
{ name: "Marketplace", description: "Browse and install tools from the marketplace" },
Expand Down
12 changes: 12 additions & 0 deletions src/renderer/modules/settingsManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export async function loadSidebarSettings(): Promise<void> {
const customFontInput = document.getElementById("sidebar-terminal-font-custom") as HTMLInputElement;
const customFontContainer = document.getElementById("custom-font-input-container");
const notificationDurationSelect = document.getElementById("sidebar-notification-duration-select") as HTMLSelectElement | null;
const httpProxyInput = document.getElementById("sidebar-http-proxy-input") as HTMLInputElement | null;

if (themeSelect && autoUpdateCheck && showDebugMenuCheck && deprecatedToolsSelect && toolDisplayModeSelect && terminalFontSelect) {
const settings = await window.toolboxAPI.getUserSettings();
Expand All @@ -39,6 +40,7 @@ export async function loadSidebarSettings(): Promise<void> {
toolDisplayMode: settings.toolDisplayMode ?? "standard",
terminalFont: settings.terminalFont || DEFAULT_TERMINAL_FONT,
notificationDuration: settings.notificationDuration ?? DEFAULT_NOTIFICATION_DURATION,
httpProxy: settings.httpProxy ?? "",
};

themeSelect.value = settings.theme;
Expand All @@ -51,6 +53,10 @@ export async function loadSidebarSettings(): Promise<void> {
notificationDurationSelect.value = String(settings.notificationDuration ?? DEFAULT_NOTIFICATION_DURATION);
}

if (httpProxyInput) {
httpProxyInput.value = settings.httpProxy ?? "";
}

const terminalFont = settings.terminalFont || DEFAULT_TERMINAL_FONT;

// Check if the font is a predefined option
Expand Down Expand Up @@ -90,6 +96,7 @@ export async function saveSidebarSettings(): Promise<void> {
const terminalFontSelect = document.getElementById("sidebar-terminal-font-select") as any; // Fluent UI select element
const customFontInput = document.getElementById("sidebar-terminal-font-custom") as HTMLInputElement;
const notificationDurationSelect = document.getElementById("sidebar-notification-duration-select") as HTMLSelectElement | null;
const httpProxyInput = document.getElementById("sidebar-http-proxy-input") as HTMLInputElement | null;

if (!themeSelect || !autoUpdateCheck || !showDebugMenuCheck || !deprecatedToolsSelect || !toolDisplayModeSelect || !terminalFontSelect) return;

Expand All @@ -101,6 +108,7 @@ export async function saveSidebarSettings(): Promise<void> {
}

const notificationDuration = notificationDurationSelect ? Number(notificationDurationSelect.value) : 5000;
const httpProxy = httpProxyInput ? httpProxyInput.value.trim() : "";

const currentSettings = {
theme: themeSelect.value,
Expand All @@ -110,6 +118,7 @@ export async function saveSidebarSettings(): Promise<void> {
toolDisplayMode: toolDisplayModeSelect.value,
terminalFont: terminalFont,
notificationDuration,
httpProxy,
};

// Only include changed settings in the update
Expand All @@ -136,6 +145,9 @@ export async function saveSidebarSettings(): Promise<void> {
if (currentSettings.notificationDuration !== originalSettings.notificationDuration) {
changedSettings.notificationDuration = currentSettings.notificationDuration;
}
if (currentSettings.httpProxy !== originalSettings.httpProxy) {
changedSettings.httpProxy = currentSettings.httpProxy;
}

// Only save and emit event if something changed
if (Object.keys(changedSettings).length > 0) {
Expand Down
1 change: 1 addition & 0 deletions src/renderer/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface SettingsState {
toolDisplayMode?: string;
terminalFont?: string;
notificationDuration?: number;
httpProxy?: string;
}

/**
Expand Down
Loading