#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 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,
};

View File

@ -1009,7 +1009,7 @@
Enter <span class="monospace">0000000000</span> to use anonymous mode.
</h5>
<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">
Model
<div id="horde_refresh" title="Refresh models" class="right_menu_button">
@ -1040,7 +1040,7 @@
</ol>
</span>
<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">
<div id="api_loading_novel" class="api-load-icon fa-solid fa-hourglass fa-spin"></div>
<h4>Novel AI Model
@ -1094,7 +1094,7 @@
</ol>
</span>
<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">
<div id="api_loading_openai" class=" api-load-icon fa-solid fa-hourglass fa-spin"></div>
</form>
@ -1131,7 +1131,7 @@
</span>
<div class="widthFreeExpand">
<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>
<input id="poe_connect" class="menu_button" type="button" value="Connect" />
@ -1153,9 +1153,12 @@
</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" />
Auto-connect to Last Server
</label>
<a id="viewSecrets" href="javascript:void(0);">View hidden API keys</a>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { getRequestHeaders } from "../script.js";
import { callPopup, getRequestHeaders } from "../script.js";
export const SECRET_KEYS = {
HORDE: 'api_key_horde',
@ -19,12 +19,35 @@ 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('<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 = {};
@ -65,3 +88,7 @@ export async function readSecretState() {
console.error('Could not read secrets file');
}
}
jQuery(() => {
$('#viewSecrets').on('click', viewSecrets);
});

View File

@ -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;

View File

@ -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.

View File

@ -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({});