mirror of
https://github.com/HeyPuter/puter.git
synced 2026-01-08 06:00:28 -06:00
Remove captcha from frotnend (#1335)
This commit is contained in:
@@ -1,287 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CaptchaView - A component for displaying and handling captcha challenges
|
||||
*
|
||||
* @param {Object} options - Configuration options
|
||||
* @param {HTMLElement} options.container - The container element to attach the captcha to
|
||||
* @param {Function} options.onReady - Callback when the captcha is ready
|
||||
* @param {Function} options.onError - Callback for handling errors
|
||||
* @param {boolean} options.required - Whether captcha is required (will not display if false)
|
||||
* @param {Function} options.onRequiredChange - Callback when the required status changes
|
||||
* @returns {Object} - Methods to interact with the captcha
|
||||
*/
|
||||
function CaptchaView(options = {}) {
|
||||
// Internal state
|
||||
const state = {
|
||||
token: null,
|
||||
image: null,
|
||||
answer: '',
|
||||
loading: false,
|
||||
error: null,
|
||||
container: options.container || document.createElement('div'),
|
||||
required: options.required !== undefined ? options.required : true, // Default to required
|
||||
initialized: false
|
||||
};
|
||||
|
||||
// Create the initial DOM structure
|
||||
const init = () => {
|
||||
const container = state.container;
|
||||
container.classList.add('captcha-view-container');
|
||||
container.style.marginTop = '20px';
|
||||
container.style.marginBottom = '20px';
|
||||
|
||||
// Add container CSS
|
||||
container.style.display = state.required ? 'flex' : 'none';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '10px';
|
||||
|
||||
state.initialized = true;
|
||||
|
||||
// Render the initial HTML
|
||||
render();
|
||||
|
||||
// Only fetch captcha if required
|
||||
if (state.required) {
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
|
||||
// Set whether captcha is required
|
||||
const setRequired = (required) => {
|
||||
if (state.required === required) return; // No change
|
||||
|
||||
state.required = required;
|
||||
|
||||
if (state.initialized) {
|
||||
// Update display
|
||||
state.container.style.display = required ? 'flex' : 'none';
|
||||
|
||||
// If becoming required and no captcha loaded, fetch one
|
||||
if (required && !state.token) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
// Notify of change if callback provided
|
||||
if (typeof options.onRequiredChange === 'function') {
|
||||
options.onRequiredChange(required);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Render the captcha HTML
|
||||
const render = () => {
|
||||
const container = state.container;
|
||||
|
||||
// Clear the container
|
||||
container.innerHTML = '';
|
||||
|
||||
// Label
|
||||
const label = document.createElement('label');
|
||||
label.textContent = i18n('captcha_verification');
|
||||
label.setAttribute('for', `captcha-input-${Date.now()}`);
|
||||
container.appendChild(label);
|
||||
|
||||
// Captcha wrapper
|
||||
const captchaWrapper = document.createElement('div');
|
||||
captchaWrapper.classList.add('captcha-wrapper');
|
||||
captchaWrapper.style.display = 'flex';
|
||||
captchaWrapper.style.flexDirection = 'column';
|
||||
captchaWrapper.style.gap = '10px';
|
||||
container.appendChild(captchaWrapper);
|
||||
|
||||
// Captcha image and refresh button container
|
||||
const imageContainer = document.createElement('div');
|
||||
imageContainer.style.display = 'flex';
|
||||
imageContainer.style.alignItems = 'stretch';
|
||||
imageContainer.style.justifyContent = 'space-between';
|
||||
imageContainer.style.gap = '10px';
|
||||
imageContainer.style.border = '1px solid #ced7e1';
|
||||
imageContainer.style.borderRadius = '4px';
|
||||
imageContainer.style.padding = '10px';
|
||||
captchaWrapper.appendChild(imageContainer);
|
||||
|
||||
// Captcha image
|
||||
const imageElement = document.createElement('div');
|
||||
imageElement.classList.add('captcha-image');
|
||||
|
||||
if (state.loading) {
|
||||
imageElement.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:50px;"><span style="font-size:14px;">Loading captcha...</span></div>';
|
||||
} else if (state.error) {
|
||||
imageElement.innerHTML = `<div style="color:red;padding:10px;">${state.error}</div>`;
|
||||
} else if (state.image) {
|
||||
imageElement.innerHTML = state.image;
|
||||
// Make SVG responsive
|
||||
const svgElement = imageElement.querySelector('svg');
|
||||
if (svgElement) {
|
||||
svgElement.style.width = '100%';
|
||||
svgElement.style.height = 'auto';
|
||||
}
|
||||
} else {
|
||||
imageElement.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:50px;"><span style="font-size:14px;">No captcha loaded</span></div>';
|
||||
}
|
||||
imageContainer.appendChild(imageElement);
|
||||
|
||||
// Refresh button
|
||||
const refreshButton = document.createElement('button');
|
||||
refreshButton.classList.add('button', 'button-small');
|
||||
refreshButton.innerHTML = '<i class="fas fa-sync-alt">⟳</i>';
|
||||
refreshButton.setAttribute('title', i18n('refresh_captcha'));
|
||||
refreshButton.style.minWidth = '30px';
|
||||
refreshButton.style.fontSize = '30px';
|
||||
refreshButton.style.height = 'auto';
|
||||
refreshButton.style.marginBottom = '4px';
|
||||
refreshButton.setAttribute('type', 'button');
|
||||
refreshButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
refresh();
|
||||
});
|
||||
imageContainer.appendChild(refreshButton);
|
||||
|
||||
// Input field
|
||||
const inputField = document.createElement('input');
|
||||
inputField.id = `captcha-input-${Date.now()}`;
|
||||
inputField.classList.add('captcha-input');
|
||||
inputField.type = 'text';
|
||||
inputField.placeholder = i18n('enter_captcha_text');
|
||||
inputField.setAttribute('autocomplete', 'off');
|
||||
inputField.setAttribute('spellcheck', 'false');
|
||||
inputField.setAttribute('autocorrect', 'off');
|
||||
inputField.setAttribute('autocapitalize', 'off');
|
||||
inputField.value = state.answer || '';
|
||||
inputField.addEventListener('input', (e) => {
|
||||
state.answer = e.target.value;
|
||||
});
|
||||
|
||||
// Prevent Enter key from triggering refresh and allow it to submit the form
|
||||
inputField.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
// Don't prevent default here - let Enter bubble up to the form
|
||||
// Just make sure we don't refresh the captcha
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
captchaWrapper.appendChild(inputField);
|
||||
|
||||
// Helper text
|
||||
const helperText = document.createElement('div');
|
||||
helperText.classList.add('captcha-helper-text');
|
||||
helperText.style.fontSize = '12px';
|
||||
helperText.style.color = '#666';
|
||||
helperText.textContent = i18n('captcha_case_sensitive');
|
||||
captchaWrapper.appendChild(helperText);
|
||||
};
|
||||
|
||||
// Fetch a new captcha
|
||||
const refresh = async () => {
|
||||
// Skip if not required
|
||||
if (!state.required) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
render();
|
||||
|
||||
const response = await fetch(window.gui_origin + '/api/captcha/generate');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load captcha: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
state.token = data.token;
|
||||
state.image = data.image;
|
||||
state.loading = false;
|
||||
|
||||
render();
|
||||
|
||||
if (typeof options.onReady === 'function') {
|
||||
options.onReady();
|
||||
}
|
||||
} catch (error) {
|
||||
state.loading = false;
|
||||
state.error = error.message || 'Failed to load captcha';
|
||||
|
||||
render();
|
||||
|
||||
if (typeof options.onError === 'function') {
|
||||
options.onError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Public API
|
||||
const api = {
|
||||
/**
|
||||
* Get the current captcha token
|
||||
* @returns {string} The captcha token
|
||||
*/
|
||||
getToken: () => state.token,
|
||||
|
||||
/**
|
||||
* Get the current captcha answer
|
||||
* @returns {string} The user's answer
|
||||
*/
|
||||
getAnswer: () => state.answer,
|
||||
|
||||
/**
|
||||
* Reset the captcha - clear answer and get a new challenge
|
||||
*/
|
||||
reset: () => {
|
||||
state.answer = '';
|
||||
refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the container element
|
||||
* @returns {HTMLElement} The container element
|
||||
*/
|
||||
getElement: () => state.container,
|
||||
|
||||
/**
|
||||
* Check if captcha is required
|
||||
* @returns {boolean} Whether captcha is required
|
||||
*/
|
||||
isRequired: () => state.required,
|
||||
|
||||
/**
|
||||
* Set whether captcha is required
|
||||
* @param {boolean} required - Whether captcha is required
|
||||
*/
|
||||
setRequired: setRequired
|
||||
};
|
||||
|
||||
// Set initial required state from options
|
||||
if (options.required !== undefined) {
|
||||
state.required = options.required;
|
||||
}
|
||||
|
||||
// Initialize the component
|
||||
init();
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
export default CaptchaView;
|
||||
@@ -28,8 +28,6 @@ import JustHTML from './Components/JustHTML.js';
|
||||
import StepView from './Components/StepView.js';
|
||||
import Button from './Components/Button.js';
|
||||
import RecoveryCodeEntryView from './Components/RecoveryCodeEntryView.js';
|
||||
import CaptchaView from './Components/CaptchaView.js'
|
||||
import { isCaptchaRequired } from '../helpers/captchaHelper.js';
|
||||
|
||||
async function UIWindowLogin(options){
|
||||
options = options ?? {};
|
||||
@@ -40,10 +38,6 @@ async function UIWindowLogin(options){
|
||||
return new Promise(async (resolve) => {
|
||||
const internal_id = window.uuidv4();
|
||||
|
||||
// Check if captcha is required for login
|
||||
const captchaRequired = await isCaptchaRequired('login');
|
||||
console.log('Login captcha required:', captchaRequired);
|
||||
|
||||
let h = ``;
|
||||
h += `<div style="max-width:100%; width:100%; height:100%; min-height:0; box-sizing:border-box; display:flex; flex-direction:column; justify-content:flex-start; align-items:stretch; padding:0; overflow:auto; color:var(--color-text);">`;
|
||||
// logo
|
||||
@@ -77,10 +71,6 @@ async function UIWindowLogin(options){
|
||||
<img class="toggle-show-password-icon" src="${options.show_password ? window.icons["eye-closed.svg"] : window.icons["eye-open.svg"]}" width="20" height="20">
|
||||
</span>`;
|
||||
h += `</div>`;
|
||||
// captcha placeholder - will be replaced with actual captcha component
|
||||
h += `<div class="captcha-container"></div>`;
|
||||
// captcha-specific error message
|
||||
h += `<div class="captcha-error-msg" style="color: #e74c3c; font-size: 12px; margin-top: 5px; display: none;" aria-live="polite"></div>`;
|
||||
// login
|
||||
h += `<button type="submit" class="login-btn button button-primary button-block button-normal">${i18n('log_in')}</button>`;
|
||||
// password recovery
|
||||
@@ -141,46 +131,6 @@ async function UIWindowLogin(options){
|
||||
}
|
||||
})
|
||||
|
||||
// Initialize the captcha component with the required state
|
||||
const captchaContainer = $(el_window).find('.captcha-container')[0];
|
||||
const captcha = CaptchaView({
|
||||
container: captchaContainer,
|
||||
required: captchaRequired,
|
||||
});
|
||||
|
||||
// Function to show captcha-specific error
|
||||
const showCaptchaError = (message) => {
|
||||
// Hide the general error message if shown
|
||||
$(el_window).find('.login-error-msg').hide();
|
||||
|
||||
// Show captcha-specific error
|
||||
const captchaError = $(el_window).find('.captcha-error-msg');
|
||||
captchaError.html(message);
|
||||
captchaError.fadeIn();
|
||||
|
||||
// Add visual indication of error to captcha container
|
||||
$(captchaContainer).addClass('error');
|
||||
$(captchaContainer).css('border', '1px solid #e74c3c');
|
||||
$(captchaContainer).css('border-radius', '4px');
|
||||
$(captchaContainer).css('padding', '10px');
|
||||
|
||||
// Focus on the captcha input for better UX
|
||||
setTimeout(() => {
|
||||
const captchaInput = $(captchaContainer).find('.captcha-input');
|
||||
if (captchaInput.length) {
|
||||
captchaInput.focus();
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Function to clear captcha errors
|
||||
const clearCaptchaError = () => {
|
||||
$(el_window).find('.captcha-error-msg').hide();
|
||||
$(captchaContainer).removeClass('error');
|
||||
$(captchaContainer).css('border', '');
|
||||
$(captchaContainer).css('padding', '');
|
||||
};
|
||||
|
||||
$(el_window).find('.forgot-password-link').on('click', function(e){
|
||||
UIWindowRecoverPassword({
|
||||
window_options: {
|
||||
@@ -197,7 +147,6 @@ async function UIWindowLogin(options){
|
||||
|
||||
// Clear previous error states
|
||||
$(el_window).find('.login-error-msg').hide();
|
||||
clearCaptchaError();
|
||||
|
||||
const email_username = $(el_window).find('.email_or_username').val();
|
||||
const password = $(el_window).find('.password').val();
|
||||
@@ -215,61 +164,17 @@ async function UIWindowLogin(options){
|
||||
return;
|
||||
}
|
||||
|
||||
// Get captcha token and answer if required
|
||||
let captchaToken = null;
|
||||
let captchaAnswer = null;
|
||||
|
||||
if (captcha.isRequired()) {
|
||||
captchaToken = captcha.getToken();
|
||||
captchaAnswer = captcha.getAnswer();
|
||||
|
||||
// Validate captcha if it's required
|
||||
if (!captcha || !captchaContainer) {
|
||||
$(el_window).find('.login-error-msg').html(i18n('captcha_system_error') || 'Verification system error. Please refresh the page.');
|
||||
$(el_window).find('.login-error-msg').fadeIn();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!captchaToken) {
|
||||
showCaptchaError(i18n('captcha_load_error') || 'Could not load verification code. Please refresh the page or try again later.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!captchaAnswer) {
|
||||
showCaptchaError(i18n('captcha_required') || 'Please enter the verification code');
|
||||
return;
|
||||
}
|
||||
|
||||
if (captchaAnswer.trim().length < 3) {
|
||||
showCaptchaError(i18n('captcha_too_short') || 'Verification code answer is too short.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (captchaAnswer.trim().length > 12) {
|
||||
showCaptchaError(i18n('captcha_too_long') || 'Verification code answer is too long.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare data for the request
|
||||
let data;
|
||||
if(window.is_email(email_username)){
|
||||
data = JSON.stringify({
|
||||
email: email_username,
|
||||
password: password,
|
||||
...(captchaToken && captchaAnswer ? {
|
||||
captchaToken: captchaToken,
|
||||
captchaAnswer: captchaAnswer
|
||||
} : {})
|
||||
});
|
||||
} else {
|
||||
data = JSON.stringify({
|
||||
username: email_username,
|
||||
password: password,
|
||||
...(captchaToken && captchaAnswer ? {
|
||||
captchaToken: captchaToken,
|
||||
captchaAnswer: captchaAnswer
|
||||
} : {})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -485,31 +390,11 @@ async function UIWindowLogin(options){
|
||||
|
||||
// Handle captcha-specific errors
|
||||
const errorText = err.responseText || '';
|
||||
const errorStatus = err.status || 0;
|
||||
|
||||
// Try to parse error as JSON
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText);
|
||||
|
||||
// Check for specific error codes
|
||||
if (errorJson.code === 'captcha_required') {
|
||||
// If captcha is now required but wasn't before, update the component
|
||||
if (!captcha.isRequired()) {
|
||||
captcha.setRequired(true);
|
||||
showCaptchaError(i18n('captcha_now_required') || 'Verification is now required. Please complete the verification below.');
|
||||
} else {
|
||||
showCaptchaError(i18n('captcha_required') || 'Please enter the verification code');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (errorJson.code === 'captcha_invalid' || errorJson.code === 'captcha_error') {
|
||||
showCaptchaError(i18n('captcha_invalid') || 'Invalid verification code');
|
||||
// Refresh the captcha if it's invalid
|
||||
captcha.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's a message in the JSON, use that
|
||||
if (errorJson.message) {
|
||||
$(el_window).find('.login-error-msg').html(errorJson.message);
|
||||
@@ -520,33 +405,6 @@ async function UIWindowLogin(options){
|
||||
// Not JSON, continue with text analysis
|
||||
}
|
||||
|
||||
// Check for specific captcha errors using more robust detection for text responses
|
||||
if (
|
||||
errorText.includes('captcha_required') ||
|
||||
errorText.includes('Captcha verification required') ||
|
||||
(errorText.includes('captcha') && errorText.includes('required'))
|
||||
) {
|
||||
// If captcha is now required but wasn't before, update the component
|
||||
if (!captcha.isRequired()) {
|
||||
captcha.setRequired(true);
|
||||
showCaptchaError(i18n('captcha_now_required') || 'Verification is now required. Please complete the verification below.');
|
||||
} else {
|
||||
showCaptchaError(i18n('captcha_required') || 'Please enter the verification code');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
errorText.includes('captcha_invalid') ||
|
||||
errorText.includes('Invalid captcha') ||
|
||||
(errorText.includes('captcha') && (errorText.includes('invalid') || errorText.includes('incorrect')))
|
||||
) {
|
||||
showCaptchaError(i18n('captcha_invalid') || 'Invalid verification code');
|
||||
// Refresh the captcha if it's invalid
|
||||
captcha.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to original error handling
|
||||
const $errorMessage = $(el_window).find('.login-error-msg');
|
||||
if (err.status === 404) {
|
||||
|
||||
@@ -21,8 +21,6 @@ import UIWindow from './UIWindow.js'
|
||||
import UIWindowLogin from './UIWindowLogin.js'
|
||||
import UIWindowEmailConfirmationRequired from './UIWindowEmailConfirmationRequired.js'
|
||||
import check_password_strength from '../helpers/check_password_strength.js'
|
||||
import CaptchaView from './Components/CaptchaView.js'
|
||||
import { isCaptchaRequired } from '../helpers/captchaHelper.js'
|
||||
|
||||
function UIWindowSignup(options){
|
||||
options = options ?? {};
|
||||
@@ -34,10 +32,6 @@ function UIWindowSignup(options){
|
||||
return new Promise(async (resolve) => {
|
||||
const internal_id = window.uuidv4();
|
||||
|
||||
// Check if captcha is required for signup
|
||||
const captchaRequired = await isCaptchaRequired('signup');
|
||||
console.log('Signup captcha required:', captchaRequired);
|
||||
|
||||
let h = '';
|
||||
h += `<div style="margin: 0 auto; max-width: 500px; min-width: 400px;">`;
|
||||
// logo
|
||||
@@ -82,10 +76,6 @@ function UIWindowSignup(options){
|
||||
<img class="toggle-show-password-icon" src="${options.show_password ? window.icons["eye-closed.svg"] : window.icons["eye-open.svg"]}" width="20" height="20">
|
||||
</span>`;
|
||||
h += `</div>`;
|
||||
// captcha placeholder - will be replaced with actual captcha component
|
||||
h += `<div class="captcha-container"></div>`;
|
||||
// captcha-specific error message
|
||||
h += `<div class="captcha-error-msg" style="color: #e74c3c; font-size: 12px; margin-top: 5px; display: none;" aria-live="polite"></div>`;
|
||||
// bot trap - if this value is submitted server will ignore the request
|
||||
h += `<input type="text" name="p102xyzname" class="p102xyzname" value="">`;
|
||||
|
||||
@@ -143,13 +133,6 @@ function UIWindowSignup(options){
|
||||
}
|
||||
})
|
||||
|
||||
// Initialize the captcha component with the required state
|
||||
const captchaContainer = $(el_window).find('.captcha-container')[0];
|
||||
const captcha = CaptchaView({
|
||||
container: captchaContainer,
|
||||
required: captchaRequired
|
||||
});
|
||||
|
||||
$(el_window).find('.login-c2a-clickable').on('click', async function(e){
|
||||
$('.login-c2a-clickable').parents('.window').close();
|
||||
const login = await UIWindowLogin({
|
||||
@@ -164,43 +147,9 @@ function UIWindowSignup(options){
|
||||
resolve(true);
|
||||
})
|
||||
|
||||
// Function to show captcha-specific error
|
||||
const showCaptchaError = (message) => {
|
||||
// Hide the general error message if shown
|
||||
$(el_window).find('.signup-error-msg').hide();
|
||||
|
||||
// Show captcha-specific error
|
||||
const captchaError = $(el_window).find('.captcha-error-msg');
|
||||
captchaError.html(message);
|
||||
captchaError.fadeIn();
|
||||
|
||||
// Add visual indication of error to captcha container
|
||||
$(captchaContainer).addClass('error');
|
||||
$(captchaContainer).css('border', '1px solid #e74c3c');
|
||||
$(captchaContainer).css('border-radius', '4px');
|
||||
$(captchaContainer).css('padding', '10px');
|
||||
|
||||
// Focus on the captcha input for better UX
|
||||
setTimeout(() => {
|
||||
const captchaInput = $(captchaContainer).find('.captcha-input');
|
||||
if (captchaInput.length) {
|
||||
captchaInput.focus();
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Function to clear captcha errors
|
||||
const clearCaptchaError = () => {
|
||||
$(el_window).find('.captcha-error-msg').hide();
|
||||
$(captchaContainer).removeClass('error');
|
||||
$(captchaContainer).css('border', '');
|
||||
$(captchaContainer).css('padding', '');
|
||||
};
|
||||
|
||||
$(el_window).find('.signup-btn').on('click', function(e){
|
||||
// Clear previous error states
|
||||
$(el_window).find('.signup-error-msg').hide();
|
||||
clearCaptchaError();
|
||||
|
||||
//Username
|
||||
let username = $(el_window).find('.username').val();
|
||||
@@ -255,46 +204,6 @@ function UIWindowSignup(options){
|
||||
$(el_window).find('.signup-error-msg').html(i18n('passwords_do_not_match'));
|
||||
$(el_window).find('.signup-error-msg').fadeIn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get captcha token and answer if required
|
||||
let captchaToken = null;
|
||||
let captchaAnswer = null;
|
||||
|
||||
if (captcha.isRequired()) {
|
||||
captchaToken = captcha.getToken();
|
||||
captchaAnswer = captcha.getAnswer();
|
||||
|
||||
// Check if the captcha component is properly loaded
|
||||
if (!captcha || !captchaContainer) {
|
||||
$(el_window).find('.signup-error-msg').html(i18n('captcha_system_error') || 'Verification system error. Please refresh the page.');
|
||||
$(el_window).find('.signup-error-msg').fadeIn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if captcha token exists
|
||||
if (!captchaToken) {
|
||||
showCaptchaError(i18n('captcha_load_error') || 'Could not load verification code. Please refresh the page or try again later.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the answer is provided
|
||||
if (!captchaAnswer) {
|
||||
showCaptchaError(i18n('captcha_required'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if answer meets minimum length requirement
|
||||
if (captchaAnswer.trim().length < 3) {
|
||||
showCaptchaError(i18n('captcha_too_short') || 'Verification code answer is too short.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if answer meets maximum length requirement
|
||||
if (captchaAnswer.trim().length > 12) {
|
||||
showCaptchaError(i18n('captcha_too_long') || 'Verification code answer is too long.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//xyzname
|
||||
@@ -316,10 +225,6 @@ function UIWindowSignup(options){
|
||||
referrer: options.referrer ?? window.referrerStr,
|
||||
send_confirmation_code: options.send_confirmation_code,
|
||||
p102xyzname: p102xyzname,
|
||||
...(captchaToken && captchaAnswer ? {
|
||||
captchaToken: captchaToken,
|
||||
captchaAnswer: captchaAnswer
|
||||
} : {})
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
@@ -352,32 +257,12 @@ function UIWindowSignup(options){
|
||||
|
||||
// Process error response
|
||||
const errorText = err.responseText || '';
|
||||
const errorStatus = err.status || 0;
|
||||
|
||||
// Handle JSON error response
|
||||
try {
|
||||
// Try to parse error as JSON
|
||||
const errorJson = JSON.parse(errorText);
|
||||
|
||||
// Check for specific error codes
|
||||
if (errorJson.code === 'captcha_required') {
|
||||
// If captcha is now required but wasn't before, update the component
|
||||
if (!captcha.isRequired()) {
|
||||
captcha.setRequired(true);
|
||||
showCaptchaError(i18n('captcha_now_required') || 'Verification is now required. Please complete the verification below.');
|
||||
} else {
|
||||
showCaptchaError(i18n('captcha_required') || 'Please enter the verification code');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (errorJson.code === 'captcha_invalid' || errorJson.code === 'captcha_error') {
|
||||
showCaptchaError(i18n('captcha_invalid') || 'Invalid verification code');
|
||||
// Refresh the captcha if it's invalid
|
||||
captcha.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle timeout specifically
|
||||
if (errorJson?.code === 'response_timeout' || errorText.includes('timeout')) {
|
||||
$(el_window).find('.signup-error-msg').html(i18n('server_timeout') || 'The server took too long to respond. Please try again.');
|
||||
@@ -394,27 +279,6 @@ function UIWindowSignup(options){
|
||||
} catch (e) {
|
||||
// Not JSON, continue with text analysis
|
||||
}
|
||||
|
||||
// Check for specific captcha errors using more robust detection for text responses
|
||||
if (
|
||||
errorText.includes('captcha_required') ||
|
||||
errorText.includes('Captcha verification required') ||
|
||||
(errorText.includes('captcha') && errorText.includes('required'))
|
||||
) {
|
||||
showCaptchaError(i18n('captcha_required'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
errorText.includes('captcha_invalid') ||
|
||||
errorText.includes('Invalid captcha') ||
|
||||
(errorText.includes('captcha') && (errorText.includes('invalid') || errorText.includes('incorrect')))
|
||||
) {
|
||||
showCaptchaError(i18n('captcha_invalid'));
|
||||
// Refresh the captcha if it's invalid
|
||||
captcha.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default general error handling
|
||||
$(el_window).find('.signup-error-msg').html(errorText || i18n('signup_error') || 'An error occurred during signup. Please try again.');
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cache for captcha requirements to avoid repeated API calls
|
||||
*/
|
||||
let captchaRequirementsCache = null;
|
||||
|
||||
/**
|
||||
* Checks if captcha is required for a specific action
|
||||
*
|
||||
* This function first checks GUI parameters, then falls back to the /whoarewe endpoint
|
||||
*
|
||||
* @param {string} actionType - The type of action (e.g., 'login', 'signup')
|
||||
* @returns {Promise<boolean>} - Whether captcha is required for this action
|
||||
*/
|
||||
async function isCaptchaRequired(actionType) {
|
||||
console.log('CAPTCHA DIAGNOSTIC (Client): isCaptchaRequired called for', actionType);
|
||||
|
||||
// Check if we have the info in GUI parameters
|
||||
if (window.gui_params?.captchaRequired?.[actionType] !== undefined) {
|
||||
console.log(`CAPTCHA DIAGNOSTIC (Client): Requirement for ${actionType} from GUI params:`, window.gui_params.captchaRequired[actionType]);
|
||||
console.log('CAPTCHA DIAGNOSTIC (Client): Full gui_params.captchaRequired =', JSON.stringify(window.gui_params.captchaRequired));
|
||||
return window.gui_params.captchaRequired[actionType];
|
||||
}
|
||||
|
||||
// If not in GUI params, check the cache
|
||||
if (captchaRequirementsCache && captchaRequirementsCache.captchaRequired?.[actionType] !== undefined) {
|
||||
console.log(`CAPTCHA DIAGNOSTIC (Client): Requirement for ${actionType} from cache:`, captchaRequirementsCache.captchaRequired[actionType]);
|
||||
console.log('CAPTCHA DIAGNOSTIC (Client): Full cache =', JSON.stringify(captchaRequirementsCache.captchaRequired));
|
||||
return captchaRequirementsCache.captchaRequired[actionType];
|
||||
}
|
||||
|
||||
// If not in cache, fetch from the /whoarewe endpoint
|
||||
try {
|
||||
console.log(`CAPTCHA DIAGNOSTIC (Client): Fetching from /whoarewe for ${actionType}`);
|
||||
const response = await fetch(window.api_origin + '/whoarewe');
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(`CAPTCHA DIAGNOSTIC (Client): Failed to get requirements: ${response.status}`);
|
||||
return true; // Default to requiring captcha if we can't determine
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log(`CAPTCHA DIAGNOSTIC (Client): /whoarewe response:`, data);
|
||||
|
||||
// Cache the result
|
||||
captchaRequirementsCache = data;
|
||||
|
||||
// Return the requirement or default to true if not specified
|
||||
const result = data.captchaRequired?.[actionType] ?? true;
|
||||
console.log(`CAPTCHA DIAGNOSTIC (Client): Final result for ${actionType}:`, result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('CAPTCHA DIAGNOSTIC (Client): Error checking requirements:', error);
|
||||
return true; // Default to requiring captcha on error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the captcha requirements cache
|
||||
* This is useful when the requirements might have changed
|
||||
*/
|
||||
function invalidateCaptchaRequirementsCache() {
|
||||
captchaRequirementsCache = null;
|
||||
}
|
||||
|
||||
export {
|
||||
isCaptchaRequired,
|
||||
invalidateCaptchaRequirementsCache
|
||||
};
|
||||
@@ -417,20 +417,6 @@ const en = {
|
||||
'billing.expanded': 'Expanded',
|
||||
'billing.accelerated': 'Accelerated',
|
||||
'billing.enjoy_msg': 'Enjoy %% of Cloud Storage plus other benefits.',
|
||||
|
||||
// Captcha related strings
|
||||
'captcha_verification': 'Verification Code',
|
||||
'enter_captcha_text': 'Enter the text you see above',
|
||||
'refresh_captcha': 'Get a new code',
|
||||
'captcha_case_sensitive': 'Please enter the characters exactly as they appear. Case sensitive.',
|
||||
'captcha_required': 'Please complete the verification code.',
|
||||
'captcha_now_required': 'Verification is now required. Please complete the verification below.',
|
||||
'captcha_invalid': 'Incorrect verification code. Please try again.',
|
||||
'captcha_expired': 'Verification code has expired. Please try a new one.',
|
||||
'captcha_system_error': 'Verification system error. Please refresh the page.',
|
||||
'captcha_load_error': 'Could not load verification code. Please refresh the page or try again later.',
|
||||
'captcha_too_short': 'Verification code answer is too short.',
|
||||
'captcha_too_long': 'Verification code answer is too long.',
|
||||
'too_many_attempts': 'Too many attempts. Please try again later.',
|
||||
'server_timeout': 'The server took too long to respond. Please try again.',
|
||||
'signup_error': 'An error occurred during signup. Please try again.'
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
window.puter_gui_enabled = true;
|
||||
|
||||
import { isCaptchaRequired } from './helpers/captchaHelper.js';
|
||||
|
||||
/**
|
||||
* Initializes and configures the GUI (Graphical User Interface) settings based on the provided options.
|
||||
*
|
||||
@@ -61,18 +59,6 @@ window.gui = async (options) => {
|
||||
window.disable_temp_users = options.disable_temp_users ?? false;
|
||||
window.co_isolation_enabled = options.co_isolation_enabled;
|
||||
|
||||
// Preload captcha requirements if not already in GUI parameters
|
||||
if (!options.captchaRequired) {
|
||||
// Start loading in the background, but don't await
|
||||
// This way we don't delay the GUI initialization
|
||||
Promise.all([
|
||||
isCaptchaRequired('login'),
|
||||
isCaptchaRequired('signup')
|
||||
]).catch(err => {
|
||||
console.warn('Failed to preload captcha requirements:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// DEV: Load the initgui.js file if we are in development mode
|
||||
if(!window.gui_env || window.gui_env === "dev"){
|
||||
await window.loadScript('/sdk/puter.dev.js');
|
||||
|
||||
Reference in New Issue
Block a user