Merge pull request #2510 from LenAnderson/proxy-confirm ask user to confirm proxy connections
Signed| @@ -53,6 +53,7 @@ import { | ||
| 53 | 53 | getFileText, |
| 54 | 54 | getImageSizeFromDataURL, |
| 55 | 55 | getSortableDelay, |
| 56 | + getStringHash, | |
| 56 | 57 | isDataURL, |
| 57 | 58 | parseJsonFile, |
| 58 | 59 | resetScrollHeight, |
| @@ -72,6 +73,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; | ||
| 72 | 73 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; |
| 73 | 74 | import { renderTemplateAsync } from './templates.js'; |
| 74 | 75 | import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js'; |
| 76 | +import { Popup, POPUP_RESULT } from './popup.js'; | |
| 75 | 77 | |
| 76 | 78 | export { |
| 77 | 79 | openai_messages_count, |
| @@ -202,6 +204,15 @@ const custom_prompt_post_processing_types = { | ||
| 202 | 204 | CLAUDE: 'claude', |
| 203 | 205 | }; |
| 204 | 206 | |
| 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 | + | |
| 205 | 216 | function getPrefixMap() { |
| 206 | 217 | return selected_group ? { |
| 207 | 218 | assistant: '', |
| @@ -388,7 +399,7 @@ let openai_settings; | ||
| 388 | 399 | |
| 389 | 400 | let promptManager = null; |
| 390 | 401 | |
| 391 | 402 | async function validateReverseProxy() { |
| 392 | 403 | if (!oai_settings.reverse_proxy) { |
| 393 | 404 | return; |
| 394 | 405 | } |
| @@ -402,6 +413,19 @@ function validateReverseProxy() { | ||
| 402 | 413 | resultCheckStatus(); |
| 403 | 414 | throw err; |
| 404 | 415 | } |
| 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)); | |
| 405 | 429 | } |
| 406 | 430 | |
| 407 | 431 | /** |
| @@ -1787,7 +1811,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1787 | 1811 | |
| 1788 | 1812 | // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite |
| 1789 | 1813 | 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)) { |
| 1790 | 1814 | await validateReverseProxy(); |
| 1791 | 1815 | generate_data['reverse_proxy'] = oai_settings.reverse_proxy; |
| 1792 | 1816 | generate_data['proxy_password'] = oai_settings.proxy_password; |
| 1793 | 1817 | } |
| @@ -3200,7 +3224,7 @@ async function getStatusOpen() { | ||
| 3200 | 3224 | }; |
| 3201 | 3225 | |
| 3202 | 3226 | if (oai_settings.reverse_proxy && (oai_settings.chat_completion_source === chat_completion_sources.OPENAI || oai_settings.chat_completion_source === chat_completion_sources.CLAUDE)) { |
| 3203 | 3227 | await validateReverseProxy(); |
| 3204 | 3228 | } |
| 3205 | 3229 | |
| 3206 | 3230 | if (oai_settings.chat_completion_source === chat_completion_sources.CUSTOM) { |
| @@ -3495,6 +3519,26 @@ async function onPresetImportFileChange(e) { | ||
| 3495 | 3519 | return; |
| 3496 | 3520 | } |
| 3497 | 3521 | |
| 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 | + | |
| 3498 | 3542 | if (name in openai_setting_names) { |
| 3499 | 3543 | const confirm = await callPopup('Preset name already exists. Overwrite?', 'confirm'); |
| 3500 | 3544 | |
| @@ -3541,8 +3585,22 @@ async function onExportPresetClick() { | ||
| 3541 | 3585 | |
| 3542 | 3586 | const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]); |
| 3543 | 3587 | |
| 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 | + } | |
| 3546 | 3604 | |
| 3547 | 3605 | const presetJsonString = JSON.stringify(preset, null, 4); |
| 3548 | 3606 | const presetFileName = `${oai_settings.preset_settings_openai}.json`; |