feat: новая функция
This commit is contained in:
@@ -19,12 +19,11 @@ const authType = ref(null);
|
||||
const userId = ref(null);
|
||||
const address = ref(null);
|
||||
const telegramId = ref(null);
|
||||
const isAdmin = ref(false);
|
||||
const email = ref(null);
|
||||
const processedGuestIds = ref([]);
|
||||
const identities = ref([]);
|
||||
const tokenBalances = ref([]);
|
||||
const userAccessLevel = ref({ level: 'user', tokenCount: 0, hasAccess: false });
|
||||
const userAccessLevel = ref({ level: 'guest', tokenCount: 0, hasAccess: false });
|
||||
|
||||
// Функция для обновления списка идентификаторов
|
||||
const updateIdentities = async () => {
|
||||
@@ -134,8 +133,8 @@ const updateAuth = async ({
|
||||
userId: newUserId,
|
||||
address: newAddress,
|
||||
telegramId: newTelegramId,
|
||||
isAdmin: newIsAdmin,
|
||||
email: newEmail,
|
||||
userAccessLevel: newUserAccessLevel,
|
||||
}) => {
|
||||
const wasAuthenticated = isAuthenticated.value;
|
||||
const previousUserId = userId.value;
|
||||
@@ -146,8 +145,8 @@ const updateAuth = async ({
|
||||
newUserId,
|
||||
newAddress,
|
||||
newTelegramId,
|
||||
newIsAdmin,
|
||||
newEmail,
|
||||
newUserAccessLevel,
|
||||
});
|
||||
|
||||
// Убедимся, что переменные являются реактивными
|
||||
@@ -156,8 +155,31 @@ const updateAuth = async ({
|
||||
userId.value = newUserId || null;
|
||||
address.value = newAddress || null;
|
||||
telegramId.value = newTelegramId || null;
|
||||
isAdmin.value = newIsAdmin === true;
|
||||
email.value = newEmail || null;
|
||||
|
||||
// Обновляем userAccessLevel только если он изменился
|
||||
if (newUserAccessLevel) {
|
||||
// Используем userAccessLevel из ответа сервера
|
||||
console.log('[updateAuth] Setting userAccessLevel from server:', JSON.stringify(newUserAccessLevel, null, 2));
|
||||
userAccessLevel.value = newUserAccessLevel;
|
||||
} else if (authenticated && newAddress) {
|
||||
// Если userAccessLevel не передан, но пользователь аутентифицирован, запрашиваем его
|
||||
try {
|
||||
const accessLevel = await checkUserAccessLevel(newAddress);
|
||||
if (accessLevel && accessLevel.level !== userAccessLevel.value.level) {
|
||||
console.log('[updateAuth] Updating userAccessLevel from API:', accessLevel);
|
||||
userAccessLevel.value = accessLevel;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating userAccessLevel in updateAuth:', error);
|
||||
}
|
||||
} else if (!authenticated) {
|
||||
// Сбрасываем userAccessLevel для неавторизованных пользователей
|
||||
if (userAccessLevel.value.level !== 'guest') {
|
||||
console.log('[updateAuth] Resetting userAccessLevel to guest');
|
||||
userAccessLevel.value = { level: 'guest', tokenCount: 0, hasAccess: false };
|
||||
}
|
||||
}
|
||||
|
||||
// Кэшируем данные аутентификации
|
||||
localStorage.setItem(
|
||||
@@ -168,7 +190,6 @@ const updateAuth = async ({
|
||||
userId: newUserId,
|
||||
address: newAddress,
|
||||
telegramId: newTelegramId,
|
||||
isAdmin: newIsAdmin,
|
||||
email: newEmail,
|
||||
})
|
||||
);
|
||||
@@ -204,8 +225,34 @@ const updateAuth = async ({
|
||||
address: address.value,
|
||||
telegramId: telegramId.value,
|
||||
email: email.value,
|
||||
isAdmin: isAdmin.value,
|
||||
});
|
||||
|
||||
// Уведомляем все компоненты об изменении состояния аутентификации
|
||||
// Только если состояние действительно изменилось
|
||||
if (wasAuthenticated !== isAuthenticated.value || previousUserId !== newUserId) {
|
||||
// Централизованная очистка данных при отключении
|
||||
if (!isAuthenticated.value && wasAuthenticated) {
|
||||
console.log('[useAuth] User logged out, clearing application data');
|
||||
// Очищаем глобальные данные приложения
|
||||
window.dispatchEvent(new CustomEvent('clear-application-data'));
|
||||
}
|
||||
|
||||
// Централизованное обновление данных при подключении
|
||||
if (isAuthenticated.value && !wasAuthenticated) {
|
||||
console.log('[useAuth] User logged in, refreshing application data');
|
||||
window.dispatchEvent(new CustomEvent('refresh-application-data'));
|
||||
}
|
||||
|
||||
window.dispatchEvent(new CustomEvent('auth-state-changed', {
|
||||
detail: {
|
||||
authenticated: isAuthenticated.value,
|
||||
authType: authType.value,
|
||||
userId: userId.value,
|
||||
address: address.value,
|
||||
userAccessLevel: userAccessLevel.value
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Если пользователь только что аутентифицировался или сменил аккаунт,
|
||||
// пробуем связать сообщения
|
||||
@@ -314,22 +361,34 @@ const linkMessages = async () => {
|
||||
const checkAuth = async () => {
|
||||
try {
|
||||
const response = await axios.get('/auth/check');
|
||||
console.log('Auth check response:', response.data);
|
||||
console.log('Auth check response:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
const wasAuthenticated = isAuthenticated.value;
|
||||
const previousUserId = userId.value;
|
||||
const previousAuthType = authType.value;
|
||||
|
||||
// Обновляем данные авторизации через updateAuth вместо прямого изменения
|
||||
await updateAuth({
|
||||
authenticated: response.data.authenticated,
|
||||
authType: response.data.authType,
|
||||
userId: response.data.userId,
|
||||
address: response.data.address,
|
||||
telegramId: response.data.telegramId,
|
||||
email: response.data.email,
|
||||
isAdmin: response.data.isAdmin,
|
||||
});
|
||||
// Проверяем, изменилось ли состояние аутентификации
|
||||
const authChanged = (
|
||||
wasAuthenticated !== response.data.authenticated ||
|
||||
previousUserId !== response.data.userId ||
|
||||
previousAuthType !== response.data.authType
|
||||
);
|
||||
|
||||
if (authChanged) {
|
||||
console.log('[checkAuth] Authentication state changed, updating...');
|
||||
// Обновляем данные авторизации через updateAuth вместо прямого изменения
|
||||
await updateAuth({
|
||||
authenticated: response.data.authenticated,
|
||||
authType: response.data.authType,
|
||||
userId: response.data.userId,
|
||||
address: response.data.address,
|
||||
telegramId: response.data.telegramId,
|
||||
email: response.data.email,
|
||||
userAccessLevel: response.data.userAccessLevel, // Добавляем userAccessLevel из ответа сервера
|
||||
});
|
||||
} else {
|
||||
console.log('[checkAuth] No authentication changes, skipping update');
|
||||
}
|
||||
|
||||
// Если пользователь аутентифицирован, обновляем список идентификаторов и связываем сообщения
|
||||
if (response.data.authenticated) {
|
||||
@@ -385,7 +444,6 @@ const disconnect = async () => {
|
||||
address: null,
|
||||
telegramId: null,
|
||||
email: null,
|
||||
isAdmin: false,
|
||||
});
|
||||
|
||||
// Обновляем отображение отключенного состояния
|
||||
@@ -399,7 +457,6 @@ const disconnect = async () => {
|
||||
localStorage.removeItem('isAuthenticated');
|
||||
localStorage.removeItem('userId');
|
||||
localStorage.removeItem('address');
|
||||
localStorage.removeItem('isAdmin');
|
||||
localStorage.removeItem('guestId');
|
||||
localStorage.removeItem('guestMessages');
|
||||
localStorage.removeItem('telegramId');
|
||||
@@ -507,7 +564,6 @@ const authApi = {
|
||||
authType,
|
||||
userId,
|
||||
address,
|
||||
isAdmin,
|
||||
telegramId,
|
||||
email,
|
||||
identities,
|
||||
|
||||
@@ -512,12 +512,29 @@ export function useChat(auth) {
|
||||
|
||||
// Подключаем WebSocket если пользователь уже аутентифицирован
|
||||
setupChatWebSocket();
|
||||
|
||||
// Логика обновления данных централизована в useAuth.js
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanupWebSocket();
|
||||
});
|
||||
|
||||
// Подписываемся на централизованные события очистки и обновления данных
|
||||
window.addEventListener('clear-application-data', () => {
|
||||
console.log('[useChat] Clearing chat data');
|
||||
// Очищаем данные при выходе из системы
|
||||
messages.value = [];
|
||||
newMessages.value = [];
|
||||
readUserIds.value = [];
|
||||
lastReadMessageDate.value = {};
|
||||
});
|
||||
|
||||
window.addEventListener('refresh-application-data', () => {
|
||||
console.log('[useChat] Refreshing chat data');
|
||||
loadMessages({ initial: true }); // Обновляем данные при входе в систему
|
||||
});
|
||||
|
||||
return {
|
||||
messages,
|
||||
newMessage, // v-model
|
||||
|
||||
@@ -63,21 +63,42 @@ export function useContactsAndMessagesWebSocket() {
|
||||
}
|
||||
|
||||
function updateNewContacts() {
|
||||
console.log('[useContactsWebSocket] updateNewContacts called');
|
||||
console.log('[useContactsWebSocket] contacts:', contacts.value.length);
|
||||
console.log('[useContactsWebSocket] readContacts:', readContacts.value);
|
||||
|
||||
if (!contacts.value.length) {
|
||||
newContacts.value = [];
|
||||
console.log('[useContactsWebSocket] No contacts, newContacts cleared');
|
||||
return;
|
||||
}
|
||||
newContacts.value = contacts.value.filter(c => !readContacts.value.includes(c.id));
|
||||
|
||||
const beforeCount = newContacts.value.length;
|
||||
newContacts.value = contacts.value.filter(c => !readContacts.value.includes(String(c.id)));
|
||||
console.log('[useContactsWebSocket] newContacts updated:', beforeCount, '->', newContacts.value.length);
|
||||
}
|
||||
|
||||
async function markContactAsRead(contactId) {
|
||||
try {
|
||||
await axios.post('/users/mark-contact-read', { contactId });
|
||||
if (!readContacts.value.includes(contactId)) {
|
||||
readContacts.value.push(contactId);
|
||||
console.log('[useContactsWebSocket] Marking contact as read:', contactId);
|
||||
const response = await axios.post('/users/mark-contact-read', { contactId });
|
||||
console.log('[useContactsWebSocket] Mark contact response:', response.data);
|
||||
|
||||
// Приводим contactId к строке для совместимости с readContacts
|
||||
const contactIdStr = String(contactId);
|
||||
console.log('[useContactsWebSocket] Converting contactId to string:', contactId, '->', contactIdStr);
|
||||
|
||||
if (!readContacts.value.includes(contactIdStr)) {
|
||||
readContacts.value.push(contactIdStr);
|
||||
updateNewContacts();
|
||||
console.log('[useContactsWebSocket] Contact marked as read, updated newContacts');
|
||||
} else {
|
||||
console.log('[useContactsWebSocket] Contact already marked as read:', contactIdStr);
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
console.error('[useContactsWebSocket] Error marking contact as read:', e);
|
||||
console.error('[useContactsWebSocket] Error response:', e.response?.data);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchReadStatus() {
|
||||
@@ -139,17 +160,41 @@ export function useContactsAndMessagesWebSocket() {
|
||||
};
|
||||
}
|
||||
|
||||
function clearContactsData() {
|
||||
contacts.value = [];
|
||||
messages.value = [];
|
||||
readContacts.value = [];
|
||||
newContacts.value = [];
|
||||
newMessages.value = [];
|
||||
readUserIds.value = [];
|
||||
lastReadMessageDate.value = {};
|
||||
}
|
||||
|
||||
// Централизованная подписка на изменения аутентификации
|
||||
onMounted(async () => {
|
||||
await fetchContactsReadStatus();
|
||||
await fetchContacts();
|
||||
await fetchReadStatus();
|
||||
await fetchMessages();
|
||||
setupWebSocket();
|
||||
|
||||
// Подписываемся на централизованные события очистки и обновления данных
|
||||
window.addEventListener('clear-application-data', () => {
|
||||
console.log('[useContactsWebSocket] Clearing contacts data');
|
||||
clearContactsData(); // Очищаем данные при выходе из системы
|
||||
});
|
||||
|
||||
window.addEventListener('refresh-application-data', () => {
|
||||
console.log('[useContactsWebSocket] Refreshing contacts data');
|
||||
fetchContacts(); // Обновляем данные при входе в систему
|
||||
});
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (ws) ws.close();
|
||||
});
|
||||
|
||||
// Логика обновления данных централизована в useAuth.js через события
|
||||
|
||||
return {
|
||||
contacts,
|
||||
newContacts,
|
||||
@@ -158,6 +203,8 @@ export function useContactsAndMessagesWebSocket() {
|
||||
markContactAsRead,
|
||||
markMessagesAsRead,
|
||||
markMessagesAsReadForUser,
|
||||
readUserIds
|
||||
readUserIds,
|
||||
fetchContacts,
|
||||
clearContactsData
|
||||
};
|
||||
}
|
||||
@@ -12,69 +12,77 @@
|
||||
|
||||
import { computed } from 'vue';
|
||||
import { useAuthContext } from './useAuth';
|
||||
import { PERMISSIONS, ROLES, hasPermission as checkPermission, getRoleDescription } from '/app/shared/permissions';
|
||||
|
||||
/**
|
||||
* Composable для работы с правами доступа
|
||||
* Использует единую матрицу прав из shared/permissions.js
|
||||
* @returns {Object} - Объект с функциями для проверки прав доступа
|
||||
*/
|
||||
export function usePermissions() {
|
||||
const { userAccessLevel, isAdmin } = useAuthContext();
|
||||
const { userAccessLevel, isAuthenticated } = useAuthContext();
|
||||
|
||||
/**
|
||||
* Проверяет, может ли пользователь только читать данные
|
||||
* Текущая роль пользователя
|
||||
*/
|
||||
const canRead = computed(() => {
|
||||
return (userAccessLevel.value && userAccessLevel.value.hasAccess) || isAdmin.value;
|
||||
const currentRole = computed(() => {
|
||||
if (!isAuthenticated.value) {
|
||||
return ROLES.GUEST; // Неавторизованный
|
||||
}
|
||||
|
||||
// Если userAccessLevel не определен, возвращаем USER (авторизованный пользователь)
|
||||
return userAccessLevel.value?.level || ROLES.USER;
|
||||
});
|
||||
|
||||
/**
|
||||
* Проверяет, может ли пользователь редактировать данные
|
||||
*/
|
||||
const canEdit = computed(() => {
|
||||
return userAccessLevel.value && userAccessLevel.value.level === 'editor';
|
||||
});
|
||||
|
||||
/**
|
||||
* Проверяет, может ли пользователь удалять данные
|
||||
*/
|
||||
const canDelete = computed(() => {
|
||||
return userAccessLevel.value && userAccessLevel.value.level === 'editor';
|
||||
});
|
||||
|
||||
/**
|
||||
* Проверяет, может ли пользователь управлять настройками системы
|
||||
*/
|
||||
const canManageSettings = computed(() => {
|
||||
return userAccessLevel.value && userAccessLevel.value.level === 'editor';
|
||||
});
|
||||
|
||||
/**
|
||||
* Получает текущий уровень доступа
|
||||
*/
|
||||
const currentLevel = computed(() => {
|
||||
return userAccessLevel.value ? userAccessLevel.value.level : 'user';
|
||||
});
|
||||
|
||||
/**
|
||||
* Получает количество токенов пользователя
|
||||
* Количество токенов пользователя
|
||||
*/
|
||||
const tokenCount = computed(() => {
|
||||
return userAccessLevel.value ? userAccessLevel.value.tokenCount : 0;
|
||||
return userAccessLevel.value?.tokenCount || 0;
|
||||
});
|
||||
|
||||
/**
|
||||
* Универсальная проверка любого права
|
||||
* @param {string} permission - Право для проверки
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasPermission = (permission) => {
|
||||
return checkPermission(currentRole.value, permission);
|
||||
};
|
||||
|
||||
// ========================================================================
|
||||
// Computed проверки для частого использования
|
||||
// ========================================================================
|
||||
|
||||
// Просмотр данных
|
||||
const canViewData = computed(() => hasPermission(PERMISSIONS.VIEW_DATA));
|
||||
const canViewContacts = computed(() => hasPermission(PERMISSIONS.VIEW_CONTACTS));
|
||||
const canViewCrm = computed(() => hasPermission(PERMISSIONS.VIEW_CRM));
|
||||
|
||||
// Редактирование и удаление
|
||||
const canEditData = computed(() => hasPermission(PERMISSIONS.EDIT_USER_DATA));
|
||||
const canEditContacts = computed(() => hasPermission(PERMISSIONS.EDIT_CONTACTS));
|
||||
const canDeleteData = computed(() => hasPermission(PERMISSIONS.DELETE_USER_DATA));
|
||||
const canDeleteMessages = computed(() => hasPermission(PERMISSIONS.DELETE_MESSAGES));
|
||||
|
||||
// Коммуникация
|
||||
const canSendToUsers = computed(() => hasPermission(PERMISSIONS.SEND_TO_USERS));
|
||||
const canChatWithAdmins = computed(() => hasPermission(PERMISSIONS.CHAT_WITH_ADMINS));
|
||||
const canGenerateAI = computed(() => hasPermission(PERMISSIONS.GENERATE_AI_REPLIES));
|
||||
const canBroadcast = computed(() => hasPermission(PERMISSIONS.BROADCAST));
|
||||
|
||||
// Управление
|
||||
const canManageTags = computed(() => hasPermission(PERMISSIONS.MANAGE_TAGS));
|
||||
const canBlockUsers = computed(() => hasPermission(PERMISSIONS.BLOCK_USERS));
|
||||
const canManageSettings = computed(() => hasPermission(PERMISSIONS.MANAGE_SETTINGS));
|
||||
|
||||
const currentLevel = computed(() => currentRole.value);
|
||||
|
||||
/**
|
||||
* Получает описание текущего уровня доступа
|
||||
*/
|
||||
const getLevelDescription = (level) => {
|
||||
switch (level) {
|
||||
case 'readonly':
|
||||
return 'Только чтение';
|
||||
case 'editor':
|
||||
return 'Редактор';
|
||||
case 'user':
|
||||
default:
|
||||
return 'Пользователь';
|
||||
}
|
||||
return getRoleDescription(level);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -82,24 +90,56 @@ export function usePermissions() {
|
||||
*/
|
||||
const getLevelClass = (level) => {
|
||||
switch (level) {
|
||||
case 'readonly':
|
||||
case ROLES.READONLY:
|
||||
return 'access-readonly';
|
||||
case 'editor':
|
||||
case ROLES.EDITOR:
|
||||
return 'access-editor';
|
||||
case 'user':
|
||||
case ROLES.USER:
|
||||
return 'access-user';
|
||||
case ROLES.GUEST:
|
||||
return 'access-guest';
|
||||
default:
|
||||
return 'access-user';
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
canRead,
|
||||
canEdit,
|
||||
canDelete,
|
||||
canManageSettings,
|
||||
currentLevel,
|
||||
// Главная функция
|
||||
hasPermission,
|
||||
|
||||
// Информация о роли
|
||||
currentRole,
|
||||
currentLevel, // alias для совместимости
|
||||
tokenCount,
|
||||
|
||||
// Просмотр
|
||||
canViewData,
|
||||
canViewContacts,
|
||||
canViewCrm,
|
||||
|
||||
// Редактирование
|
||||
canEditData,
|
||||
canEditContacts,
|
||||
canDeleteData,
|
||||
canDeleteMessages,
|
||||
|
||||
// Коммуникация
|
||||
canSendToUsers,
|
||||
canChatWithAdmins,
|
||||
canGenerateAI,
|
||||
canBroadcast,
|
||||
|
||||
// Управление
|
||||
canManageTags,
|
||||
canBlockUsers,
|
||||
canManageSettings,
|
||||
|
||||
// Утилиты
|
||||
getLevelDescription,
|
||||
getLevelClass
|
||||
getLevelClass,
|
||||
|
||||
// Константы
|
||||
ROLES,
|
||||
PERMISSIONS
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user