ваше сообщение коммита
This commit is contained in:
@@ -101,6 +101,80 @@
|
||||
{{ em.from_email }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- Настройки RAG поиска -->
|
||||
<div class="rag-search-settings">
|
||||
<h3>Настройки RAG поиска</h3>
|
||||
|
||||
<!-- Метод поиска -->
|
||||
<label>Метод поиска</label>
|
||||
<select v-model="ragSettings.searchMethod">
|
||||
<option value="semantic">Только семантический поиск</option>
|
||||
<option value="keyword">Только поиск по ключевым словам</option>
|
||||
<option value="hybrid">Гибридный поиск</option>
|
||||
</select>
|
||||
|
||||
<!-- Количество результатов -->
|
||||
<label>Максимальное количество результатов поиска</label>
|
||||
<input type="number" v-model="ragSettings.maxResults" min="1" max="20" />
|
||||
|
||||
<!-- Порог релевантности -->
|
||||
<label>Порог релевантности ({{ ragSettings.relevanceThreshold }})</label>
|
||||
<input type="range" v-model="ragSettings.relevanceThreshold"
|
||||
min="0.01" max="1.0" step="0.01" />
|
||||
|
||||
<!-- Настройки извлечения ключевых слов -->
|
||||
<div class="keyword-settings">
|
||||
<h4>Извлечение ключевых слов</h4>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="ragSettings.keywordExtraction.enabled" />
|
||||
Включить извлечение ключевых слов
|
||||
</label>
|
||||
|
||||
<label>Минимальная длина слова</label>
|
||||
<input type="number" v-model="ragSettings.keywordExtraction.minWordLength"
|
||||
min="2" max="10" />
|
||||
|
||||
<label>Максимальное количество ключевых слов</label>
|
||||
<input type="number" v-model="ragSettings.keywordExtraction.maxKeywords"
|
||||
min="5" max="20" />
|
||||
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="ragSettings.keywordExtraction.removeStopWords" />
|
||||
Удалять стоп-слова
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Веса для гибридного поиска -->
|
||||
<div v-if="ragSettings.searchMethod === 'hybrid'" class="search-weights">
|
||||
<h4>Веса поиска</h4>
|
||||
<label>Семантический поиск: {{ ragSettings.searchWeights.semantic }}%</label>
|
||||
<input type="range" v-model="ragSettings.searchWeights.semantic"
|
||||
min="0" max="100" />
|
||||
|
||||
<label>Поиск по ключевым словам: {{ ragSettings.searchWeights.keyword }}%</label>
|
||||
<input type="range" v-model="ragSettings.searchWeights.keyword"
|
||||
min="0" max="100" />
|
||||
</div>
|
||||
|
||||
<!-- Дополнительные настройки -->
|
||||
<div class="advanced-settings">
|
||||
<h4>Дополнительные настройки</h4>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="ragSettings.advanced.enableFuzzySearch" />
|
||||
Нечеткий поиск
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="ragSettings.advanced.enableStemming" />
|
||||
Стемминг слов
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="ragSettings.advanced.enableSynonyms" />
|
||||
Поиск синонимов
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit">Сохранить</button>
|
||||
<button type="button" @click="goBack">Отмена</button>
|
||||
@@ -143,6 +217,29 @@ const placeholders = ref([]);
|
||||
const editingPlaceholder = ref(null);
|
||||
const editingPlaceholderValue = ref('');
|
||||
|
||||
// Настройки RAG поиска
|
||||
const ragSettings = ref({
|
||||
searchMethod: 'hybrid',
|
||||
maxResults: 5,
|
||||
relevanceThreshold: 0.1,
|
||||
keywordExtraction: {
|
||||
enabled: true,
|
||||
minWordLength: 3,
|
||||
maxKeywords: 10,
|
||||
removeStopWords: true,
|
||||
language: 'ru'
|
||||
},
|
||||
searchWeights: {
|
||||
semantic: 70,
|
||||
keyword: 30
|
||||
},
|
||||
advanced: {
|
||||
enableFuzzySearch: true,
|
||||
enableStemming: true,
|
||||
enableSynonyms: false
|
||||
}
|
||||
});
|
||||
|
||||
async function loadUserTables() {
|
||||
const { data } = await axios.get('/tables');
|
||||
userTables.value = Array.isArray(data) ? data : [];
|
||||
@@ -165,7 +262,14 @@ async function loadSettings() {
|
||||
}
|
||||
|
||||
settings.value = settingsData;
|
||||
|
||||
// Загружаем настройки RAG, если они есть
|
||||
if (data.settings.ragSettings) {
|
||||
ragSettings.value = { ...ragSettings.value, ...data.settings.ragSettings };
|
||||
}
|
||||
|
||||
console.log('[AiAssistantSettings] Loaded settings:', settings.value);
|
||||
console.log('[AiAssistantSettings] Loaded RAG settings:', ragSettings.value);
|
||||
}
|
||||
}
|
||||
async function loadTelegramBots() {
|
||||
@@ -225,7 +329,11 @@ async function saveSettings() {
|
||||
settingsToSave.selected_rag_tables = [settingsToSave.selected_rag_tables];
|
||||
}
|
||||
|
||||
// Добавляем настройки RAG
|
||||
settingsToSave.ragSettings = ragSettings.value;
|
||||
|
||||
console.log('[AiAssistantSettings] Saving settings:', settingsToSave);
|
||||
console.log('[AiAssistantSettings] Saving RAG settings:', ragSettings.value);
|
||||
await axios.put('/settings/ai-assistant', settingsToSave);
|
||||
goBack();
|
||||
}
|
||||
@@ -411,4 +519,63 @@ button[type="button"] {
|
||||
font-size: 1em;
|
||||
margin: 0.7em 0;
|
||||
}
|
||||
|
||||
/* Стили для настроек RAG поиска */
|
||||
.rag-search-settings {
|
||||
margin: 2rem 0;
|
||||
padding: 1.5rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.rag-search-settings h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.keyword-settings, .search-weights, .advanced-settings {
|
||||
margin: 1rem 0;
|
||||
padding: 1rem;
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.keyword-settings h4, .search-weights h4, .advanced-settings h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: #555;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.search-weights input[type="range"] {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0.5rem 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rag-search-settings input[type="range"] {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.rag-search-settings input[type="number"] {
|
||||
width: 100px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -70,6 +70,7 @@
|
||||
import { ref } from 'vue';
|
||||
import AIProviderSettings from './AIProviderSettings.vue';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
import NoAccessModal from '@/components/NoAccessModal.vue';
|
||||
|
||||
const showProvider = ref(null);
|
||||
@@ -80,6 +81,7 @@ const showAiAssistantSettings = ref(false);
|
||||
const showNoAccessModal = ref(false);
|
||||
|
||||
const { isAdmin } = useAuthContext();
|
||||
const { canManageSettings } = usePermissions();
|
||||
|
||||
const providerLabels = {
|
||||
openai: {
|
||||
@@ -117,7 +119,7 @@ const providerLabels = {
|
||||
};
|
||||
|
||||
function goTo(path) {
|
||||
if (!isAdmin.value) {
|
||||
if (!canManageSettings.value) {
|
||||
showNoAccessModal.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,17 +13,31 @@
|
||||
<template>
|
||||
<div class="auth-tokens-settings">
|
||||
<h4>Токены аутентификации</h4>
|
||||
|
||||
<!-- Отображение текущего уровня доступа -->
|
||||
<div v-if="userAccessLevel && userAccessLevel.hasAccess" class="access-level-info">
|
||||
<div class="access-level-badge" :class="getLevelClass(userAccessLevel.level)">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
<span>{{ getLevelDescription(userAccessLevel.level) }}</span>
|
||||
<span class="token-count">({{ userAccessLevel.tokenCount }} токен{{ userAccessLevel.tokenCount === 1 ? '' : userAccessLevel.tokenCount < 5 ? 'а' : 'ов' }})</span>
|
||||
</div>
|
||||
<div class="access-level-description">
|
||||
{{ getAccessLevelDescription(userAccessLevel.level) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="authTokens.length > 0" class="tokens-list">
|
||||
<div v-for="(token, index) in authTokens" :key="token.address + token.network" class="token-entry">
|
||||
<span><strong>Название:</strong> {{ token.name }}</span>
|
||||
<span><strong>Адрес:</strong> {{ token.address }}</span>
|
||||
<span><strong>Сеть:</strong> {{ getNetworkLabel(token.network) }}</span>
|
||||
<span><strong>Мин. баланс:</strong> {{ token.minBalance }}</span>
|
||||
<span><strong>Read-Only:</strong> {{ token.readonlyThreshold || 1 }} токен{{ token.readonlyThreshold === 1 ? '' : 'а' }}</span>
|
||||
<span><strong>Editor:</strong> {{ token.editorThreshold || 2 }} токен{{ token.editorThreshold === 1 ? '' : token.editorThreshold < 5 ? 'а' : 'ов' }}</span>
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
:class="isAdmin ? 'btn-danger' : 'btn-secondary'"
|
||||
@click="isAdmin ? removeToken(index) : null"
|
||||
:disabled="!isAdmin"
|
||||
:class="canEdit ? 'btn-danger' : 'btn-secondary'"
|
||||
@click="canEdit ? removeToken(index) : null"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
@@ -39,7 +53,7 @@
|
||||
v-model="newToken.name"
|
||||
class="form-control"
|
||||
placeholder="test2"
|
||||
:disabled="!isAdmin"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -49,12 +63,12 @@
|
||||
v-model="newToken.address"
|
||||
class="form-control"
|
||||
placeholder="0x..."
|
||||
:disabled="!isAdmin"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Сеть:</label>
|
||||
<select v-model="newToken.network" class="form-control" :disabled="!isAdmin">
|
||||
<select v-model="newToken.network" class="form-control" :disabled="!canEdit">
|
||||
<option value="">-- Выберите сеть --</option>
|
||||
<optgroup v-for="(group, groupIndex) in networkGroups" :key="groupIndex" :label="group.label">
|
||||
<option v-for="option in group.options" :key="option.value" :value="option.value">
|
||||
@@ -70,14 +84,43 @@
|
||||
v-model.number="newToken.minBalance"
|
||||
class="form-control"
|
||||
placeholder="0"
|
||||
:disabled="!isAdmin"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Настройки прав доступа -->
|
||||
<div class="access-settings">
|
||||
<h6>Настройки прав доступа</h6>
|
||||
<div class="form-group">
|
||||
<label>Минимум токенов для Read-Only доступа:</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model="newToken.readonlyThreshold"
|
||||
class="form-control"
|
||||
placeholder="1"
|
||||
min="1"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
<small class="form-text">Количество токенов для получения прав только на чтение</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Минимум токенов для Editor доступа:</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model="newToken.editorThreshold"
|
||||
class="form-control"
|
||||
placeholder="2"
|
||||
min="2"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
<small class="form-text">Количество токенов для получения прав на редактирование и удаление</small>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn"
|
||||
:class="isAdmin ? 'btn-secondary' : 'btn-secondary'"
|
||||
@click="isAdmin ? addToken() : null"
|
||||
:disabled="!isAdmin"
|
||||
:class="canEdit ? 'btn-primary' : 'btn-secondary'"
|
||||
@click="canEdit ? addToken() : null"
|
||||
:disabled="!canEdit"
|
||||
>
|
||||
Добавить токен
|
||||
</button>
|
||||
@@ -86,36 +129,58 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { reactive, computed } from 'vue';
|
||||
import useBlockchainNetworks from '@/composables/useBlockchainNetworks';
|
||||
import api from '@/api/axios';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
import eventBus from '@/utils/eventBus';
|
||||
const props = defineProps({
|
||||
authTokens: { type: Array, required: true }
|
||||
});
|
||||
const emit = defineEmits(['update']);
|
||||
const newToken = reactive({ name: '', address: '', network: '', minBalance: 0 });
|
||||
const newToken = reactive({
|
||||
name: '',
|
||||
address: '',
|
||||
network: '',
|
||||
minBalance: 0,
|
||||
readonlyThreshold: 1,
|
||||
editorThreshold: 2
|
||||
});
|
||||
|
||||
const { networkGroups, networks } = useBlockchainNetworks();
|
||||
const { isAdmin, checkTokenBalances, address, checkAuth } = useAuthContext();
|
||||
const { isAdmin, checkTokenBalances, address, checkAuth, userAccessLevel, checkUserAccessLevel } = useAuthContext();
|
||||
const { canEdit, getLevelClass, getLevelDescription } = usePermissions();
|
||||
|
||||
async function addToken() {
|
||||
if (!newToken.name || !newToken.address || !newToken.network) {
|
||||
alert('Все поля обязательны');
|
||||
return;
|
||||
}
|
||||
|
||||
const tokenData = {
|
||||
name: newToken.name,
|
||||
address: newToken.address,
|
||||
network: newToken.network,
|
||||
minBalance: Number(newToken.minBalance) || 0,
|
||||
readonlyThreshold: newToken.readonlyThreshold !== null && newToken.readonlyThreshold !== undefined && newToken.readonlyThreshold !== '' ? Number(newToken.readonlyThreshold) : 1,
|
||||
editorThreshold: newToken.editorThreshold !== null && newToken.editorThreshold !== undefined && newToken.editorThreshold !== '' ? Number(newToken.editorThreshold) : 2
|
||||
};
|
||||
|
||||
console.log('[AuthTokensSettings] Отправляем данные токена:', tokenData);
|
||||
console.log('[AuthTokensSettings] newToken объект:', newToken);
|
||||
console.log('[AuthTokensSettings] newToken.readonlyThreshold:', newToken.readonlyThreshold, 'тип:', typeof newToken.readonlyThreshold);
|
||||
console.log('[AuthTokensSettings] newToken.editorThreshold:', newToken.editorThreshold, 'тип:', typeof newToken.editorThreshold);
|
||||
|
||||
try {
|
||||
await api.post('/settings/auth-token', {
|
||||
...newToken,
|
||||
minBalance: Number(newToken.minBalance) || 0
|
||||
});
|
||||
await api.post('/settings/auth-token', tokenData);
|
||||
|
||||
// После добавления токена перепроверяем баланс пользователя и обновляем состояние аутентификации
|
||||
try {
|
||||
if (address.value) {
|
||||
await checkTokenBalances(address.value);
|
||||
console.log('[AuthTokensSettings] Баланс токенов перепроверен после добавления');
|
||||
await checkUserAccessLevel(address.value);
|
||||
console.log('[AuthTokensSettings] Баланс токенов и уровень доступа перепроверены после добавления');
|
||||
}
|
||||
|
||||
// Обновляем состояние аутентификации чтобы отразить изменения роли
|
||||
@@ -138,6 +203,8 @@ async function addToken() {
|
||||
newToken.address = '';
|
||||
newToken.network = '';
|
||||
newToken.minBalance = 0;
|
||||
newToken.readonlyThreshold = 1;
|
||||
newToken.editorThreshold = 2;
|
||||
} catch (e) {
|
||||
alert('Ошибка при добавлении токена: ' + (e.response?.data?.error || e.message));
|
||||
}
|
||||
@@ -159,7 +226,8 @@ async function removeToken(index) {
|
||||
try {
|
||||
if (address.value) {
|
||||
await checkTokenBalances(address.value);
|
||||
console.log('[AuthTokensSettings] Баланс токенов перепроверен после удаления');
|
||||
await checkUserAccessLevel(address.value);
|
||||
console.log('[AuthTokensSettings] Баланс токенов и уровень доступа перепроверены после удаления');
|
||||
}
|
||||
|
||||
// Обновляем состояние аутентификации чтобы отразить изменения роли
|
||||
@@ -188,13 +256,118 @@ function getNetworkLabel(networkId) {
|
||||
const found = networks.value.find(n => n.value === networkId);
|
||||
return found ? found.label : networkId;
|
||||
}
|
||||
|
||||
|
||||
function getAccessLevelDescription(level) {
|
||||
switch (level) {
|
||||
case 'readonly':
|
||||
return 'Можете просматривать данные, но не можете редактировать или удалять';
|
||||
case 'editor':
|
||||
return 'Можете просматривать, редактировать и удалять данные';
|
||||
case 'user':
|
||||
default:
|
||||
return 'Базовые права пользователя';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tokens-list { margin-bottom: 1rem; }
|
||||
.token-entry { display: flex; gap: 1rem; align-items: center; margin-bottom: 0.5rem; }
|
||||
.token-entry {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.75rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.token-entry span {
|
||||
font-size: 0.875rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.add-token-form { margin-top: 1rem; }
|
||||
|
||||
/* Стили для секции настроек прав доступа */
|
||||
.access-settings {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.access-settings h6 {
|
||||
margin-bottom: 1rem;
|
||||
color: #495057;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* Стили для отображения уровня доступа */
|
||||
.access-level-info {
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.access-level-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.access-level-badge i {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.access-readonly {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.access-editor {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border: 1px solid #bee5eb;
|
||||
}
|
||||
|
||||
.access-user {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.token-count {
|
||||
font-weight: 400;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.access-level-description {
|
||||
font-size: 0.85rem;
|
||||
color: #6c757d;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* Стили для неактивных кнопок */
|
||||
.btn[disabled], .btn:disabled {
|
||||
background: #e0e0e0 !important;
|
||||
|
||||
@@ -858,7 +858,7 @@
|
||||
@click="deploySmartContracts"
|
||||
type="button"
|
||||
class="btn btn-primary btn-lg deploy-btn"
|
||||
:disabled="!isFormValid || !adminTokenCheck.isAdmin || adminTokenCheck.isLoading || showDeployProgress"
|
||||
:disabled="!isFormValid || !canEdit || adminTokenCheck.isLoading || showDeployProgress"
|
||||
:title="`isFormValid: ${isFormValid}, isAdmin: ${adminTokenCheck.isAdmin}, isLoading: ${adminTokenCheck.isLoading}, showDeployProgress: ${showDeployProgress}`"
|
||||
>
|
||||
<i class="fas fa-cogs"></i>
|
||||
@@ -941,6 +941,7 @@
|
||||
import { reactive, ref, computed, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
import api from '@/api/axios';
|
||||
import DeploymentWizard from '@/components/deployment/DeploymentWizard.vue';
|
||||
|
||||
@@ -959,6 +960,7 @@ function normalizePrivateKey(raw) {
|
||||
|
||||
// Получаем контекст авторизации для адреса кошелька
|
||||
const { address, isAdmin } = useAuthContext();
|
||||
const { canEdit } = usePermissions();
|
||||
|
||||
// Состояние для проверки админских токенов
|
||||
const adminTokenCheck = ref({
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
</div>
|
||||
<button
|
||||
class="btn-primary"
|
||||
@click="isAdmin ? goToAkashDetails() : null"
|
||||
:disabled="!isAdmin"
|
||||
@click="canManageSettings ? goToAkashDetails() : null"
|
||||
:disabled="!canManageSettings"
|
||||
>
|
||||
Подробнее
|
||||
</button>
|
||||
@@ -54,8 +54,8 @@
|
||||
</div>
|
||||
<button
|
||||
class="btn-primary"
|
||||
@click="isAdmin ? goToFluxDetails() : null"
|
||||
:disabled="!isAdmin"
|
||||
@click="canManageSettings ? goToFluxDetails() : null"
|
||||
:disabled="!canManageSettings"
|
||||
>
|
||||
Подробнее
|
||||
</button>
|
||||
@@ -91,10 +91,12 @@
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
import NoAccessModal from '@/components/NoAccessModal.vue';
|
||||
import { ref } from 'vue';
|
||||
const router = useRouter();
|
||||
const { isAdmin } = useAuthContext();
|
||||
const { canManageSettings } = usePermissions();
|
||||
const goBack = () => router.push('/settings');
|
||||
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ import RpcProvidersSettings from './RpcProvidersSettings.vue';
|
||||
import AuthTokensSettings from './AuthTokensSettings.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
import NoAccessModal from '@/components/NoAccessModal.vue';
|
||||
import wsClient from '@/utils/websocket';
|
||||
|
||||
@@ -88,6 +89,7 @@ const showNoAccessModal = ref(false);
|
||||
|
||||
// Получаем контекст авторизации
|
||||
const { isAdmin } = useAuthContext();
|
||||
const { canManageSettings } = usePermissions();
|
||||
|
||||
// Настройки безопасности
|
||||
const securitySettings = reactive({
|
||||
@@ -168,7 +170,9 @@ const loadSettings = async () => {
|
||||
name: token.name,
|
||||
address: token.address,
|
||||
network: token.network,
|
||||
minBalance: token.min_balance
|
||||
minBalance: token.min_balance,
|
||||
readonlyThreshold: token.readonly_threshold || 1,
|
||||
editorThreshold: token.editor_threshold || 2
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -331,11 +335,11 @@ provide('networks', networks);
|
||||
|
||||
// Функция для обработки клика по кнопке "Подробнее" для RPC провайдеров
|
||||
const handleRpcDetailsClick = () => {
|
||||
if (isAdmin.value) {
|
||||
// Если администратор - показываем детали RPC
|
||||
if (canManageSettings.value) {
|
||||
// Если есть права на управление настройками - показываем детали RPC
|
||||
showRpcSettings.value = !showRpcSettings.value;
|
||||
} else {
|
||||
// Если обычный пользователь - показываем модальное окно с ограничением доступа
|
||||
// Если нет прав - показываем модальное окно с ограничением доступа
|
||||
showNoAccessModal.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user