Регестрация/вход
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Регистрация и вход</title>
<style>
* {
box-sizing: border-box;
font-family: system-ui, 'Segoe UI', Roboto, sans-serif;
}
body {
background: linear-gradient(145deg, #f5f7fc 0%, #e9eef4 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
padding: 20px;
}
.card {
background: white;
border-radius: 32px;
box-shadow: 0 20px 35px -12px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 460px;
padding: 30px 28px 38px;
transition: all 0.2s ease;
}
h2 {
font-size: 28px;
font-weight: 600;
margin: 0 0 8px 0;
color: #1e293b;
text-align: center;
}
.sub {
text-align: center;
color: #5b6e8c;
font-size: 14px;
margin-bottom: 28px;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 12px;
}
.tabs {
display: flex;
gap: 12px;
margin-bottom: 28px;
background: #f1f5f9;
padding: 6px;
border-radius: 60px;
}
.tab-btn {
flex: 1;
background: transparent;
border: none;
font-size: 16px;
font-weight: 600;
padding: 10px 0;
border-radius: 40px;
cursor: pointer;
transition: 0.2s;
color: #475569;
}
.tab-btn.active {
background: white;
color: #0f3b5c;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
font-size: 13px;
font-weight: 500;
color: #1e293b;
margin-bottom: 6px;
}
input {
width: 100%;
padding: 14px 16px;
font-size: 15px;
border: 1.5px solid #e2e8f0;
border-radius: 24px;
background: #ffffff;
transition: 0.2s;
outline: none;
}
input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59,130,246,0.2);
}
button[type="submit"] {
width: 100%;
background: #0f172a;
color: white;
border: none;
padding: 14px;
font-size: 16px;
font-weight: 600;
border-radius: 40px;
cursor: pointer;
transition: 0.2s;
margin-top: 12px;
}
button[type="submit"]:hover {
background: #1e293b;
transform: scale(0.98);
}
.message {
margin-top: 20px;
padding: 12px 16px;
border-radius: 28px;
font-size: 14px;
text-align: center;
background: #f8fafc;
color: #0f172a;
}
.message.error {
background: #fee2e2;
color: #b91c1c;
}
.message.success {
background: #e0f2fe;
color: #075985;
}
.logout-area {
margin-top: 24px;
text-align: center;
border-top: 1px solid #e2e8f0;
padding-top: 20px;
}
.logout-btn {
background: none;
border: 1px solid #cbd5e1;
padding: 8px 20px;
border-radius: 40px;
font-size: 13px;
cursor: pointer;
color: #475569;
}
.logout-btn:hover {
background: #f1f5f9;
}
.user-greeting {
background: #e6f0ff;
border-radius: 40px;
padding: 12px;
text-align: center;
font-weight: 500;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="card" id="app">
<h2>🔐 Добро пожаловать</h2>
<div class="sub">войдите или создайте аккаунт</div>
<!-- Динамический контент будет отображаться через JS -->
<div id="dynamicContent"></div>
</div>
<script>
(function() {
// Ключ для хранения пользователей в localStorage
const USERS_STORAGE_KEY = 'fake_auth_users';
const SESSION_KEY = 'current_logged_in_user';
// ---- Вспомогательные функции работы с хранилищем ----
function getUsers() {
const raw = localStorage.getItem(USERS_STORAGE_KEY);
if (!raw) return [];
try {
return JSON.parse(raw);
} catch(e) {
return [];
}
}
function saveUsers(users) {
localStorage.setItem(USERS_STORAGE_KEY, JSON.stringify(users));
}
// Проверка существования логина (регистрация)
function isLoginTaken(login) {
const users = getUsers();
return users.some(user => user.login.toLowerCase() === login.toLowerCase());
}
// Регистрация нового пользователя
function registerUser(login, password) {
if (!login || !password) return { success: false, message: 'Заполните оба поля' };
if (login.length < 3) return { success: false, message: 'Логин должен быть не менее 3 символов' };
if (password.length < 4) return { success: false, message: 'Пароль не менее 4 символов' };
if (isLoginTaken(login)) return { success: false, message: 'Пользователь с таким логином уже существует' };
const users = getUsers();
const newUser = {
id: Date.now(),
login: login.trim(),
password: password.trim() // в демо-примере храним открыто, для реального проекта нужен хеш
};
users.push(newUser);
saveUsers(users);
return { success: true, message: 'Регистрация успешна! Теперь войдите.' };
}
// Авторизация (вход)
function loginUser(login, password) {
if (!login || !password) return { success: false, message: 'Введите логин и пароль' };
const users = getUsers();
const found = users.find(user => user.login === login.trim() && user.password === password.trim());
if (found) {
// сохраняем сессию
localStorage.setItem(SESSION_KEY, JSON.stringify({ login: found.login, id: found.id }));
return { success: true, message: `Добро пожаловать, ${found.login}!` };
} else {
return { success: false, message: 'Неверный логин или пароль' };
}
}
// Выход из аккаунта
function logout() {
localStorage.removeItem(SESSION_KEY);
renderApp(); // перерисовать интерфейс (покажем формы)
}
// Получить текущего залогиненного пользователя
function getCurrentUser() {
const raw = localStorage.getItem(SESSION_KEY);
if (!raw) return null;
try {
return JSON.parse(raw);
} catch(e) {
return null;
}
}
// ---- Рендер всей UI в зависимости от состояния аутентификации ----
function renderApp() {
const container = document.getElementById('dynamicContent');
if (!container) return;
const currentUser = getCurrentUser();
// Если пользователь уже залогинен — показываем приветствие + кнопку выхода
if (currentUser) {
container.innerHTML = `
<div class="user-greeting">
✅ Вы вошли как <strong>${escapeHtml(currentUser.login)}</strong>
</div>
<div class="logout-area">
<button class="logout-btn" id="logoutBtn">🚪 Выйти из аккаунта</button>
</div>
`;
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) logoutBtn.addEventListener('click', logout);
return;
}
// Иначе рисуем формы с переключением (регистрация / вход)
container.innerHTML = `
<div class="tabs">
<button class="tab-btn active" data-tab="login">Вход</button>
<button class="tab-btn" data-tab="register">Регистрация</button>
</div>
<!-- Форма входа (по умолчанию активна) -->
<div id="loginFormContainer">
<form id="loginForm">
<div class="form-group">
<label>📧 Логин</label>
<input type="text" id="loginUsername" placeholder="Введите логин" autocomplete="username">
</div>
<div class="form-group">
<label>🔒 Пароль</label>
<input type="password" id="loginPassword" placeholder="Введите пароль" autocomplete="current-password">
</div>
<button type="submit">Войти</button>
</form>
</div>
<!-- Форма регистрации (скрыта изначально) -->
<div id="registerFormContainer" style="display: none;">
<form id="registerForm">
<div class="form-group">
<label>👤 Логин (мин. 3 символа)</label>
<input type="text" id="regUsername" placeholder="Придумайте логин" autocomplete="off">
</div>
<div class="form-group">
<label>🔐 Пароль (мин. 4 символа)</label>
<input type="password" id="regPassword" placeholder="Придумайте пароль" autocomplete="new-password">
</div>
<button type="submit">Зарегистрироваться</button>
</form>
</div>
<div id="globalMessage" class="message"></div>
`;
// ----- Логика переключения табов -----
const tabBtns = document.querySelectorAll('.tab-btn');
const loginContainer = document.getElementById('loginFormContainer');
const registerContainer = document.getElementById('registerFormContainer');
const messageDiv = document.getElementById('globalMessage');
function clearMessage() {
if (messageDiv) {
messageDiv.textContent = '';
messageDiv.className = 'message';
}
}
function showMessage(text, type = 'error') {
if (messageDiv) {
messageDiv.textContent = text;
messageDiv.className = `message ${type}`;
setTimeout(() => {
if (messageDiv.textContent === text) {
// не удаляем сообщение если оно не поменялось, но через 4 секунды очистим
setTimeout(() => {
if (messageDiv.textContent === text) clearMessage();
}, 3000);
}
}, 200);
}
}
function setActiveTab(tabId) {
tabBtns.forEach(btn => {
if (btn.dataset.tab === tabId) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
if (tabId === 'login') {
loginContainer.style.display = 'block';
registerContainer.style.display = 'none';
} else {
loginContainer.style.display = 'none';
registerContainer.style.display = 'block';
}
clearMessage();
}
tabBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
const tab = btn.dataset.tab;
if (tab === 'login') setActiveTab('login');
else if (tab === 'register') setActiveTab('register');
});
});
// ---- Обработчик ВХОДА ----
const loginForm = document.getElementById('loginForm');
if (loginForm) {
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
const usernameInput = document.getElementById('loginUsername');
const passwordInput = document.getElementById('loginPassword');
const login = usernameInput ? usernameInput.value.trim() : '';
const password = passwordInput ? passwordInput.value : '';
const result = loginUser(login, password);
if (result.success) {
showMessage(result.message, 'success');
// Перерисовываем интерфейс через полсекунды, чтобы показать сообщение
setTimeout(() => {
renderApp();
}, 800);
} else {
showMessage(result.message, 'error');
}
});
}
// ---- Обработчик РЕГИСТРАЦИИ ----
const registerForm = document.getElementById('registerForm');
if (registerForm) {
registerForm.addEventListener('submit', (e) => {
e.preventDefault();
const usernameInput = document.getElementById('regUsername');
const passwordInput = document.getElementById('regPassword');
const login = usernameInput ? usernameInput.value.trim() : '';
const password = passwordInput ? passwordInput.value : '';
const result = registerUser(login, password);
if (result.success) {
showMessage(result.message, 'success');
// Очищаем поля регистрации
if (usernameInput) usernameInput.value = '';
if (passwordInput) passwordInput.value = '';
// Переключаем на вкладку "Вход" через секунду
setTimeout(() => {
setActiveTab('login');
// дополнительно очищаем поля входа, чтобы было удобно
const loginUserField = document.getElementById('loginUsername');
const loginPassField = document.getElementById('loginPassword');
if (loginUserField) loginUserField.value = login;
if (loginPassField) loginPassField.value = '';
}, 1000);
} else {
showMessage(result.message, 'error');
}
});
}
// Установим активный таб по умолчанию (вход)
setActiveTab('login');
}
// простой escape для защиты от XSS
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&';
if (m === '<') return '<';
if (m === '>') return '>';
return m;
}).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, function(c) {
return c;
});
}
// Старт приложения
renderApp();
})();
</script>
</body>
</html>