mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Initial commit
This commit is contained in:
158
public/scripts/secrets.js
Normal file
158
public/scripts/secrets.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import { callPopup, getRequestHeaders } from "../script.js";
|
||||
|
||||
export const SECRET_KEYS = {
|
||||
HORDE: 'api_key_horde',
|
||||
OPENAI: 'api_key_openai',
|
||||
NOVEL: 'api_key_novel',
|
||||
CLAUDE: 'api_key_claude',
|
||||
OPENROUTER: 'api_key_openrouter',
|
||||
SCALE: 'api_key_scale',
|
||||
}
|
||||
|
||||
const INPUT_MAP = {
|
||||
[SECRET_KEYS.HORDE]: '#horde_api_key',
|
||||
[SECRET_KEYS.OPENAI]: '#api_key_openai',
|
||||
[SECRET_KEYS.NOVEL]: '#api_key_novel',
|
||||
[SECRET_KEYS.CLAUDE]: '#api_key_claude',
|
||||
[SECRET_KEYS.OPENROUTER]: '#api_key_openrouter',
|
||||
[SECRET_KEYS.SCALE]: '#api_key_scale',
|
||||
}
|
||||
|
||||
async function clearSecret() {
|
||||
const key = $(this).data('key');
|
||||
await writeSecret(key, '');
|
||||
secret_state[key] = false;
|
||||
updateSecretDisplay();
|
||||
$(INPUT_MAP[key]).val('');
|
||||
$('#main_api').trigger('change');
|
||||
}
|
||||
|
||||
function updateSecretDisplay() {
|
||||
for (const [secret_key, input_selector] of Object.entries(INPUT_MAP)) {
|
||||
const validSecret = !!secret_state[secret_key];
|
||||
const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
|
||||
$(input_selector).attr('placeholder', placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
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 async function writeSecret(key, value) {
|
||||
try {
|
||||
const response = await fetch('/writesecret', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ key, value }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const text = await response.text();
|
||||
|
||||
if (text == 'ok') {
|
||||
secret_state[key] = true;
|
||||
updateSecretDisplay();
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.error('Could not write secret value: ', key);
|
||||
}
|
||||
}
|
||||
|
||||
export async function readSecretState() {
|
||||
try {
|
||||
const response = await fetch('/readsecretstate', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
secret_state = await response.json();
|
||||
updateSecretDisplay();
|
||||
await checkOpenRouterAuth();
|
||||
}
|
||||
} catch {
|
||||
console.error('Could not read secrets file');
|
||||
}
|
||||
}
|
||||
|
||||
function authorizeOpenRouter() {
|
||||
const openRouterUrl = `https://openrouter.ai/auth?callback_url=${encodeURIComponent(location.origin)}`;
|
||||
location.href = openRouterUrl;
|
||||
}
|
||||
|
||||
async function checkOpenRouterAuth() {
|
||||
const params = new URLSearchParams(location.search);
|
||||
if (params.has('code')) {
|
||||
const code = params.get('code');
|
||||
try {
|
||||
const response = await fetch("https://openrouter.ai/api/v1/auth/keys", {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ code }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('OpenRouter exchange error');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!data || !data.key) {
|
||||
throw new Error('OpenRouter invalid response');
|
||||
}
|
||||
|
||||
await writeSecret(SECRET_KEYS.OPENROUTER, data.key);
|
||||
|
||||
if (secret_state[SECRET_KEYS.OPENROUTER]) {
|
||||
toastr.success('OpenRouter token saved');
|
||||
// Remove the code from the URL
|
||||
const currentUrl = window.location.href;
|
||||
const urlWithoutSearchParams = currentUrl.split("?")[0];
|
||||
window.history.pushState({}, "", urlWithoutSearchParams);
|
||||
} else {
|
||||
throw new Error('OpenRouter token not saved');
|
||||
}
|
||||
} catch (err) {
|
||||
toastr.error('Could not verify OpenRouter token. Please try again.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(async () => {
|
||||
$('#viewSecrets').on('click', viewSecrets);
|
||||
$(document).on('click', '.clear-api-key', clearSecret);
|
||||
$(document).on('input', Object.values(INPUT_MAP).join(','), function () {
|
||||
const id = $(this).attr('id');
|
||||
const value = $(this).val();
|
||||
const warningElement = $(`[data-for="${id}"]`);
|
||||
warningElement.toggle(value.length > 0);
|
||||
});
|
||||
$('#openrouter_authorize').on('click', authorizeOpenRouter);
|
||||
});
|
Reference in New Issue
Block a user