diff --git a/public/css/login.css b/public/css/login.css new file mode 100644 index 000000000..d93ae1b85 --- /dev/null +++ b/public/css/login.css @@ -0,0 +1,35 @@ +body.login #shadow_popup { + opacity: 1; + display: flex; +} + +body.login .logo { + max-width: 30px; +} + +body.login #logoBlock { + align-items: center; + margin: 0 auto; + gap: 10px; +} + +body.login .userSelect { + display: flex; + flex-direction: column; + color: var(--SmartThemeBodyColor); + border: 1px solid var(--SmartThemeBorderColor); + border-radius: 5px; + padding: 3px 5px; + width: min-content; + cursor: pointer; + margin: 5px 0; + transition: background-color 0.15s ease-in-out; + display: flex; + align-items: center; + justify-content: center; + text-align: center; +} + +body.login .userSelect:hover { + background-color: var(--black30a); +} diff --git a/public/img/logo.png b/public/img/logo.png new file mode 100644 index 000000000..14768e82b Binary files /dev/null and b/public/img/logo.png differ diff --git a/public/login.html b/public/login.html index e69de29bb..eb05d012f 100644 --- a/public/login.html +++ b/public/login.html @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + SillyTavern + + + +
+
+
+
+
+

+ + Welcome to SillyTavern +

+

Select a User

+
+
+ + +
+
+
+
+
+
+
+ + + diff --git a/public/scripts/loader.js b/public/scripts/loader.js index 179a0437b..91e7df196 100644 --- a/public/scripts/loader.js +++ b/public/scripts/loader.js @@ -1,7 +1,5 @@ const ELEMENT_ID = 'loader'; -import { populateUserList } from './userManagement.js' - export function showLoader() { const container = $('
').attr('id', ELEMENT_ID); const loader = $('
').attr('id', 'load-spinner').addClass('fa-solid fa-gear fa-spin fa-3x'); @@ -10,7 +8,6 @@ export function showLoader() { } export async function hideLoader() { - //Sets up a 2-step animation. Spinner blurs/fades out, and then the loader shadow does the same. $('#load-spinner').on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { //uncomment this as part of user selection enabling @@ -35,8 +32,4 @@ export async function hideLoader() { //uncomment to make user selection live //await populateUserList() - - } - - diff --git a/public/scripts/login.js b/public/scripts/login.js new file mode 100644 index 000000000..ef34b6747 --- /dev/null +++ b/public/scripts/login.js @@ -0,0 +1,142 @@ +async function getUserList() { + const response = await fetch('/api/users/list'); + const userListObj = await response.json(); + console.log(userListObj); + return userListObj; +} + +async function sendRecoveryPart1(handle) { + const response = await fetch('/api/users/recover-step1', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ handle }), + }); + + if (!response.ok) { + const errorData = await response.json(); + return displayError(errorData.error || 'An error occurred'); + } + + showRecoveryBlock(); +} + +async function sendRecoveryPart2(handle, code, newPassword) { + const recoveryData = { + handle, + code, + newPassword, + }; + + const response = await fetch('/api/users/recover-step2', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(recoveryData), + }); + + if (!response.ok) { + const errorData = await response.json(); + return displayError(errorData.error || 'An error occurred'); + } + + console.log(`Successfully recovered password for ${handle}!`); + await performLogin(handle, newPassword); +} + +async function onUserSelected(user) { + // No password, just log in + if (!user.password) { + return await performLogin(user.handle, ''); + } + + $('#passwordRecoveryBlock').hide(); + $('#passwordEntryBlock').show(); + $('#loginButton').off('click').on('click', async () => { + const password = String($('#userPassword').val()); + await performLogin(user.handle, password); + }); + + $('#recoverPassword').off('click').on('click', async () => { + await sendRecoveryPart1(user.handle); + }); + + $('#sendRecovery').off('click').on('click', async () => { + const code = String($('#recoveryCode').val()); + const newPassword = String($('#newPassword').val()); + await sendRecoveryPart2(user.handle, code, newPassword); + }); + + displayError(''); +} + +function displayError(message) { + $('#errorMessage').text(message); +} + +async function performLogin(handle, password) { + const userInfo = { + handle: handle, + password: password, + }; + + try { + const response = await fetch('/api/users/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(userInfo), + }); + + if (!response.ok) { + const errorData = await response.json(); + return displayError(errorData.error || 'An error occurred'); + } + + const data = await response.json(); + + if (data.handle) { + console.log(`Successfully logged in as ${handle}!`); + redirectToHome(); + } + } catch (error) { + console.error('Error logging in:', error); + displayError(String(error)); + } +} + +function redirectToHome() { + window.location.href = '/'; +} + +function showRecoveryBlock() { + $('#passwordEntryBlock').hide(); + $('#passwordRecoveryBlock').show(); + displayError(''); +} + +function onCancelRecoveryClick() { + $('#passwordRecoveryBlock').hide(); + $('#passwordEntryBlock').show(); + displayError(''); +} + +(async function () { + const userList = await getUserList(); + console.log(userList); + for (const user of userList) { + const userBlock = $('
').addClass('userSelect'); + const avatarBlock = $('
').addClass('avatar'); + avatarBlock.append($('').attr('src', user.avatar)); + userBlock.append(avatarBlock); + userBlock.append($('').text(user.name)); + userBlock.append($('').text(user.handle)); + userBlock.on('click', () => onUserSelected(user)); + $('#userList').append(userBlock); + } + document.body.style.display = ''; + $('#cancelRecovery').on('click', onCancelRecoveryClick); +})(); diff --git a/public/scripts/userManagement.js b/public/scripts/userManagement.js index eff0b02b3..e3cd9d34f 100644 --- a/public/scripts/userManagement.js +++ b/public/scripts/userManagement.js @@ -1,10 +1,3 @@ -async function getUserList() { - const response = await fetch('/api/users/list'); - const userListObj = await response.json(); // Assuming the response is in JSON format - console.log(userListObj) - return userListObj; -} - async function registerNewUser() { let handle = String($("#newUserHandle").val()); let name = String($("#newUserName").val()); @@ -111,17 +104,6 @@ export async function populateUserList() { ` const userSelectHTML = ` -
-

Select User

- This is merely a test.
Click a user, and then click Login to proceed.
-
-
- -