Merge pull request #2510 from LenAnderson/proxy-confirm ask user to confirm proxy connections

2232a4fbd75563503d8039eedd8827100ece8170

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +63 -5Ignore whitespace
public/scripts/openai.js+63 -5
@@ -53,6 +53,7 @@ import {
5353 getFileText,
5454 getImageSizeFromDataURL,
5555 getSortableDelay,
56+ getStringHash,
5657 isDataURL,
5758 parseJsonFile,
5859 resetScrollHeight,
@@ -72,6 +73,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
7273import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
7374import { renderTemplateAsync } from './templates.js';
7475import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
76+import { Popup, POPUP_RESULT } from './popup.js';
7577
7678export {
7779 openai_messages_count,
@@ -202,6 +204,15 @@ const custom_prompt_post_processing_types = {
202204 CLAUDE: 'claude',
203205};
204206
207+const sensitiveFields = [
208+ 'reverse_proxy',
209+ 'proxy_password',
210+ 'custom_url',
211+ 'custom_include_body',
212+ 'custom_exclude_body',
213+ 'custom_include_headers',
214+];
215+
205216function getPrefixMap() {
206217 return selected_group ? {
207218 assistant: '',
@@ -388,7 +399,7 @@ let openai_settings;
388399
389400let promptManager = null;
390401
391402async function validateReverseProxy() {
392403 if (!oai_settings.reverse_proxy) {
393404 return;
394405 }
@@ -402,6 +413,19 @@ function validateReverseProxy() {
402413 resultCheckStatus();
403414 throw err;
404415 }
416+ const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
417+ const skipConfirm = localStorage.getItem(rememberKey) === 'true';
418+
419+ const confirmation = skipConfirm || await Popup.show.confirm('Connecting To Proxy', `<span>Are you sure you want to connect to the following proxy URL?</span><var>${DOMPurify.sanitize(oai_settings.reverse_proxy)}</var>`);
420+
421+ if (!confirmation) {
422+ toastr.error('Update or remove your reverse proxy settings.');
423+ setOnlineStatus('no_connection');
424+ resultCheckStatus();
425+ throw new Error('Proxy connection denied.');
426+ }
427+
428+ localStorage.setItem(rememberKey, String(true));
405429}
406430
407431/**
@@ -1787,7 +1811,7 @@ async function sendOpenAIRequest(type, messages, signal) {
17871811
17881812 // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite
17891813 if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE].includes(oai_settings.chat_completion_source)) {
17901814 await validateReverseProxy();
17911815 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
17921816 generate_data['proxy_password'] = oai_settings.proxy_password;
17931817 }
@@ -3200,7 +3224,7 @@ async function getStatusOpen() {
32003224 };
32013225
32023226 if (oai_settings.reverse_proxy && (oai_settings.chat_completion_source === chat_completion_sources.OPENAI || oai_settings.chat_completion_source === chat_completion_sources.CLAUDE)) {
32033227 await validateReverseProxy();
32043228 }
32053229
32063230 if (oai_settings.chat_completion_source === chat_completion_sources.CUSTOM) {
@@ -3495,6 +3519,26 @@ async function onPresetImportFileChange(e) {
34953519 return;
34963520 }
34973521
3522+ const fields = sensitiveFields.filter(field => presetBody[field]).map(field => `<b>${field}</b>`);
3523+ const shouldConfirm = fields.length > 0;
3524+
3525+ if (shouldConfirm) {
3526+ const textHeader = 'The imported preset contains proxy and/or custom endpoint settings.';
3527+ const textMessage = fields.join('<br>');
3528+ const cancelButton = { text: 'Cancel import', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
3529+ const popupOptions = { customButtons: [cancelButton], okButton: 'Remove them', cancelButton: 'Import as-is' };
3530+ const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);
3531+
3532+ if (popupResult === POPUP_RESULT.CANCELLED) {
3533+ console.log('Import cancelled by user');
3534+ return;
3535+ }
3536+
3537+ if (popupResult === POPUP_RESULT.AFFIRMATIVE) {
3538+ sensitiveFields.forEach(field => delete presetBody[field]);
3539+ }
3540+ }
3541+
34983542 if (name in openai_setting_names) {
34993543 const confirm = await callPopup('Preset name already exists. Overwrite?', 'confirm');
35003544
@@ -3541,8 +3585,22 @@ async function onExportPresetClick() {
35413585
35423586 const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]);
35433587
3544- delete preset.reverse_proxy;
3588+ const fieldValues = sensitiveFields.filter(field => preset[field]).map(field => `<b>${field}</b>: <code>${preset[field]}</code>`);
3545- delete preset.proxy_password;
3589+ const shouldConfirm = fieldValues.length > 0;
3590+ const textHeader = 'Your preset contains proxy and/or custom endpoint settings.';
3591+ const textMessage = `<div>Do you want to remove these fields before exporting?</div><br>${DOMPurify.sanitize(fieldValues.join('<br>'))}`;
3592+ const cancelButton = { text: 'Cancel', result: POPUP_RESULT.CANCELLED, appendAtEnd: true };
3593+ const popupOptions = { customButtons: [cancelButton] };
3594+ const popupResult = await Popup.show.confirm(textHeader, textMessage, popupOptions);
3595+
3596+ if (popupResult === POPUP_RESULT.CANCELLED) {
3597+ console.log('Export cancelled by user');
3598+ return;
3599+ }
3600+
3601+ if (!shouldConfirm || popupResult === POPUP_RESULT.AFFIRMATIVE) {
3602+ sensitiveFields.forEach(field => delete preset[field]);
3603+ }
35463604
35473605 const presetJsonString = JSON.stringify(preset, null, 4);
35483606 const presetFileName = `${oai_settings.preset_settings_openai}.json`;