Make Reset account functional
This commit is contained in:
parent
2e14132a20
commit
53386b35c9
|
@ -666,13 +666,15 @@ async function getSystemMessages() {
|
|||
registerPromptManagerMigration();
|
||||
|
||||
$(document).ajaxError(function myErrorHandler(_, xhr) {
|
||||
// Cohee: CSRF doesn't error out in multiple tabs anymore, so this is unnecessary
|
||||
/*
|
||||
if (xhr.status == 403) {
|
||||
toastr.warning(
|
||||
'doubleCsrf errors in console are NORMAL in this case. If you want to run ST in multiple tabs, start the server with --disableCsrf option.',
|
||||
'Looks like you\'ve opened SillyTavern in another browser tab',
|
||||
{ timeOut: 0, extendedTimeOut: 0, preventDuplicates: true },
|
||||
);
|
||||
}
|
||||
} */
|
||||
});
|
||||
|
||||
async function getClientVersion() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<form action="javascript:void(0);" class="flex-container flexFlowColumn">
|
||||
<div class="currentPasswordBlock">
|
||||
<label data-i18n="Current Password:" for="user">Current Password:</label>
|
||||
<input type="text" name="current" class="text_pole" placeholder="[ No password ]" autocomplete="current-password">
|
||||
<input type="password" name="current" class="text_pole" placeholder="[ No password ]" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="newPasswordBlock">
|
||||
<label data-i18n="New Password:" for="password">New Password:</label>
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<i class="fa-fw fa-solid fa-cog warning"></i>
|
||||
<span data-i18n="Reset Settings">Reset Settings</span>
|
||||
</div>
|
||||
<div class="userResetAllButton menu_button menu_button_icon disabled" title="Wipe all user data and reset your account to factory settings.">
|
||||
<div class="userResetAllButton menu_button menu_button_icon" title="Wipe all user data and reset your account to factory settings.">
|
||||
<i class="fa-fw fa-solid fa-skull warning"></i>
|
||||
<span data-i18n="Reset Everything">Reset Everything</span>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<form action="javascript:void(0);" class="flex-container flexFlowColumn">
|
||||
<h3 class="neutral_warning">
|
||||
This will delete all your settings and data. There will be no undo button.
|
||||
Make sure you have a backup before proceeding.
|
||||
</h3>
|
||||
<hr>
|
||||
<div>
|
||||
Account reset code has been posted to the server console.
|
||||
</div>
|
||||
<div class="currentPasswordBlock">
|
||||
<label data-i18n="Current Password:" for="user">Current Password:</label>
|
||||
<input type="password" name="password" class="text_pole" placeholder="[ No password ]" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="resetCodeBlock">
|
||||
<label data-i18n="Reset Code:" for="password">Reset Code:</label>
|
||||
<input type="text" name="code" class="text_pole" placeholder="XXXX">
|
||||
</div>
|
||||
</form>
|
|
@ -597,6 +597,64 @@ async function viewSettingsSnapshots() {
|
|||
renderSnapshots();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset everything to default.
|
||||
* @param {function} callback Success callback
|
||||
*/
|
||||
async function resetEverything(callback) {
|
||||
try {
|
||||
const step1Response = await fetch('/api/users/reset-step1', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
|
||||
if (!step1Response.ok) {
|
||||
const data = await step1Response.json();
|
||||
toastr.error(data.error || 'Unknown error', 'Failed to reset');
|
||||
throw new Error('Failed to reset everything');
|
||||
}
|
||||
|
||||
let password = '';
|
||||
let code = '';
|
||||
|
||||
const template = $(await renderTemplateAsync('userReset'));
|
||||
template.find('input[name="password"]').on('input', function () {
|
||||
password = String($(this).val());
|
||||
});
|
||||
template.find('input[name="code"]').on('input', function () {
|
||||
code = String($(this).val());
|
||||
});
|
||||
const confirm = await callGenericPopup(
|
||||
template,
|
||||
POPUP_TYPE.CONFIRM,
|
||||
'',
|
||||
{ okButton: 'Reset', cancelButton: 'Cancel', wide: false, large: false },
|
||||
);
|
||||
|
||||
if (confirm !== POPUP_RESULT.AFFIRMATIVE) {
|
||||
throw new Error('Reset everything cancelled');
|
||||
}
|
||||
|
||||
const step2Response = await fetch('/api/users/reset-step2', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ password, code }),
|
||||
});
|
||||
|
||||
if (!step2Response.ok) {
|
||||
const data = await step2Response.json();
|
||||
toastr.error(data.error || 'Unknown error', 'Failed to reset');
|
||||
throw new Error('Failed to reset everything');
|
||||
}
|
||||
|
||||
toastr.success('Everything reset successfully', 'Reset Everything');
|
||||
callback();
|
||||
} catch (error) {
|
||||
console.error('Error resetting everything:', error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function openUserProfile() {
|
||||
await getCurrentUser();
|
||||
const template = $(await renderTemplateAsync('userProfile'));
|
||||
|
@ -624,6 +682,7 @@ async function openUserProfile() {
|
|||
});
|
||||
});
|
||||
template.find('.userResetSettingsButton').on('click', () => resetSettings(currentUser.handle, () => location.reload()));
|
||||
template.find('.userResetAllButton').on('click', () => resetEverything(() => location.reload()));
|
||||
|
||||
if (!accountsEnabled) {
|
||||
template.find('[data-require-accounts]').hide();
|
||||
|
|
|
@ -2,10 +2,15 @@ const path = require('path');
|
|||
const fsPromises = require('fs').promises;
|
||||
const storage = require('node-persist');
|
||||
const express = require('express');
|
||||
const crypto = require('crypto');
|
||||
const { jsonParser } = require('../express-common');
|
||||
const { getUserAvatar, toKey, getPasswordHash, getPasswordSalt, createBackupArchive } = require('../users');
|
||||
const { getUserAvatar, toKey, getPasswordHash, getPasswordSalt, createBackupArchive, ensurePublicDirectoriesExist } = require('../users');
|
||||
const { SETTINGS_FILE } = require('../constants');
|
||||
const contentManager = require('./content-manager');
|
||||
const { color, Cache } = require('../util');
|
||||
const { checkForNewContent } = require('./content-manager');
|
||||
|
||||
const RESET_CACHE = new Cache(5 * 60 * 1000);
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
@ -27,7 +32,7 @@ router.post('/logout', async (request, response) => {
|
|||
router.get('/me', async (request, response) => {
|
||||
try {
|
||||
if (!request.user) {
|
||||
return response.sendStatus(401);
|
||||
return response.sendStatus(403);
|
||||
}
|
||||
|
||||
const user = request.user.profile;
|
||||
|
@ -74,7 +79,7 @@ router.post('/change-password', jsonParser, async (request, response) => {
|
|||
|
||||
if (!request.user.profile.admin && user.password && user.password !== getPasswordHash(request.body.oldPassword, user.salt)) {
|
||||
console.log('Change password failed: Incorrect password');
|
||||
return response.status(401).json({ error: 'Incorrect password' });
|
||||
return response.status(403).json({ error: 'Incorrect password' });
|
||||
}
|
||||
|
||||
if (request.body.newPassword) {
|
||||
|
@ -121,7 +126,7 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
|
|||
|
||||
if (request.user.profile.password && request.user.profile.password !== getPasswordHash(password, request.user.profile.salt)) {
|
||||
console.log('Reset settings failed: Incorrect password');
|
||||
return response.status(401).json({ error: 'Incorrect password' });
|
||||
return response.status(403).json({ error: 'Incorrect password' });
|
||||
}
|
||||
|
||||
const pathToFile = path.join(request.user.directories.root, SETTINGS_FILE);
|
||||
|
@ -165,6 +170,53 @@ router.post('/change-name', jsonParser, async (request, response) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.post('/reset-step1', jsonParser, async (request, response) => {
|
||||
try {
|
||||
const resetCode = String(crypto.randomInt(1000, 9999));
|
||||
console.log();
|
||||
console.log(color.magenta(`${request.user.profile.name}, your account reset code is: `) + color.red(resetCode));
|
||||
console.log();
|
||||
RESET_CACHE.set(request.user.profile.handle, resetCode);
|
||||
return response.sendStatus(204);
|
||||
} catch (error) {
|
||||
console.error('Recover step 1 failed:', error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/reset-step2', jsonParser, async (request, response) => {
|
||||
try{
|
||||
if (!request.body.code) {
|
||||
console.log('Recover step 2 failed: Missing required fields');
|
||||
return response.status(400).json({ error: 'Missing required fields' });
|
||||
}
|
||||
|
||||
if (request.user.profile.password && request.user.profile.password !== getPasswordHash(request.body.password, request.user.profile.salt)) {
|
||||
console.log('Recover step 2 failed: Incorrect password');
|
||||
return response.status(400).json({ error: 'Incorrect password' });
|
||||
}
|
||||
|
||||
const code = RESET_CACHE.get(request.user.profile.handle);
|
||||
|
||||
if (!code || code !== request.body.code) {
|
||||
console.log('Recover step 2 failed: Incorrect code');
|
||||
return response.status(400).json({ error: 'Incorrect code' });
|
||||
}
|
||||
|
||||
console.log('Resetting account data:', request.user.profile.handle);
|
||||
await fsPromises.rm(request.user.directories.root, { recursive: true, force: true });
|
||||
|
||||
await ensurePublicDirectoriesExist();
|
||||
await checkForNewContent([request.user.directories]);
|
||||
|
||||
RESET_CACHE.remove(request.user.profile.handle);
|
||||
return response.sendStatus(204);
|
||||
} catch (error) {
|
||||
console.error('Recover step 2 failed:', error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
router,
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ router.post('/login', jsonParser, async (request, response) => {
|
|||
|
||||
if (!user) {
|
||||
console.log('Login failed: User not found');
|
||||
return response.status(401).json({ error: 'User not found' });
|
||||
return response.status(403).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
if (!user.enabled) {
|
||||
|
@ -70,7 +70,7 @@ router.post('/login', jsonParser, async (request, response) => {
|
|||
|
||||
if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {
|
||||
console.log('Login failed: Incorrect password');
|
||||
return response.status(401).json({ error: 'Incorrect password' });
|
||||
return response.status(403).json({ error: 'Incorrect password' });
|
||||
}
|
||||
|
||||
if (!request.session) {
|
||||
|
@ -159,7 +159,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
|
|||
if (request.body.code !== mfaCode) {
|
||||
await recoverLimiter.consume(ip);
|
||||
console.log('Recover step 2 failed: Incorrect code');
|
||||
return response.status(401).json({ error: 'Incorrect code' });
|
||||
return response.status(403).json({ error: 'Incorrect code' });
|
||||
}
|
||||
|
||||
if (request.body.newPassword) {
|
||||
|
|
|
@ -551,7 +551,7 @@ async function setUserDataMiddleware(request, response, next) {
|
|||
*/
|
||||
function requireLoginMiddleware(request, response, next) {
|
||||
if (!request.user) {
|
||||
return response.sendStatus(401);
|
||||
return response.sendStatus(403);
|
||||
}
|
||||
|
||||
return next();
|
||||
|
@ -583,7 +583,7 @@ function createRouteHandler(directoryFn) {
|
|||
*/
|
||||
function requireAdminMiddleware(request, response, next) {
|
||||
if (!request.user) {
|
||||
return response.sendStatus(401);
|
||||
return response.sendStatus(403);
|
||||
}
|
||||
|
||||
if (request.user.profile.admin) {
|
||||
|
|
Loading…
Reference in New Issue