mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
#284 Add a button to expose private keys
This commit is contained in:
12
config.conf
12
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 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,
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
<label for="auto-connect-checkbox" class="checkbox_label"><input id="auto-connect-checkbox" type="checkbox" />
|
<div class="flex-container alignitemscenter spaceBetween wide100p">
|
||||||
Auto-connect to Last Server
|
<label for="auto-connect-checkbox" class="checkbox_label"><input id="auto-connect-checkbox" type="checkbox" />
|
||||||
</label>
|
Auto-connect to Last Server
|
||||||
|
</label>
|
||||||
|
<a id="viewSecrets" href="javascript:void(0);">View hidden API keys</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -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,14 +19,37 @@ 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
|
|
||||||
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 = {};
|
export let secret_state = {};
|
||||||
|
|
||||||
export async function writeSecret(key, value) {
|
export async function writeSecret(key, value) {
|
||||||
@ -64,4 +87,8 @@ export async function readSecretState() {
|
|||||||
} catch {
|
} catch {
|
||||||
console.error('Could not read secrets file');
|
console.error('Could not read secrets file');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jQuery(() => {
|
||||||
|
$('#viewSecrets').on('click', viewSecrets);
|
||||||
|
});
|
@ -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;
|
||||||
|
11
readme.md
11
readme.md
@ -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.
|
||||||
|
22
server.js
22
server.js
@ -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({});
|
||||||
|
Reference in New Issue
Block a user