diff --git a/config.conf b/config.conf index ee25c3a8d..452636cca 100644 --- a/config.conf +++ b/config.conf @@ -8,7 +8,17 @@ const disableThumbnails = false; //Disables the generation of thumbnails, opting const autorun = true; //Autorun in the browser. true/false const enableExtensions = true; //Enables support for TavernAI-extras project const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine. +const allowKeysExposure = false; // If true, private API keys could be fetched to the frontend. module.exports = { - port, whitelist, whitelistMode, basicAuthMode, basicAuthUser, autorun, enableExtensions, listen, disableThumbnails + port, + whitelist, + whitelistMode, + basicAuthMode, + basicAuthUser, + autorun, + enableExtensions, + listen, + disableThumbnails, + allowKeysExposure, }; diff --git a/public/index.html b/public/index.html index f148be6c9..1539f15a9 100644 --- a/public/index.html +++ b/public/index.html @@ -1009,7 +1009,7 @@ Enter 0000000000 to use anonymous mode. -
Your API key will removed from here after you reload the page for privacy reasons.
+
For privacy reasons, your API key will hidden after you reload the page.

Model
@@ -1040,7 +1040,7 @@ -
Your API key will removed from here after you click "Connect" for privacy reasons.
+
For privacy reasons, your API key will hidden after you reload the page.

Novel AI Model @@ -1094,7 +1094,7 @@ -
Your API key will removed from here after you click "Connect" for privacy reasons.
+
For privacy reasons, your API key will hidden after you reload the page.
@@ -1131,7 +1131,7 @@
-
Your API key will removed from here after you click "Connect" for privacy reasons.
+
For privacy reasons, your API key will hidden after you reload the page.
@@ -1153,9 +1153,12 @@

- +
+ + View hidden API keys +
diff --git a/public/scripts/secrets.js b/public/scripts/secrets.js index 0998ceadc..6b0c615fd 100644 --- a/public/scripts/secrets.js +++ b/public/scripts/secrets.js @@ -1,4 +1,4 @@ -import { getRequestHeaders } from "../script.js"; +import { callPopup, getRequestHeaders } from "../script.js"; export const SECRET_KEYS = { HORDE: 'api_key_horde', @@ -19,14 +19,37 @@ function updateSecretDisplay() { const validSecret = !!secret_state[secret_key]; const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key'; $(input_selector).attr('placeholder', placeholder); - - // Horde doesn't have a connect button - if (secret_key !== SECRET_KEYS.HORDE) { - $(input_selector).val(''); - } } } +async function viewSecrets() { + const response = await fetch('/viewsecrets', { + method: 'POST', + headers: getRequestHeaders(), + }); + + if (response.status == 403) { + callPopup('

Forbidden

To view your API keys here, set the value of allowKeysExposure to true in config.conf file and restart the SillyTavern server.

', 'text'); + return; + } + + if (!response.ok) { + return; + } + + $('#dialogue_popup').addClass('wide_dialogue_popup'); + const data = await response.json(); + const table = document.createElement('table'); + table.classList.add('responsiveTable'); + $(table).append('KeyValue'); + + for (const [key,value] of Object.entries(data)) { + $(table).append(`${DOMPurify.sanitize(key)}${DOMPurify.sanitize(value)}`); + } + + callPopup(table.outerHTML, 'text'); +} + export let secret_state = {}; export async function writeSecret(key, value) { @@ -64,4 +87,8 @@ export async function readSecretState() { } catch { console.error('Could not read secrets file'); } -} \ No newline at end of file +} + +jQuery(() => { + $('#viewSecrets').on('click', viewSecrets); +}); \ No newline at end of file diff --git a/public/style.css b/public/style.css index 017c4ded7..0d415f11d 100644 --- a/public/style.css +++ b/public/style.css @@ -109,6 +109,24 @@ body { background-clip: content-box; } +table.responsiveTable { + width: 100%; + margin: 10px 0; +} + +.responsiveTable tr { + display: flex; +} + +.responsiveTable, +.responsiveTable th, +.responsiveTable td { + flex: 1; + border: 1px solid; + border-collapse: collapse; + word-break: break-all; + padding: 5px; +} .sysHR { border-top: 2px solid grey; diff --git a/readme.md b/readme.md index 1fbfae648..5ac168377 100644 --- a/readme.md +++ b/readme.md @@ -125,6 +125,17 @@ Get in touch with the developers directly: 1. Run the `start.sh` script. 2. Enjoy. +## API keys management + +SillyTavern saves your API keys to a `secrets.json` file in the server directory. + +By default they will not be exposed to a frontend after you enter them and reload the page. + +In order to enable viewing your keys by clicking a button in the API block: + +1. Set the value of `allowKeysExposure` to `true` in `config.conf` file. +2. Restart the SillyTavern server. + ## Remote connections Most often this is for people who want to use SillyTavern on their mobile phones while at home. diff --git a/server.js b/server.js index 401f67d36..7aee9bb00 100644 --- a/server.js +++ b/server.js @@ -77,6 +77,7 @@ const whitelistMode = config.whitelistMode; const autorun = config.autorun && !cliArguments.ssl; const enableExtensions = config.enableExtensions; const listen = config.listen; +const allowKeysExposure = config.allowKeysExposure; const axios = require('axios'); const tiktoken = require('@dqbd/tiktoken'); @@ -2894,6 +2895,27 @@ app.post('/generate_horde', jsonParser, async (request, response) => { } }); +app.post('/viewsecrets', jsonParser, async (_, response) => { + if (!allowKeysExposure) { + console.error('secrets.json could not be viewed unless the value of allowKeysExposure in config.conf is set to true'); + return response.sendStatus(403); + } + + if (!fs.existsSync(SECRETS_FILE)) { + console.error('secrets.json does not exist'); + return response.sendStatus(404); + } + + try { + const fileContents = fs.readFileSync(SECRETS_FILE); + const secrets = JSON.parse(fileContents); + return response.send(secrets); + } catch (error) { + console.error(error); + return response.sendStatus(500); + } +}); + function writeSecret(key, value) { if (!fs.existsSync(SECRETS_FILE)) { const emptyFile = JSON.stringify({});