diff --git a/build.gradle.kts b/build.gradle.kts index 0417509a..b59c1c84 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "ee.carlrobert" -version = "1.4.1" +version = "1.4.2" repositories { mavenCentral() diff --git a/src/main/java/ee/carlrobert/chatgpt/client/Client.java b/src/main/java/ee/carlrobert/chatgpt/client/Client.java index 606cfdec..c30d251f 100644 --- a/src/main/java/ee/carlrobert/chatgpt/client/Client.java +++ b/src/main/java/ee/carlrobert/chatgpt/client/Client.java @@ -11,6 +11,7 @@ import java.net.Proxy; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import okhttp3.Credentials; import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -68,6 +69,17 @@ public abstract class Client { var proxyPort = settings.proxyPort; if (!proxyHost.isEmpty() && proxyPort != 0) { builder.proxy(new Proxy(settings.proxyType, new InetSocketAddress(proxyHost, proxyPort))); + + var username = settings.proxyUsername; + var password = settings.proxyPassword; + if (settings.isProxyAuthSelected) { + builder.proxyAuthenticator((route, response) -> { + String credential = Credentials.basic(username, password); + return response.request().newBuilder() + .header("Proxy-Authorization", credential) + .build(); + }); + } } return builder.build(); diff --git a/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsComponent.java b/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsComponent.java index 7c48fa14..8bf078bf 100644 --- a/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsComponent.java +++ b/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsComponent.java @@ -3,6 +3,8 @@ package ee.carlrobert.chatgpt.ide.settings; import com.intellij.openapi.ui.ComboBox; import com.intellij.ui.PortField; import com.intellij.ui.TitledSeparator; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBPasswordField; import com.intellij.ui.components.JBRadioButton; import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.FormBuilder; @@ -10,6 +12,7 @@ import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UI; import ee.carlrobert.chatgpt.client.BaseModel; import java.awt.Desktop; +import java.awt.event.ItemEvent; import java.io.IOException; import java.net.Proxy; import java.net.URISyntaxException; @@ -37,6 +40,9 @@ public class SettingsComponent { private final ComboBox proxyTypeComboBox; private final JBTextField proxyHostField; private final PortField proxyPortField; + private final JBCheckBox proxyAuthCheckbox; + private final JBTextField proxyAuthUsername; + private final JBPasswordField proxyAuthPassword; public SettingsComponent(SettingsState settings) { apiKeyField = new JBTextField(settings.apiKey, 1); @@ -68,6 +74,16 @@ public class SettingsComponent { }); proxyTypeComboBox.setSelectedItem(settings.proxyType); proxyHostField = new JBTextField(settings.proxyHost, 20); + proxyAuthCheckbox = new JBCheckBox("Proxy authentication"); + proxyAuthUsername = new JBTextField(20); + proxyAuthUsername.setEnabled(settings.isProxyAuthSelected); + proxyAuthPassword = new JBPasswordField(); + proxyAuthPassword.setColumns(20); + proxyAuthPassword.setEnabled(settings.isProxyAuthSelected); + proxyAuthCheckbox.addItemListener(itemEvent -> { + proxyAuthUsername.setEnabled(itemEvent.getStateChange() == ItemEvent.SELECTED); + proxyAuthPassword.setEnabled(itemEvent.getStateChange() == ItemEvent.SELECTED); + }); useGPTRadioButton = new JBRadioButton("Use OpenAI's official API (recommended)", settings.isGPTOptionSelected); useChatCompletionRadioButton = new JBRadioButton("Use chat completion", settings.isChatCompletionOptionSelected); useTextCompletionRadioButton = new JBRadioButton("Use text completion", settings.isTextCompletionOptionSelected); @@ -177,6 +193,29 @@ public class SettingsComponent { proxyTypeComboBox.setSelectedItem(type); } + public boolean isProxyAuthSelected() { + return proxyAuthCheckbox.isSelected(); + } + + public void setUseProxyAuthentication(boolean isProxyAuthSelected) { + proxyAuthCheckbox.setSelected(isProxyAuthSelected); + } + + public String getProxyAuthUsername() { + return proxyAuthUsername.getText().trim(); + } + + public void setProxyUsername(String proxyUsername) { + proxyAuthUsername.setText(proxyUsername); + } + + public String getProxyAuthPassword() { + return new String(proxyAuthPassword.getPassword()); + } + + public void setProxyPassword(String proxyPassword) { + proxyAuthPassword.setText(proxyPassword); + } public BaseModel getTextCompletionBaseModel() { return (BaseModel) textCompletionBaseModelComboBox.getSelectedItem(); @@ -272,6 +311,22 @@ public class SettingsComponent { proxyPanel.add(proxyTypePanel); proxyPanel.add(proxyHostPanel); proxyPanel.add(proxyPortPanel); + proxyPanel.add(UI.PanelFactory + .panel(proxyAuthCheckbox) + .createPanel()); + + var proxyUsernamePanel = createPanel(proxyAuthUsername, "Username:", false); + var proxyPasswordPanel = createPanel(proxyAuthPassword, "Password:", false); + setEqualLabelWidths(proxyPasswordPanel, proxyUsernamePanel); + + var proxyAuthPanel = FormBuilder.createFormBuilder() + .addVerticalGap(8) + .addComponent(proxyUsernamePanel) + .addComponent(proxyPasswordPanel) + .getPanel(); + proxyAuthPanel.setBorder(JBUI.Borders.emptyLeft(16)); + proxyPanel.add(proxyAuthPanel); + return proxyPanel; } diff --git a/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsConfigurable.java b/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsConfigurable.java index b9d2efae..a5d7a1f8 100644 --- a/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsConfigurable.java +++ b/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsConfigurable.java @@ -37,6 +37,9 @@ public class SettingsConfigurable implements Configurable { !settingsComponent.getProxyHost().equals(settings.proxyHost) || settingsComponent.getProxyPort() != settings.proxyPort || !settingsComponent.getProxyType().equals(settings.proxyType) || + settingsComponent.isProxyAuthSelected() != settings.isProxyAuthSelected || + !settingsComponent.getProxyAuthUsername().equals(settings.proxyUsername) || + !settingsComponent.getProxyAuthPassword().equals(settings.proxyPassword) || !settingsComponent.getChatCompletionBaseModel().equals(settings.chatCompletionBaseModel) || !settingsComponent.getTextCompletionBaseModel().equals(settings.textCompletionBaseModel) || !settingsComponent.getReverseProxyUrl().equals(settings.reverseProxyUrl) || @@ -57,6 +60,9 @@ public class SettingsConfigurable implements Configurable { settings.proxyHost = settingsComponent.getProxyHost(); settings.proxyPort = settingsComponent.getProxyPort(); settings.proxyType = settingsComponent.getProxyType(); + settings.isProxyAuthSelected = settingsComponent.isProxyAuthSelected(); + settings.proxyUsername = settingsComponent.getProxyAuthUsername(); + settings.proxyPassword = settingsComponent.getProxyAuthPassword(); settings.apiKey = settingsComponent.getApiKey(); settings.reverseProxyUrl = settingsComponent.getReverseProxyUrl(); settings.chatCompletionBaseModel = settingsComponent.getChatCompletionBaseModel(); @@ -76,6 +82,9 @@ public class SettingsConfigurable implements Configurable { settingsComponent.setProxyHost(settings.proxyHost); settingsComponent.setProxyPort(settings.proxyPort); settingsComponent.setProxyType(settings.proxyType); + settingsComponent.setUseProxyAuthentication(settings.isProxyAuthSelected); + settingsComponent.setProxyUsername(settings.proxyUsername); + settingsComponent.setProxyPassword(settings.proxyPassword); settingsComponent.setApiKey(settings.apiKey); settingsComponent.setReverseProxyUrl(settings.reverseProxyUrl); settingsComponent.setChatCompletionBaseModel(settings.chatCompletionBaseModel); diff --git a/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsState.java b/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsState.java index 5a400647..cd184c79 100644 --- a/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsState.java +++ b/src/main/java/ee/carlrobert/chatgpt/ide/settings/SettingsState.java @@ -22,12 +22,15 @@ public class SettingsState implements PersistentStateComponent { public BaseModel textCompletionBaseModel = BaseModel.DAVINCI; public BaseModel chatCompletionBaseModel = BaseModel.CHATGPT; public boolean isGPTOptionSelected = true; - public boolean isChatGPTOptionSelected = false; + public boolean isChatGPTOptionSelected; public boolean isChatCompletionOptionSelected = true; - public boolean isTextCompletionOptionSelected = false; + public boolean isTextCompletionOptionSelected; public String proxyHost = ""; public int proxyPort; public Proxy.Type proxyType = Proxy.Type.SOCKS; + public boolean isProxyAuthSelected; + public String proxyUsername; + public String proxyPassword; public static SettingsState getInstance() { return ApplicationManager.getApplication().getService(SettingsState.class); diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 1af07d9b..f0385bb7 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -93,9 +93,7 @@ -
  • Add conversation history and ability to restore the previous sessions
  • -
  • Show the most recent conversation when opening the project
  • -
  • Visual improvements
  • +
  • Proxy auth support
  • ]]>