Refactor API key management in extensions

579ef1e86c3609dcf3d5c998330ecc772e304129

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

5 files changed, +48 -93Showing whitespace changes
public/scripts/extensions/translate/index.js+2 -2
@@ -768,7 +768,7 @@ jQuery(async () => {
768768 return;
769769 }
770770
771771 await writeSecret(extension_settings.translate.provider, String(key));
772772 toastr.success('API Key saved');
773773 $('#translate_key_button').addClass('success');
774774 });
@@ -802,7 +802,7 @@ jQuery(async () => {
802802 return;
803803 }
804804
805805 await writeSecret(secretKey, String(url));
806806
807807 toastr.success('API URL saved');
808808 $('#translate_url_button').addClass('success');
public/scripts/extensions/tts/azure.js+19 -31
@@ -1,6 +1,5 @@
11import { event_types, eventSource, getRequestHeaders } from '../../../script.js';
22import { POPUP_RESULT, POPUP_TYPESECRET_KEYS, callGenericPopupsecret_state } from '../../popupsecrets.js';
3-import { SECRET_KEYS, findSecret, secret_state, writeSecret } from '../../secrets.js';
43import { getPreviewString, saveTtsProviderSettings } from './index.js';
54export { AzureTtsProvider };
65
@@ -26,7 +25,7 @@ class AzureTtsProvider {
2625 <h4 for="azure_tts_key" class="flex1 margin0">
2726 <a href="https://portal.azure.com/" target="_blank">Azure TTS Key</a>
2827 </h4>
2928 <div id="azure_tts_key" class="menu_button menu_button_icon manage-api-keys" data-key="api_key_azure_tts">
3029 <i class="fa-solid fa-key"></i>
3130 <span>Click to set</span>
3231 </div>
@@ -39,6 +38,20 @@ class AzureTtsProvider {
3938 return html;
4039 }
4140
41+ constructor() {
42+ this.handler = async function (/** @type {string} */ key) {
43+ if (key !== SECRET_KEYS.AZURE_TTS) return;
44+ $('#azure_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.AZURE_TTS]);
45+ await this.onRefreshClick();
46+ }.bind(this);
47+ }
48+
49+ dispose() {
50+ [event_types.SECRET_WRITTEN, event_types.SECRET_DELETED, event_types.SECRET_ROTATED].forEach(event => {
51+ eventSource.removeListener(event, this.handler);
52+ });
53+ }
54+
4255 onSettingsChange() {
4356 // Update dynamically
4457 this.settings.region = String($('#azure_tts_region').val());
@@ -65,34 +78,9 @@ class AzureTtsProvider {
6578 }
6679
6780 $('#azure_tts_region').val(this.settings.region).on('input', () => this.onSettingsChange());
68- $('#azure_tts_key').toggleClass('success', secret_state[SECRET_KEYS.AZURE_TTS]);
69- $('#azure_tts_key').on('click', async () => {
70- const popupText = 'Azure TTS API Key';
71- const savedKey = secret_state[SECRET_KEYS.AZURE_TTS] ? await findSecret(SECRET_KEYS.AZURE_TTS) : '';
72-
73- const key = await callGenericPopup(popupText, POPUP_TYPE.INPUT, savedKey, {
74- customButtons: [{
75- text: 'Remove Key',
76- appendAtEnd: true,
77- result: POPUP_RESULT.NEGATIVE,
78- action: async () => {
79- await writeSecret(SECRET_KEYS.AZURE_TTS, '');
8081 $('#azure_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.AZURE_TTS]);
81- toastr.success('API Key removed');
82+ [event_types.SECRET_WRITTEN, event_types.SECRET_DELETED, event_types.SECRET_ROTATED].forEach(event => {
82- await this.onRefreshClick();
83+ eventSource.on(event, this.handler);
83- },
84- }],
85- });
86-
87- if (!key) {
88- return;
89- }
90-
91- await writeSecret(SECRET_KEYS.AZURE_TTS, String(key));
92-
93- toastr.success('API Key saved');
94- $('#azure_tts_key').addClass('success');
95- await this.onRefreshClick();
9684 });
9785
9886 try {
public/scripts/extensions/tts/openai-compatible.js+19 -31
@@ -1,6 +1,5 @@
11import { event_types, eventSource, getRequestHeaders } from '../../../script.js';
22import { callGenericPopup, POPUP_RESULTSECRET_KEYS, POPUP_TYPEsecret_state } from '../../popupsecrets.js';
3-import { findSecret, SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';
43import { getPreviewString, saveTtsProviderSettings } from './index.js';
54
65export { OpenAICompatibleTtsProvider };
@@ -27,7 +26,7 @@ class OpenAICompatibleTtsProvider {
2726 <div class="flex1">
2827 <input id="openai_compatible_tts_endpoint" type="text" class="text_pole" maxlength="500" value="${this.defaultSettings.provider_endpoint}"/>
2928 </div>
3029 <div id="openai_compatible_tts_key" class="menu_button menu_button_icon manage-api-keys" data-key="api_key_custom_openai_tts">
3130 <i class="fa-solid fa-key"></i>
3231 <span>API Key</span>
3332 </div>
@@ -41,6 +40,20 @@ class OpenAICompatibleTtsProvider {
4140 return html;
4241 }
4342
43+ constructor() {
44+ this.handler = async function (/** @type {string} */ key) {
45+ if (key !== SECRET_KEYS.CUSTOM_OPENAI_TTS) return;
46+ $('#openai_compatible_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.CUSTOM_OPENAI_TTS]);
47+ await this.onRefreshClick();
48+ }.bind(this);
49+ }
50+
51+ dispose() {
52+ [event_types.SECRET_WRITTEN, event_types.SECRET_DELETED, event_types.SECRET_ROTATED].forEach(event => {
53+ eventSource.removeListener(event, this.handler);
54+ });
55+ }
56+
4457 async loadSettings(settings) {
4558 // Populate Provider UI given input settings
4659 if (Object.keys(settings).length == 0) {
@@ -74,34 +87,9 @@ class OpenAICompatibleTtsProvider {
7487
7588 $('#openai_compatible_tts_speed_output').text(this.settings.speed);
7689
77- $('#openai_compatible_tts_key').toggleClass('success', secret_state[SECRET_KEYS.CUSTOM_OPENAI_TTS]);
78- $('#openai_compatible_tts_key').on('click', async () => {
79- const popupText = 'OpenAI-compatible TTS API Key';
80- const savedKey = secret_state[SECRET_KEYS.CUSTOM_OPENAI_TTS] ? await findSecret(SECRET_KEYS.CUSTOM_OPENAI_TTS) : '';
81-
82- const key = await callGenericPopup(popupText, POPUP_TYPE.INPUT, savedKey, {
83- customButtons: [{
84- text: 'Remove Key',
85- appendAtEnd: true,
86- result: POPUP_RESULT.NEGATIVE,
87- action: async () => {
88- await writeSecret(SECRET_KEYS.CUSTOM_OPENAI_TTS, '');
8990 $('#openai_compatible_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.CUSTOM_OPENAI_TTS]);
90- toastr.success('API Key removed');
91+ [event_types.SECRET_WRITTEN, event_types.SECRET_DELETED, event_types.SECRET_ROTATED].forEach(event => {
91- await this.onRefreshClick();
92+ eventSource.on(event, this.handler);
92- },
93- }],
94- });
95-
96- if (!key) {
97- return;
98- }
99-
100- await writeSecret(SECRET_KEYS.CUSTOM_OPENAI_TTS, String(key));
101-
102- toastr.success('API Key saved');
103- $('#openai_compatible_tts_key').toggleClass('success', secret_state[SECRET_KEYS.CUSTOM_OPENAI_TTS]);
104- await this.onRefreshClick();
10593 });
10694
10795 await this.checkReady();
public/scripts/extensions/vectors/index.js+7 -28
@@ -22,7 +22,7 @@ import {
2222 openThirdPartyExtensionMenu,
2323} from '../../extensions.js';
2424import { collapseNewlines, registerDebugFunction } from '../../power-user.js';
2525import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';
2626import { getDataBankAttachments, getDataBankAttachmentsForSource, getFileAttachment } from '../../chats.js';
2727import { debounce, getStringHash as calculateHash, waitUntilCondition, onlyUnique, splitRecursive, trimToStartSentence, trimToEndSentence, escapeHtml } from '../../utils.js';
2828import { debounce_timeout } from '../../constants.js';
@@ -33,7 +33,6 @@ import { SlashCommand } from '../../slash-commands/SlashCommand.js';
3333import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
3434import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
3535import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js';
36-import { callGenericPopup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';
3736import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js';
3837import { WebLlmVectorProvider } from './webllm.js';
3938import { removeReasoningFromString } from '../../reasoning.js';
@@ -1494,32 +1493,6 @@ jQuery(async () => {
14941493 Object.assign(extension_settings.vectors, settings);
14951494 saveSettingsDebounced();
14961495 });
1497- $('#api_key_nomicai').on('click', async () => {
1498- const popupText = 'NomicAI API Key:';
1499- const key = await callGenericPopup(popupText, POPUP_TYPE.INPUT, '', {
1500- customButtons: [{
1501- text: 'Remove Key',
1502- appendAtEnd: true,
1503- result: POPUP_RESULT.NEGATIVE,
1504- action: async () => {
1505- await writeSecret(SECRET_KEYS.NOMICAI, '');
1506- toastr.success('API Key removed');
1507- $('#api_key_nomicai').toggleClass('success', !!secret_state[SECRET_KEYS.NOMICAI]);
1508- saveSettingsDebounced();
1509- },
1510- }],
1511- });
1512-
1513- if (!key) {
1514- return;
1515- }
1516-
1517- await writeSecret(SECRET_KEYS.NOMICAI, String(key));
1518- $('#api_key_nomicai').toggleClass('success', !!secret_state[SECRET_KEYS.NOMICAI]);
1519-
1520- toastr.success('API Key saved');
1521- saveSettingsDebounced();
1522- });
15231496 $('#vectors_togetherai_model').val(settings.togetherai_model).on('change', () => {
15241497 settings.togetherai_model = String($('#vectors_togetherai_model').val());
15251498 Object.assign(extension_settings.vectors, settings);
@@ -1777,6 +1750,12 @@ jQuery(async () => {
17771750 });
17781751
17791752 $('#api_key_nomicai').toggleClass('success', !!secret_state[SECRET_KEYS.NOMICAI]);
1753+ [event_types.SECRET_WRITTEN, event_types.SECRET_DELETED, event_types.SECRET_ROTATED].forEach(event => {
1754+ eventSource.on(event, (/** @type {string} */ key)=> {
1755+ if (key !== SECRET_KEYS.NOMICAI) return;
1756+ $('#api_key_nomicai').toggleClass('success', !!secret_state[SECRET_KEYS.NOMICAI]);
1757+ });
1758+ });
17801759
17811760 toggleSettings();
17821761 eventSource.on(event_types.MESSAGE_DELETED, onChatEvent);
public/scripts/extensions/vectors/settings.html+1 -1
@@ -145,7 +145,7 @@
145145 <label for="api_key_nomicai" class="flex1">
146146 <span data-i18n="NomicAI API Key">NomicAI API Key</span>
147147 </label>
148148 <div id="api_key_nomicai" class="menu_button menu_button_icon manage-api-keys" data-key="api_key_nomicai">
149149 <i class="fa-solid fa-key"></i>
150150 <span data-i18n="Click to set">Click to set</span>
151151 </div>