#284 Add a button to expose private keys

This commit is contained in:
SillyLossy
2023-05-12 20:20:06 +03:00
parent d90b5ded45
commit 69feecd0fa
6 changed files with 106 additions and 15 deletions

View File

@ -8,7 +8,17 @@ const disableThumbnails = false; //Disables the generation of thumbnails, opting
const autorun = true; //Autorun in the browser. true/false const autorun = true; //Autorun in the browser. true/false
const enableExtensions = true; //Enables support for TavernAI-extras project 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 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 = { module.exports = {
port, whitelist, whitelistMode, basicAuthMode, basicAuthUser, autorun, enableExtensions, listen, disableThumbnails port,
whitelist,
whitelistMode,
basicAuthMode,
basicAuthUser,
autorun,
enableExtensions,
listen,
disableThumbnails,
allowKeysExposure,
}; };

View File

@ -1009,7 +1009,7 @@
Enter <span class="monospace">0000000000</span> to use anonymous mode. Enter <span class="monospace">0000000000</span> to use anonymous mode.
</h5> </h5>
<input id="horde_api_key" name="horde_api_key" class="text_pole" maxlength="500" type="text" placeholder="0000000000"> <input id="horde_api_key" name="horde_api_key" class="text_pole" maxlength="500" type="text" placeholder="0000000000">
<div class="neutral_warning">Your API key will removed from here after you reload the page for privacy reasons.</div> <div class="neutral_warning">For privacy reasons, your API key will hidden after you reload the page.</div>
<h4 class="horde_model_title"> <h4 class="horde_model_title">
Model Model
<div id="horde_refresh" title="Refresh models" class="right_menu_button"> <div id="horde_refresh" title="Refresh models" class="right_menu_button">
@ -1040,7 +1040,7 @@
</ol> </ol>
</span> </span>
<input id="api_key_novel" name="api_key_novel" class="text_pole" maxlength="500" size="35" type="text"> <input id="api_key_novel" name="api_key_novel" class="text_pole" maxlength="500" size="35" type="text">
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div> <div class="neutral_warning">For privacy reasons, your API key will hidden after you reload the page.</div>
<input id="api_button_novel" class="menu_button" type="submit" value="Connect"> <input id="api_button_novel" class="menu_button" type="submit" value="Connect">
<div id="api_loading_novel" class="api-load-icon fa-solid fa-hourglass fa-spin"></div> <div id="api_loading_novel" class="api-load-icon fa-solid fa-hourglass fa-spin"></div>
<h4>Novel AI Model <h4>Novel AI Model
@ -1094,7 +1094,7 @@
</ol> </ol>
</span> </span>
<input id="api_key_openai" name="api_key_openai" class="text_pole" maxlength="500" value="" type="text"> <input id="api_key_openai" name="api_key_openai" class="text_pole" maxlength="500" value="" type="text">
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div> <div class="neutral_warning">For privacy reasons, your API key will hidden after you reload the page.</div>
<input id="api_button_openai" class="menu_button" type="submit" value="Connect"> <input id="api_button_openai" class="menu_button" type="submit" value="Connect">
<div id="api_loading_openai" class=" api-load-icon fa-solid fa-hourglass fa-spin"></div> <div id="api_loading_openai" class=" api-load-icon fa-solid fa-hourglass fa-spin"></div>
</form> </form>
@ -1131,7 +1131,7 @@
</span> </span>
<div class="widthFreeExpand"> <div class="widthFreeExpand">
<input id="poe_token" class="text_pole" type="text" maxlength="100" /> <input id="poe_token" class="text_pole" type="text" maxlength="100" />
<div class="neutral_warning">Your API key will removed from here after you click "Connect" for privacy reasons.</div> <div class="neutral_warning">For privacy reasons, your API key will hidden after you reload the page.</div>
</div> </div>
<input id="poe_connect" class="menu_button" type="button" value="Connect" /> <input id="poe_connect" class="menu_button" type="button" value="Connect" />
@ -1153,9 +1153,12 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flex-container alignitemscenter spaceBetween wide100p">
<label for="auto-connect-checkbox" class="checkbox_label"><input id="auto-connect-checkbox" type="checkbox" /> <label for="auto-connect-checkbox" class="checkbox_label"><input id="auto-connect-checkbox" type="checkbox" />
Auto-connect to Last Server Auto-connect to Last Server
</label> </label>
<a id="viewSecrets" href="javascript:void(0);">View hidden API keys</a>
</div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import { getRequestHeaders } from "../script.js"; import { callPopup, getRequestHeaders } from "../script.js";
export const SECRET_KEYS = { export const SECRET_KEYS = {
HORDE: 'api_key_horde', HORDE: 'api_key_horde',
@ -19,12 +19,35 @@ function updateSecretDisplay() {
const validSecret = !!secret_state[secret_key]; const validSecret = !!secret_state[secret_key];
const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key'; const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
$(input_selector).attr('placeholder', placeholder); $(input_selector).attr('placeholder', placeholder);
}
}
// Horde doesn't have a connect button async function viewSecrets() {
if (secret_key !== SECRET_KEYS.HORDE) { const response = await fetch('/viewsecrets', {
$(input_selector).val(''); method: 'POST',
headers: getRequestHeaders(),
});
if (response.status == 403) {
callPopup('<h3>Forbidden</h3><p>To view your API keys here, set the value of allowKeysExposure to true in config.conf file and restart the SillyTavern server.</p>', '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('<thead><th>Key</th><th>Value</th></thead>');
for (const [key,value] of Object.entries(data)) {
$(table).append(`<tr><td>${DOMPurify.sanitize(key)}</td><td>${DOMPurify.sanitize(value)}</td></tr>`);
}
callPopup(table.outerHTML, 'text');
} }
export let secret_state = {}; export let secret_state = {};
@ -65,3 +88,7 @@ export async function readSecretState() {
console.error('Could not read secrets file'); console.error('Could not read secrets file');
} }
} }
jQuery(() => {
$('#viewSecrets').on('click', viewSecrets);
});

View File

@ -109,6 +109,24 @@ body {
background-clip: content-box; 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 { .sysHR {
border-top: 2px solid grey; border-top: 2px solid grey;

View File

@ -125,6 +125,17 @@ Get in touch with the developers directly:
1. Run the `start.sh` script. 1. Run the `start.sh` script.
2. Enjoy. 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 ## Remote connections
Most often this is for people who want to use SillyTavern on their mobile phones while at home. Most often this is for people who want to use SillyTavern on their mobile phones while at home.

View File

@ -77,6 +77,7 @@ const whitelistMode = config.whitelistMode;
const autorun = config.autorun && !cliArguments.ssl; const autorun = config.autorun && !cliArguments.ssl;
const enableExtensions = config.enableExtensions; const enableExtensions = config.enableExtensions;
const listen = config.listen; const listen = config.listen;
const allowKeysExposure = config.allowKeysExposure;
const axios = require('axios'); const axios = require('axios');
const tiktoken = require('@dqbd/tiktoken'); 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) { function writeSecret(key, value) {
if (!fs.existsSync(SECRETS_FILE)) { if (!fs.existsSync(SECRETS_FILE)) {
const emptyFile = JSON.stringify({}); const emptyFile = JSON.stringify({});