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 -5Showing whitespace changes
public/scripts/openai.js+63 -5
@@ -53,6 +53,7 @@ import {
53 getFileText,53 getFileText,
54 getImageSizeFromDataURL,54 getImageSizeFromDataURL,
55 getSortableDelay,55 getSortableDelay,
56 getStringHash,
56 isDataURL,57 isDataURL,
57 parseJsonFile,58 parseJsonFile,
58 resetScrollHeight,59 resetScrollHeight,
@@ -72,6 +73,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
72import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';73import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
73import { renderTemplateAsync } from './templates.js';74import { renderTemplateAsync } from './templates.js';
74import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';75import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
76import { Popup, POPUP_RESULT } from './popup.js';
7577
76export {78export {
77 openai_messages_count,79 openai_messages_count,
@@ -202,6 +204,15 @@ const custom_prompt_post_processing_types = {
202 CLAUDE: 'claude',204 CLAUDE: 'claude',
203};205};
204206
207const sensitiveFields = [
208 'reverse_proxy',
209 'proxy_password',
210 'custom_url',
211 'custom_include_body',
212 'custom_exclude_body',
213 'custom_include_headers',
214];
215
205function getPrefixMap() {216function getPrefixMap() {
206 return selected_group ? {217 return selected_group ? {
207 assistant: '',218 assistant: '',
@@ -388,7 +399,7 @@ let openai_settings;
388399
389let promptManager = null;400let promptManager = null;
390401
391function validateReverseProxy() {402async function validateReverseProxy() {
392 if (!oai_settings.reverse_proxy) {403 if (!oai_settings.reverse_proxy) {
393 return;404 return;
394 }405 }
@@ -402,6 +413,19 @@ function validateReverseProxy() {
402 resultCheckStatus();413 resultCheckStatus();
403 throw err;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}
406430
407/**431/**
@@ -1787,7 +1811,7 @@ async function sendOpenAIRequest(type, messages, signal) {
17871811
1788 // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite1812 // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite
1789 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)) {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 validateReverseProxy();1814 await validateReverseProxy();
1791 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;1815 generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
1792 generate_data['proxy_password'] = oai_settings.proxy_password;1816 generate_data['proxy_password'] = oai_settings.proxy_password;
1793 }1817 }
@@ -3200,7 +3224,7 @@ async function getStatusOpen() {
3200 };3224 };
32013225
3202 if (oai_settings.reverse_proxy && (oai_settings.chat_completion_source === chat_completion_sources.OPENAI || oai_settings.chat_completion_source === chat_completion_sources.CLAUDE)) {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 validateReverseProxy();3227 await validateReverseProxy();
3204 }3228 }
32053229
3206 if (oai_settings.chat_completion_source === chat_completion_sources.CUSTOM) {3230 if (oai_settings.chat_completion_source === chat_completion_sources.CUSTOM) {
@@ -3495,6 +3519,26 @@ async function onPresetImportFileChange(e) {
3495 return;3519 return;
3496 }3520 }
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
3498 if (name in openai_setting_names) {3542 if (name in openai_setting_names) {
3499 const confirm = await callPopup('Preset name already exists. Overwrite?', 'confirm');3543 const confirm = await callPopup('Preset name already exists. Overwrite?', 'confirm');
35003544
@@ -3541,8 +3585,22 @@ async function onExportPresetClick() {
35413585
3542 const preset = structuredClone(openai_settings[openai_setting_names[oai_settings.preset_settings_openai]]);3586 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
3547 const presetJsonString = JSON.stringify(preset, null, 4);3605 const presetJsonString = JSON.stringify(preset, null, 4);
3548 const presetFileName = `${oai_settings.preset_settings_openai}.json`;3606 const presetFileName = `${oai_settings.preset_settings_openai}.json`;