проверка активности клиета и изменение своего профиля
This commit is contained in:
@@ -19,7 +19,7 @@ function hideLoader() {
|
||||
}
|
||||
|
||||
export async function apiRequest(url, payload = {}, method = 'POST') {
|
||||
method = method.toUpperCase();
|
||||
method = typeof method === 'string' ? method.toUpperCase() : 'POST';
|
||||
|
||||
let finalUrl = url;
|
||||
let options = {
|
||||
|
||||
@@ -26,6 +26,9 @@ export async function setCookie(name, value, days = 180) {
|
||||
const expires = new Date(Date.now() + days * 864e5).toUTCString();
|
||||
const encodedName = encodeURIComponent(name);
|
||||
|
||||
document.cookie = `${encodedName}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;`;
|
||||
document.cookie = `${encodedName}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; Secure;`;
|
||||
|
||||
// ---------- 1. Пытаемся установить безопасную куку ----------
|
||||
let secureCookie = `${encodedName}=${cookieValue}; expires=${expires}; path=/; Secure; SameSite=Lax`;
|
||||
document.cookie = secureCookie;
|
||||
@@ -45,6 +48,7 @@ export async function setCookie(name, value, days = 180) {
|
||||
return false; // безопасную куку установить не удалось
|
||||
}
|
||||
|
||||
|
||||
export async function getCookie(name) {
|
||||
const cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||
|
||||
|
||||
@@ -67,6 +67,19 @@ const predefinedSpecs = {
|
||||
async function getCookieData() {
|
||||
accessData = await getCookie('toolbox_access');
|
||||
userData = await getCookie('toolbox_user');
|
||||
await checkActiveUser();
|
||||
}
|
||||
|
||||
async function checkActiveUser() {
|
||||
const activeCookie = loadFromStorage('active');
|
||||
if (!activeCookie || !activeCookie.active || !activeCookie.datetime || Date.now() - activeCookie.datetime > 12 * 60 * 60 * 1000) {
|
||||
const checkActive = await apiRequest('/user/check', { userId: userData.id });
|
||||
if (checkActive.status === 'ok') {
|
||||
saveToStorage('active', { active: true, datetime: Date.now() });
|
||||
} else {
|
||||
window.clientManager?.initLogout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function openTab(event, tabId) {
|
||||
@@ -7209,6 +7222,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
|
||||
if (!accessData || !userData) {
|
||||
console.warn('Access data or user data not found');
|
||||
console.log(accessData, userData);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+227
-38
@@ -1,4 +1,5 @@
|
||||
import { getCookie, deleteCookie } from '/static/js/cookies.js';
|
||||
import { apiRequest } from './api.js';
|
||||
import { getCookie, deleteCookie, setCookie } from './cookies.js';
|
||||
|
||||
class ClientManager {
|
||||
constructor() {
|
||||
@@ -13,11 +14,9 @@ class ClientManager {
|
||||
this.userData = await getCookie('toolbox_user');
|
||||
this.accessData = await getCookie('toolbox_access');
|
||||
|
||||
|
||||
if (!this.userData || !this.accessData) {
|
||||
console.warn('User data or access data not found in cookie');
|
||||
this.clearUserCookie();
|
||||
window.location.href = '/user/login';
|
||||
this.initLogout();
|
||||
}
|
||||
|
||||
// Вставляем данные пользователя в DOM
|
||||
@@ -35,8 +34,7 @@ class ClientManager {
|
||||
|
||||
renderUserInfo() {
|
||||
if (!this.userData) {
|
||||
// Если нет данных, показываем заглушку
|
||||
this.renderFallbackUser();
|
||||
this.initLogout();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,37 +62,6 @@ class ClientManager {
|
||||
|
||||
}
|
||||
|
||||
renderFallbackUser() {
|
||||
const userCard = document.querySelector('.client-card');
|
||||
if (userCard) {
|
||||
userCard.innerHTML = `
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="position-relative">
|
||||
<img src="images/users/default.png"
|
||||
alt="Гость"
|
||||
class="client-avatar">
|
||||
<span class="position-absolute bottom-0 end-0 bg-warning border border-2 border-white rounded-circle p-1"></span>
|
||||
</div>
|
||||
<div class="me-3">
|
||||
<h6 class="client-name mb-1">Гость</h6>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning fs-7 px-2 py-1 me-2">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>
|
||||
Не авторизован
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/login" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-box-arrow-in-right me-1"></i>
|
||||
Войти
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
initLogoutHandler() {
|
||||
const logoutBtn = document.getElementById('clientLogoutBtn');
|
||||
if (logoutBtn) {
|
||||
@@ -105,12 +72,18 @@ class ClientManager {
|
||||
}
|
||||
}
|
||||
|
||||
initLogout() {
|
||||
const logoutBtn = document.getElementById('clientLogoutBtn');
|
||||
if (logoutBtn) {
|
||||
this.handleLogout(logoutBtn);
|
||||
}
|
||||
}
|
||||
|
||||
handleLogout(button) {
|
||||
// Анимация нажатия
|
||||
button.style.transform = 'scale(0.95)';
|
||||
|
||||
// Добавляем иконку загрузки
|
||||
const originalContent = button.innerHTML;
|
||||
button.innerHTML = `
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
Выход...
|
||||
@@ -162,6 +135,220 @@ class ClientManager {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
userProfile() {
|
||||
const avatarBtn = document.getElementById('userProfile');
|
||||
|
||||
async function openEditUserModal(user = null) {
|
||||
const isNew = !user;
|
||||
const modalId = `profile-edit-user-modal`;
|
||||
|
||||
// Удаляем старую модалку, если есть
|
||||
const existingModal = document.getElementById(modalId);
|
||||
if (existingModal) {
|
||||
existingModal.remove();
|
||||
}
|
||||
|
||||
const modalHTML = `
|
||||
<div class="modal fade" id="${modalId}" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">${isNew ? 'Добавить пользователя' : 'Редактировать пользователя'}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="${modalId}-form">
|
||||
<div class="text-center mb-3">
|
||||
<img src="${user?.photo || 'static/images/users/default.png'}"
|
||||
alt="Фото пользователя"
|
||||
class="rounded-circle border object-fit-cover"
|
||||
width="100" height="100"
|
||||
id="${modalId}-photo-preview"
|
||||
onerror="this.src='static/images/users/default.png'">
|
||||
<div class="mt-2">
|
||||
<input type="file"
|
||||
class="form-control form-control-sm"
|
||||
id="${modalId}-photo-input"
|
||||
accept="image/*"
|
||||
style="display: none;">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<button type="button" class="btn btn-outline-primary" id="${modalId}-change-photo-btn">
|
||||
<i class="bi bi-upload"></i> Изменить
|
||||
</button>
|
||||
${user?.photo && !user.photo.includes('default.png') ? `
|
||||
<button type="button" class="btn btn-outline-danger" id="${modalId}-remove-photo-btn">
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="${modalId}-login" class="form-label">Логин *</label>
|
||||
<input type="text" class="form-control" id="${modalId}-login"
|
||||
value="${user?.login || ''}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="${modalId}-username" class="form-label">Имя пользователя *</label>
|
||||
<input type="text" class="form-control" id="${modalId}-username"
|
||||
value="${user?.username || ''}" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="${modalId}-password" class="form-label">${isNew ? 'Пароль *' : 'Новый пароль'}</label>
|
||||
<input type="password" class="form-control" id="${modalId}-password"
|
||||
${isNew ? 'required' : ''}
|
||||
placeholder="${isNew ? '' : 'Оставьте пустым, если не нужно менять'}">
|
||||
${!isNew ? '<div class="form-text">Оставьте пустым, если не нужно менять пароль</div>' : ''}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<div id="${modalId}-error-message" class="alert alert-danger mb-3" role="alert" hidden></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="button" class="btn btn-primary" id="${modalId}-save-btn">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||
const modal = new bootstrap.Modal(document.getElementById(modalId));
|
||||
let photoFile = null;
|
||||
let removePhoto = false;
|
||||
|
||||
// Обработчики для фото
|
||||
const changePhotoBtn = document.getElementById(`${modalId}-change-photo-btn`);
|
||||
const photoInput = document.getElementById(`${modalId}-photo-input`);
|
||||
const photoPreview = document.getElementById(`${modalId}-photo-preview`);
|
||||
|
||||
changePhotoBtn.addEventListener('click', () => {
|
||||
photoInput.click();
|
||||
});
|
||||
|
||||
photoInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
if (file.type.startsWith('image/')) {
|
||||
photoFile = file;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
photoPreview.src = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
removePhoto = false;
|
||||
} else {
|
||||
showConfirmationModal('Ошибка', 'Пожалуйста, выберите файл изображения', () => { });
|
||||
photoInput.value = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (document.getElementById(`${modalId}-remove-photo-btn`)) {
|
||||
document.getElementById(`${modalId}-remove-photo-btn`).addEventListener('click', () => {
|
||||
photoPreview.src = 'static/images/users/default.png';
|
||||
photoFile = null;
|
||||
removePhoto = true;
|
||||
});
|
||||
}
|
||||
|
||||
// Обработчик сохранения
|
||||
document.getElementById(`${modalId}-save-btn`).addEventListener('click', async () => {
|
||||
const form = document.getElementById(`${modalId}-form`);
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
return;
|
||||
}
|
||||
|
||||
const formUserData = {
|
||||
login: document.getElementById(`${modalId}-login`).value.trim(),
|
||||
username: document.getElementById(`${modalId}-username`).value.trim(),
|
||||
};
|
||||
|
||||
// Добавляем пароль, если он указан
|
||||
const password = document.getElementById(`${modalId}-password`).value;
|
||||
if (password || isNew) {
|
||||
formUserData.password = password || '';
|
||||
}
|
||||
|
||||
// Определяем действие
|
||||
let action = isNew ? 'create' : 'update';
|
||||
|
||||
// Обработка фото
|
||||
if (photoFile) {
|
||||
if (photoFile.size > 5 * 1024 * 1024) {
|
||||
showInfo('Фото больше 5 МБ, оно не подходит', 'warning');
|
||||
return;
|
||||
}
|
||||
formUserData.photo = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = () => reject(new Error('Ошибка чтения файла'));
|
||||
|
||||
reader.readAsDataURL(photoFile);
|
||||
});
|
||||
|
||||
} else if (removePhoto) {
|
||||
formUserData.photo = '';
|
||||
}
|
||||
|
||||
let changedUserData = {};
|
||||
if (isNew) {
|
||||
changedUserData = formUserData;
|
||||
} else {
|
||||
changedUserData = Object.keys(formUserData).reduce((acc, key) => {
|
||||
if (formUserData[key] !== user[key]) {
|
||||
acc[key] = formUserData[key];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
if (Object.keys(changedUserData).length === 0) {
|
||||
showInfo('Нет изменений', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
// Обновляем данные
|
||||
if (!isNew) {
|
||||
changedUserData.id = user.id;
|
||||
}
|
||||
|
||||
const result = await apiRequest('/user/', { action, userData: changedUserData, userId: user.id });
|
||||
|
||||
if (result && result.status === 'ok') {
|
||||
const newUserData = await apiRequest('/user/', { userId: user.id }, 'get');
|
||||
await setCookie('toolbox_user', JSON.stringify(newUserData));
|
||||
const checkData = await getCookie('toolbox_user');
|
||||
if (window.clientManager) {
|
||||
window.clientManager.userData = newUserData;
|
||||
window.clientManager.renderUserInfo();
|
||||
}
|
||||
if (modal) { modal.hide(); }
|
||||
} else {
|
||||
const errorMessageDiv = document.getElementById(`${modalId}-error-message`);
|
||||
errorMessageDiv.textContent = result.message;
|
||||
errorMessageDiv.hidden = false;
|
||||
}
|
||||
});
|
||||
|
||||
modal.show();
|
||||
|
||||
// Удаляем модалку после скрытия
|
||||
modal._element.addEventListener('hidden.bs.modal', () => {
|
||||
modal._element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
avatarBtn.addEventListener('click', async () => {
|
||||
await openEditUserModal(this.userData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация при загрузке документа
|
||||
@@ -174,6 +361,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
elements.forEach((el, index) => {
|
||||
el.style.animationDelay = `${index * 0.1}s`;
|
||||
});
|
||||
|
||||
window.clientManager.userProfile();
|
||||
});
|
||||
|
||||
// Экспорт для использования в других модулях
|
||||
|
||||
Reference in New Issue
Block a user