mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Add discreet login mode
This commit is contained in:
@ -22,6 +22,8 @@ basicAuthUser:
|
|||||||
enableCorsProxy: false
|
enableCorsProxy: false
|
||||||
# Enable multi-user mode
|
# Enable multi-user mode
|
||||||
enableUserAccounts: false
|
enableUserAccounts: false
|
||||||
|
# Enable discreet login mode: hides user list on the login screen
|
||||||
|
enableDiscreetLogin: false
|
||||||
# Used to sign session cookies. Will be auto-generated if not set
|
# Used to sign session cookies. Will be auto-generated if not set
|
||||||
cookieSecret: ''
|
cookieSecret: ''
|
||||||
# Disable security checks - NOT RECOMMENDED
|
# Disable security checks - NOT RECOMMENDED
|
||||||
|
@ -15,5 +15,6 @@
|
|||||||
"**/node_modules/*",
|
"**/node_modules/*",
|
||||||
"public/lib",
|
"public/lib",
|
||||||
"backups/*",
|
"backups/*",
|
||||||
|
"data/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,20 @@
|
|||||||
<img src="img/logo.png" alt="SillyTavern" class="logo">
|
<img src="img/logo.png" alt="SillyTavern" class="logo">
|
||||||
<span>Welcome to SillyTavern</span>
|
<span>Welcome to SillyTavern</span>
|
||||||
</h2>
|
</h2>
|
||||||
<h3>Select a User</h3>
|
<h3 id="normalLoginPrompt">
|
||||||
|
Select a User
|
||||||
|
</h3>
|
||||||
|
<h3 id="discreetLoginPrompt">
|
||||||
|
Enter Login Details
|
||||||
|
</h3>
|
||||||
<div id="userListBlock" class="wide100p">
|
<div id="userListBlock" class="wide100p">
|
||||||
<div id="userList" class="flex-container justifyCenter"></div>
|
<div id="userList" class="flex-container justifyCenter"></div>
|
||||||
|
<div id="handleEntryBlock" style="display:none;" class="flex-container flexFlowColumn alignItemsCenter">
|
||||||
|
<input id="userHandle" class="text_pole" type="text" placeholder="User handle" autocomplete="username">
|
||||||
|
</div>
|
||||||
<div id="passwordEntryBlock" style="display:none;"
|
<div id="passwordEntryBlock" style="display:none;"
|
||||||
class="flex-container flexFlowColumn alignItemsCenter">
|
class="flex-container flexFlowColumn alignItemsCenter">
|
||||||
<input id="userPassword" class="text_pole" type="password" placeholder="Enter a password...">
|
<input id="userPassword" class="text_pole" type="password" placeholder="Password" autocomplete="current-password">
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div id="loginButton" class="menu_button">Login</div>
|
<div id="loginButton" class="menu_button">Login</div>
|
||||||
<div id="recoverPassword" class="menu_button">Recover</div>
|
<div id="recoverPassword" class="menu_button">Recover</div>
|
||||||
@ -55,8 +63,8 @@
|
|||||||
Recovery code has been posted to the server console.
|
Recovery code has been posted to the server console.
|
||||||
</div>
|
</div>
|
||||||
<input id="recoveryCode" class="text_pole" type="text" placeholder="Recovery code">
|
<input id="recoveryCode" class="text_pole" type="text" placeholder="Recovery code">
|
||||||
<input id="newPassword" class="text_pole" type="password" placeholder="New password...">
|
<input id="newPassword" class="text_pole" type="password" placeholder="New password" autocomplete="new-password">
|
||||||
<div class="flex-container">
|
<div class="flex-container flexGap10">
|
||||||
<div id="sendRecovery" class="menu_button">Send</div>
|
<div id="sendRecovery" class="menu_button">Send</div>
|
||||||
<div id="cancelRecovery" class="menu_button">Cancel</div>
|
<div id="cancelRecovery" class="menu_button">Cancel</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* CRSF token for requests.
|
* CRSF token for requests.
|
||||||
*/
|
*/
|
||||||
let csrfToken = '';
|
let csrfToken = '';
|
||||||
|
let discreetLogin = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a CSRF token from the server.
|
* Gets a CSRF token from the server.
|
||||||
@ -25,6 +26,17 @@ async function getUserList() {
|
|||||||
'X-CSRF-Token': csrfToken,
|
'X-CSRF-Token': csrfToken,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json();
|
||||||
|
return displayError(errorData.error || 'An error occurred');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 204) {
|
||||||
|
discreetLogin = true;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const userListObj = await response.json();
|
const userListObj = await response.json();
|
||||||
console.log(userListObj);
|
console.log(userListObj);
|
||||||
return userListObj;
|
return userListObj;
|
||||||
@ -188,9 +200,15 @@ function onCancelRecoveryClick() {
|
|||||||
displayError('');
|
displayError('');
|
||||||
}
|
}
|
||||||
|
|
||||||
(async function () {
|
/**
|
||||||
csrfToken = await getCsrfToken();
|
* Configures the login page for normal login.
|
||||||
const userList = await getUserList();
|
* @param {import('../../src/users').UserViewModel[]} userList List of users
|
||||||
|
*/
|
||||||
|
function configureNormalLogin(userList) {
|
||||||
|
console.log('Discreet login is disabled');
|
||||||
|
$('#handleEntryBlock').hide();
|
||||||
|
$('#normalLoginPrompt').show();
|
||||||
|
$('#discreetLoginPrompt').hide();
|
||||||
console.log(userList);
|
console.log(userList);
|
||||||
for (const user of userList) {
|
for (const user of userList) {
|
||||||
const userBlock = $('<div></div>').addClass('userSelect');
|
const userBlock = $('<div></div>').addClass('userSelect');
|
||||||
@ -202,6 +220,47 @@ function onCancelRecoveryClick() {
|
|||||||
userBlock.on('click', () => onUserSelected(user));
|
userBlock.on('click', () => onUserSelected(user));
|
||||||
$('#userList').append(userBlock);
|
$('#userList').append(userBlock);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the login page for discreet login.
|
||||||
|
*/
|
||||||
|
function configureDiscreetLogin() {
|
||||||
|
console.log('Discreet login is enabled');
|
||||||
|
$('#handleEntryBlock').show();
|
||||||
|
$('#normalLoginPrompt').hide();
|
||||||
|
$('#discreetLoginPrompt').show();
|
||||||
|
$('#userList').hide();
|
||||||
|
$('#passwordRecoveryBlock').hide();
|
||||||
|
$('#passwordEntryBlock').show();
|
||||||
|
$('#loginButton').off('click').on('click', async () => {
|
||||||
|
const handle = String($('#userHandle').val());
|
||||||
|
const password = String($('#userPassword').val());
|
||||||
|
await performLogin(handle, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#recoverPassword').off('click').on('click', async () => {
|
||||||
|
const handle = String($('#userHandle').val());
|
||||||
|
await sendRecoveryPart1(handle);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#sendRecovery').off('click').on('click', async () => {
|
||||||
|
const handle = String($('#userHandle').val());
|
||||||
|
const code = String($('#recoveryCode').val());
|
||||||
|
const newPassword = String($('#newPassword').val());
|
||||||
|
await sendRecoveryPart2(handle, code, newPassword);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
csrfToken = await getCsrfToken();
|
||||||
|
const userList = await getUserList();
|
||||||
|
|
||||||
|
if (discreetLogin) {
|
||||||
|
configureDiscreetLogin();
|
||||||
|
} else {
|
||||||
|
configureNormalLogin(userList);
|
||||||
|
}
|
||||||
document.getElementById('shadow_popup').style.opacity = '';
|
document.getElementById('shadow_popup').style.opacity = '';
|
||||||
$('#cancelRecovery').on('click', onCancelRecoveryClick);
|
$('#cancelRecovery').on('click', onCancelRecoveryClick);
|
||||||
})();
|
})();
|
||||||
|
@ -3,9 +3,10 @@ const storage = require('node-persist');
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const { RateLimiterMemory, RateLimiterRes } = require('rate-limiter-flexible');
|
const { RateLimiterMemory, RateLimiterRes } = require('rate-limiter-flexible');
|
||||||
const { jsonParser, getIpFromRequest } = require('../express-common');
|
const { jsonParser, getIpFromRequest } = require('../express-common');
|
||||||
const { color, Cache } = require('../util');
|
const { color, Cache, getConfigValue } = require('../util');
|
||||||
const { KEY_PREFIX, getUserAvatar, toKey, getPasswordHash, getPasswordSalt } = require('../users');
|
const { KEY_PREFIX, getUserAvatar, toKey, getPasswordHash, getPasswordSalt } = require('../users');
|
||||||
|
|
||||||
|
const DISCREET_LOGIN = getConfigValue('enableDiscreetLogin', false);
|
||||||
const MFA_CACHE = new Cache(5 * 60 * 1000);
|
const MFA_CACHE = new Cache(5 * 60 * 1000);
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
@ -20,6 +21,10 @@ const recoverLimiter = new RateLimiterMemory({
|
|||||||
|
|
||||||
router.post('/list', async (_request, response) => {
|
router.post('/list', async (_request, response) => {
|
||||||
try {
|
try {
|
||||||
|
if (DISCREET_LOGIN) {
|
||||||
|
return response.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {import('../users').User[]} */
|
/** @type {import('../users').User[]} */
|
||||||
const users = await storage.values(x => x.key.startsWith(KEY_PREFIX));
|
const users = await storage.values(x => x.key.startsWith(KEY_PREFIX));
|
||||||
const viewModels = users
|
const viewModels = users
|
||||||
|
Reference in New Issue
Block a user