ваше сообщение коммита
This commit is contained in:
@@ -148,6 +148,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Операции модулей (динамические) -->
|
||||
<div v-if="isLoadingModuleOperations" class="loading-modules">
|
||||
Загрузка операций модулей...
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="moduleOperation in moduleOperations"
|
||||
:key="moduleOperation.moduleType"
|
||||
class="operation-category"
|
||||
>
|
||||
<h5>{{ getModuleIcon(moduleOperation.moduleType) }} {{ moduleOperation.moduleName }}</h5>
|
||||
<p class="module-description">{{ moduleOperation.moduleDescription }}</p>
|
||||
<div class="operation-blocks">
|
||||
<div
|
||||
v-for="operation in moduleOperation.operations"
|
||||
:key="operation.id"
|
||||
class="operation-block module-operation-block"
|
||||
>
|
||||
<div class="operation-icon">{{ operation.icon }}</div>
|
||||
<h6>{{ operation.name }}</h6>
|
||||
<p>{{ operation.description }}</p>
|
||||
<div class="operation-category-tag">{{ operation.category }}</div>
|
||||
<button
|
||||
class="create-btn"
|
||||
@click="openModuleOperationForm(moduleOperation.moduleType, operation)"
|
||||
:disabled="!props.isAuthenticated || isLoadingModuleOperations"
|
||||
>
|
||||
<span v-if="isLoadingModuleOperations">Загрузка...</span>
|
||||
<span v-else>Создать</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Оффчейн операции -->
|
||||
<div class="operation-category">
|
||||
<h5>📋 Оффчейн операции</h5>
|
||||
@@ -169,12 +203,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, defineProps, defineEmits, inject } from 'vue';
|
||||
import { ref, computed, onMounted, onUnmounted, defineProps, defineEmits, inject } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAuthContext } from '../../composables/useAuth';
|
||||
import BaseLayout from '../../components/BaseLayout.vue';
|
||||
import { getDLEInfo, getSupportedChains } from '../../services/dleV2Service.js';
|
||||
import { createProposal as createProposalAPI } from '../../services/proposalsService.js';
|
||||
import { getModuleOperations } from '../../services/moduleOperationsService.js';
|
||||
import api from '../../api/axios';
|
||||
import wsClient from '../../utils/websocket.js';
|
||||
import { ethers } from 'ethers';
|
||||
@@ -221,6 +256,12 @@ const isLoadingDle = ref(false);
|
||||
// Доступные цепочки (загружаются из конфигурации)
|
||||
const availableChains = ref([]);
|
||||
|
||||
// Состояние модулей и их операций
|
||||
const moduleOperations = ref([]);
|
||||
const isLoadingModuleOperations = ref(false);
|
||||
const modulesWebSocket = ref(null);
|
||||
const isModulesWSConnected = ref(false);
|
||||
|
||||
// Функции для открытия отдельных форм операций
|
||||
function openTransferForm() {
|
||||
// TODO: Открыть форму для передачи токенов
|
||||
@@ -273,6 +314,26 @@ function openOffchainActionForm() {
|
||||
alert('Форма оффчейн действий будет реализована');
|
||||
}
|
||||
|
||||
// Функция для создания предложения операции модуля
|
||||
function openModuleOperationForm(moduleType, operation) {
|
||||
console.log('[CreateProposalView] Открытие формы для операции модуля:', { moduleType, operation });
|
||||
|
||||
// TODO: Открыть форму для создания предложения операции модуля
|
||||
// Пока показываем alert с информацией об операции
|
||||
alert(`Создание предложения для операции "${operation.name}" модуля ${moduleType}.\n\nОписание: ${operation.description}\nФункция: ${operation.functionName}\nКатегория: ${operation.category}`);
|
||||
}
|
||||
|
||||
// Получить иконку для типа модуля
|
||||
function getModuleIcon(moduleType) {
|
||||
const icons = {
|
||||
treasury: '💰',
|
||||
timelock: '⏰',
|
||||
reader: '📖',
|
||||
hierarchicalVoting: '🗳️'
|
||||
};
|
||||
return icons[moduleType] || '🔧';
|
||||
}
|
||||
|
||||
// Функции
|
||||
async function loadDleData() {
|
||||
console.log('loadDleData вызвана с адресом:', dleAddress.value);
|
||||
@@ -300,6 +361,9 @@ async function loadDleData() {
|
||||
const chainsResponse = await getSupportedChains(dleAddress.value);
|
||||
availableChains.value = chainsResponse.data?.chains || [];
|
||||
|
||||
// Загружаем операции модулей
|
||||
await loadModuleOperations();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных DLE из блокчейна:', error);
|
||||
} finally {
|
||||
@@ -307,6 +371,113 @@ async function loadDleData() {
|
||||
}
|
||||
}
|
||||
|
||||
// Загрузка операций модулей
|
||||
async function loadModuleOperations() {
|
||||
if (!dleAddress.value) {
|
||||
console.warn('Адрес DLE не указан для загрузки операций модулей');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoadingModuleOperations.value = true;
|
||||
try {
|
||||
console.log('[CreateProposalView] Загрузка операций модулей для DLE:', dleAddress.value);
|
||||
|
||||
const response = await getModuleOperations(dleAddress.value);
|
||||
|
||||
if (response.success) {
|
||||
moduleOperations.value = response.data.moduleOperations || [];
|
||||
console.log('[CreateProposalView] Загружены операции модулей:', moduleOperations.value);
|
||||
} else {
|
||||
console.error('[CreateProposalView] Ошибка загрузки операций модулей:', response.error);
|
||||
moduleOperations.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[CreateProposalView] Ошибка загрузки операций модулей:', error);
|
||||
moduleOperations.value = [];
|
||||
} finally {
|
||||
isLoadingModuleOperations.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket функции для модулей
|
||||
function connectModulesWebSocket() {
|
||||
if (modulesWebSocket.value && modulesWebSocket.value.readyState === WebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wsUrl = `ws://localhost:8000/ws/deployment`;
|
||||
modulesWebSocket.value = new WebSocket(wsUrl);
|
||||
|
||||
modulesWebSocket.value.onopen = () => {
|
||||
console.log('[CreateProposalView] WebSocket модулей соединение установлено');
|
||||
isModulesWSConnected.value = true;
|
||||
|
||||
// Подписываемся на обновления модулей для текущего DLE
|
||||
if (dleAddress.value) {
|
||||
modulesWebSocket.value.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
dleAddress: dleAddress.value
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
modulesWebSocket.value.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
handleModulesWebSocketMessage(data);
|
||||
} catch (error) {
|
||||
console.error('[CreateProposalView] Ошибка парсинга WebSocket сообщения модулей:', error);
|
||||
}
|
||||
};
|
||||
|
||||
modulesWebSocket.value.onclose = () => {
|
||||
console.log('[CreateProposalView] WebSocket модулей соединение закрыто');
|
||||
isModulesWSConnected.value = false;
|
||||
|
||||
// Переподключаемся через 5 секунд
|
||||
setTimeout(() => {
|
||||
connectModulesWebSocket();
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
modulesWebSocket.value.onerror = (error) => {
|
||||
console.error('[CreateProposalView] Ошибка WebSocket модулей:', error);
|
||||
isModulesWSConnected.value = false;
|
||||
};
|
||||
}
|
||||
|
||||
function handleModulesWebSocketMessage(data) {
|
||||
console.log('[CreateProposalView] WebSocket модулей сообщение:', data);
|
||||
|
||||
switch (data.type) {
|
||||
case 'modules_updated':
|
||||
// Автоматически обновляем список операций модулей
|
||||
console.log('[CreateProposalView] Получено уведомление об обновлении модулей');
|
||||
loadModuleOperations();
|
||||
break;
|
||||
|
||||
case 'module_verified':
|
||||
// Обновляем операции модуля
|
||||
console.log(`[CreateProposalView] Модуль ${data.moduleType} верифицирован`);
|
||||
loadModuleOperations();
|
||||
break;
|
||||
|
||||
case 'module_status_changed':
|
||||
// Обновляем операции модуля
|
||||
console.log(`[CreateProposalView] Статус модуля ${data.moduleType} изменен`);
|
||||
loadModuleOperations();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function disconnectModulesWebSocket() {
|
||||
if (modulesWebSocket.value) {
|
||||
modulesWebSocket.value.close();
|
||||
modulesWebSocket.value = null;
|
||||
isModulesWSConnected.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// Принудительно загружаем токены, если пользователь аутентифицирован
|
||||
if (isAuthenticated.value && address.value) {
|
||||
@@ -318,6 +489,14 @@ onMounted(async () => {
|
||||
if (dleAddress.value) {
|
||||
loadDleData();
|
||||
}
|
||||
|
||||
// Подключаемся к WebSocket для получения обновлений модулей
|
||||
connectModulesWebSocket();
|
||||
});
|
||||
|
||||
// Отключаем WebSocket при размонтировании компонента
|
||||
onUnmounted(() => {
|
||||
disconnectModulesWebSocket();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -562,6 +741,91 @@ onMounted(async () => {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Стили для модулей */
|
||||
.module-description {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
margin: 0.5rem 0 1rem 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.module-operation-block {
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||
border: 2px solid #e9ecef;
|
||||
}
|
||||
|
||||
.module-operation-block::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #28a745, #20c997);
|
||||
transform: scaleX(0);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.module-operation-block:hover::before {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.operation-category-tag {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #28a745, #20c997);
|
||||
color: white;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
margin: 0.5rem 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* Анимация появления модулей */
|
||||
.operation-category {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Индикатор загрузки модулей */
|
||||
.loading-modules {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.loading-modules::before {
|
||||
content: '';
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid var(--color-primary);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.operations-blocks {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,329 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="application-module-deploy">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Деплой ApplicationModule</h1>
|
||||
<p>Управление вызовом функций приложения через предложения и голосование</p>
|
||||
<p v-if="dleAddress" class="dle-address">
|
||||
<strong>DLE:</strong> {{ dleAddress }}
|
||||
</p>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management/modules')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Информация о модуле -->
|
||||
<div class="module-info">
|
||||
<div class="info-card">
|
||||
<h3>🖥️ ApplicationModule</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<strong>Назначение:</strong> Управление функциями приложения через DLE
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Функции:</strong> Создание предложений для вызова API, голосование за операции
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Безопасность:</strong> Все операции приложения через кворум токен-холдеров
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Примеры:</strong> Удаление пользователей, изменение настроек, обновление данных
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Детальное описание -->
|
||||
<div class="module-details">
|
||||
<div class="details-card">
|
||||
<h3>📋 Как работает ApplicationModule</h3>
|
||||
<div class="details-content">
|
||||
<div class="detail-step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-content">
|
||||
<h4>Создание предложения</h4>
|
||||
<p>Токен-холдер создает предложение для выполнения операции в приложении (например, удаление пользователя, изменение настроек)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-content">
|
||||
<h4>Голосование</h4>
|
||||
<p>Все токен-холдеры голосуют за или против предложения в течение установленного времени</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-content">
|
||||
<h4>Исполнение</h4>
|
||||
<p>При достижении кворума предложение исполняется - вызывается соответствующая функция приложения</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-content">
|
||||
<h4>Аудит</h4>
|
||||
<p>Все операции логируются в блокчейне для полной прозрачности и подотчетности</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя будет добавлена позже -->
|
||||
<div class="deploy-form-placeholder">
|
||||
<div class="placeholder-content">
|
||||
<h3>🚧 Форма деплоя в разработке</h3>
|
||||
<p>Здесь будет форма для деплоя ApplicationModule</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, ref, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
isAuthenticated: { type: Boolean, default: false },
|
||||
identities: { type: Array, default: () => [] },
|
||||
tokenBalances: { type: Object, default: () => ({}) },
|
||||
isLoadingTokens: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
// Определяем emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// Состояние
|
||||
const isLoading = ref(false);
|
||||
const dleAddress = ref(route.query.address || null);
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
console.log('[ApplicationModuleDeployView] Страница загружена');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.application-module-deploy {
|
||||
padding: 20px;
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 10px 0 0 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
margin-top: 10px !important;
|
||||
font-family: monospace;
|
||||
background: #f8f9fa;
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Информация о модуле */
|
||||
.module-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.info-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.info-item strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Детальное описание */
|
||||
.module-details {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.details-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.details-card h3 {
|
||||
margin: 0 0 20px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.details-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.detail-step {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content h4 {
|
||||
margin: 0 0 8px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.step-content p {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Плейсхолдер для формы */
|
||||
.deploy-form-placeholder {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
border: 2px dashed #dee2e6;
|
||||
}
|
||||
|
||||
.placeholder-content h3 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.placeholder-content p {
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.detail-step {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,515 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="module-deploy-page">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>🔥 Деплой BurnModule</h1>
|
||||
<p>Модуль для сжигания токенов DLE через governance</p>
|
||||
<div v-if="selectedDle" class="dle-info">
|
||||
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
|
||||
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Описание модуля -->
|
||||
<div class="module-description">
|
||||
<div class="description-card">
|
||||
<h3>📋 Описание BurnModule</h3>
|
||||
<div class="description-content">
|
||||
<p><strong>BurnModule</strong> - это модуль для управления сжиганием токенов DLE через систему governance.</p>
|
||||
|
||||
<h4>🔧 Функциональность:</h4>
|
||||
<ul>
|
||||
<li><strong>Сжигание токенов:</strong> Уменьшение общего предложения токенов DLE</li>
|
||||
<li><strong>Governance:</strong> Все операции требуют голосования и кворума</li>
|
||||
<li><strong>Безопасность:</strong> Контролируемое сжигание через коллективные решения</li>
|
||||
<li><strong>Прозрачность:</strong> Все операции записываются в блокчейн</li>
|
||||
</ul>
|
||||
|
||||
<h4>⚠️ Важные особенности:</h4>
|
||||
<ul>
|
||||
<li>Сжигание токенов возможно только через предложения и голосование</li>
|
||||
<li>Можно сжигать токены из казны DLE или от имени участников</li>
|
||||
<li>Все операции требуют достижения кворума</li>
|
||||
<li>История всех сжиганий сохраняется в блокчейне</li>
|
||||
<li>Сжигание необратимо - токены уничтожаются навсегда</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя -->
|
||||
<div class="deploy-form-section">
|
||||
<div class="form-header">
|
||||
<h3>⚙️ Настройки деплоя</h3>
|
||||
<p>Настройте параметры для деплоя BurnModule</p>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="deployModule" class="deploy-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="moduleName">Название модуля:</label>
|
||||
<input
|
||||
id="moduleName"
|
||||
v-model="deployData.moduleName"
|
||||
type="text"
|
||||
placeholder="BurnModule"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="moduleVersion">Версия модуля:</label>
|
||||
<input
|
||||
id="moduleVersion"
|
||||
v-model="deployData.moduleVersion"
|
||||
type="text"
|
||||
placeholder="1.0.0"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="moduleDescription">Описание модуля:</label>
|
||||
<textarea
|
||||
id="moduleDescription"
|
||||
v-model="deployData.moduleDescription"
|
||||
placeholder="Модуль для сжигания токенов DLE через governance..."
|
||||
rows="3"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="maxBurnPerProposal">Максимальное сжигание за одно предложение:</label>
|
||||
<input
|
||||
id="maxBurnPerProposal"
|
||||
v-model="deployData.maxBurnPerProposal"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
placeholder="1000000"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Максимальное количество токенов, которое можно сжечь за одно предложение</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="burnCooldown">Кулдаун между сжиганиями (часы):</label>
|
||||
<input
|
||||
id="burnCooldown"
|
||||
v-model="deployData.burnCooldown"
|
||||
type="number"
|
||||
min="0"
|
||||
step="1"
|
||||
placeholder="24"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Минимальное время между успешными сжиганиями токенов</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="allowTreasuryBurn">Разрешить сжигание из казны:</label>
|
||||
<select
|
||||
id="allowTreasuryBurn"
|
||||
v-model="deployData.allowTreasuryBurn"
|
||||
required
|
||||
>
|
||||
<option value="true">Да</option>
|
||||
<option value="false">Нет</option>
|
||||
</select>
|
||||
<small class="form-help">Разрешить сжигание токенов из казны DLE</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="allowUserBurn">Разрешить сжигание от участников:</label>
|
||||
<select
|
||||
id="allowUserBurn"
|
||||
v-model="deployData.allowUserBurn"
|
||||
required
|
||||
>
|
||||
<option value="true">Да</option>
|
||||
<option value="false">Нет</option>
|
||||
</select>
|
||||
<small class="form-help">Разрешить сжигание токенов от имени участников DLE</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="deployDescription">Описание предложения для деплоя:</label>
|
||||
<textarea
|
||||
id="deployDescription"
|
||||
v-model="deployData.deployDescription"
|
||||
placeholder="Предложение о деплое BurnModule для управления сжиганием токенов DLE..."
|
||||
rows="3"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="votingDuration">Длительность голосования (часы):</label>
|
||||
<input
|
||||
id="votingDuration"
|
||||
v-model="deployData.votingDuration"
|
||||
type="number"
|
||||
min="1"
|
||||
max="168"
|
||||
placeholder="24"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Время для голосования по предложению деплоя (1-168 часов)</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary" :disabled="isDeploying">
|
||||
{{ isDeploying ? 'Деплой...' : 'Деплой BurnModule' }}
|
||||
</button>
|
||||
|
||||
<!-- Статус деплоя -->
|
||||
<div v-if="deployStatus" class="deploy-status">
|
||||
<p class="status-message">{{ deployStatus }}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import api from '../../../api/axios';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
isAuthenticated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
identities: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
tokenBalances: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLoadingTokens: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
// Router
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// Состояние
|
||||
const isDeploying = ref(false);
|
||||
const deployStatus = ref('');
|
||||
const selectedDle = ref(null);
|
||||
const isLoadingDle = ref(false);
|
||||
|
||||
// Данные для деплоя
|
||||
const deployData = ref({
|
||||
moduleName: 'BurnModule',
|
||||
moduleVersion: '1.0.0',
|
||||
moduleDescription: 'Модуль для сжигания токенов DLE через governance',
|
||||
maxBurnPerProposal: 1000000,
|
||||
burnCooldown: 24,
|
||||
allowTreasuryBurn: 'true',
|
||||
allowUserBurn: 'true',
|
||||
deployDescription: 'Предложение о деплое BurnModule для управления сжиганием токенов DLE',
|
||||
votingDuration: 24
|
||||
});
|
||||
|
||||
// Получаем адрес DLE из URL
|
||||
const dleAddress = computed(() => route.query.address);
|
||||
|
||||
// Загрузка данных DLE
|
||||
const loadDleData = async () => {
|
||||
if (!dleAddress.value) return;
|
||||
|
||||
try {
|
||||
isLoadingDle.value = true;
|
||||
const response = await api.post('/blockchain/read-dle-info', {
|
||||
dleAddress: dleAddress.value
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
selectedDle.value = response.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных DLE:', error);
|
||||
} finally {
|
||||
isLoadingDle.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Функция деплоя модуля
|
||||
const deployModule = async () => {
|
||||
if (isDeploying.value) return;
|
||||
|
||||
try {
|
||||
isDeploying.value = true;
|
||||
deployStatus.value = 'Подготовка к деплою...';
|
||||
|
||||
// Здесь будет логика деплоя модуля
|
||||
console.log('Деплой BurnModule:', deployData.value);
|
||||
|
||||
// Временная заглушка
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
deployStatus.value = 'Модуль успешно развернут!';
|
||||
|
||||
// Очищаем статус через 3 секунды
|
||||
setTimeout(() => {
|
||||
deployStatus.value = '';
|
||||
}, 3000);
|
||||
|
||||
alert('BurnModule успешно развернут!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка деплоя модуля:', error);
|
||||
deployStatus.value = 'Ошибка деплоя модуля';
|
||||
|
||||
setTimeout(() => {
|
||||
deployStatus.value = '';
|
||||
}, 3000);
|
||||
|
||||
alert('Ошибка при деплое модуля');
|
||||
} finally {
|
||||
isDeploying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Загружаем данные при монтировании
|
||||
onMounted(() => {
|
||||
loadDleData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-deploy-page {
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2.5rem;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.header-content p {
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 1.1rem;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dle-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: monospace;
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: var(--color-grey-dark);
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.module-description {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.description-card {
|
||||
background: #f8f9fa;
|
||||
padding: 25px;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.description-card h3 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.description-content h4 {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
|
||||
.description-content ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.description-content li {
|
||||
margin: 5px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.deploy-form-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-header {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-header h3 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.form-header p {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.deploy-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: 600;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.form-help {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-grey-dark);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.deploy-status {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #e8f5e8;
|
||||
border-radius: var(--radius-sm);
|
||||
border-left: 4px solid #28a745;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,218 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="communication-module-deploy">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Деплой CommunicationModule</h1>
|
||||
<p>Коммуникации - сообщения, звонки, история общения между участниками</p>
|
||||
<p v-if="dleAddress" class="dle-address">
|
||||
<strong>DLE:</strong> {{ dleAddress }}
|
||||
</p>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management/modules')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Информация о модуле -->
|
||||
<div class="module-info">
|
||||
<div class="info-card">
|
||||
<h3>💬 CommunicationModule</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<strong>Назначение:</strong> Коммуникации между участниками DLE
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Функции:</strong> Сообщения, аудио/видео звонки, история общения
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Безопасность:</strong> Кворум для коммуникационных операций
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя будет добавлена позже -->
|
||||
<div class="deploy-form-placeholder">
|
||||
<div class="placeholder-content">
|
||||
<h3>🚧 Форма деплоя в разработке</h3>
|
||||
<p>Здесь будет форма для деплоя CommunicationModule</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, ref, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
isAuthenticated: { type: Boolean, default: false },
|
||||
identities: { type: Array, default: () => [] },
|
||||
tokenBalances: { type: Object, default: () => ({}) },
|
||||
isLoadingTokens: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
// Определяем emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// Состояние
|
||||
const isLoading = ref(false);
|
||||
const dleAddress = ref(route.query.address || null);
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
console.log('[CommunicationModuleDeployView] Страница загружена');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.communication-module-deploy {
|
||||
padding: 20px;
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 10px 0 0 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
margin-top: 10px !important;
|
||||
font-family: monospace;
|
||||
background: #f8f9fa;
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Информация о модуле */
|
||||
.module-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.info-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.info-item strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Плейсхолдер для формы */
|
||||
.deploy-form-placeholder {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
border: 2px dashed #dee2e6;
|
||||
}
|
||||
|
||||
.placeholder-content h3 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.placeholder-content p {
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,707 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="reader-module-deploy">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Деплой DLEReader</h1>
|
||||
<p>API для чтения данных DLE - получение информации о контракте и предложениях</p>
|
||||
<p v-if="dleAddress" class="dle-address">
|
||||
<strong>DLE:</strong> {{ dleAddress }}
|
||||
</p>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management/modules')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Информация о модуле -->
|
||||
<div class="module-info">
|
||||
<div class="info-card">
|
||||
<h3>📊 DLEReader</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<strong>Назначение:</strong> Чтение данных DLE контракта
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Функции:</strong> API для предложений, голосования, статистики
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Безопасность:</strong> Только чтение, не изменяет состояние
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя модуля администратором -->
|
||||
<div v-if="canManageSettings" class="deploy-form">
|
||||
<div class="form-header">
|
||||
<h3>🔧 Деплой DLEReader администратором</h3>
|
||||
<p>Администратор деплоит модуль, затем создает предложение для добавления в DLE</p>
|
||||
</div>
|
||||
|
||||
<div class="form-content">
|
||||
<!-- Информация о сетях -->
|
||||
<div class="networks-info">
|
||||
<h4>📡 Сети для деплоя:</h4>
|
||||
<div class="networks-list">
|
||||
<div class="network-item">
|
||||
<span class="network-name">Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 11155111</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Holesky</span>
|
||||
<span class="network-chain-id">Chain ID: 17000</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Arbitrum Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 421614</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Base Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 84532</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Настройки модуля -->
|
||||
<div class="module-settings">
|
||||
<h4>⚙️ Настройки DLEReader:</h4>
|
||||
|
||||
<div class="settings-form">
|
||||
<!-- Поля администратора -->
|
||||
<div class="admin-section">
|
||||
<h5>🔐 Настройки администратора:</h5>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="adminPrivateKey">Приватный ключ администратора:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="adminPrivateKey"
|
||||
v-model="moduleSettings.adminPrivateKey"
|
||||
class="form-control"
|
||||
placeholder="0x..."
|
||||
required
|
||||
>
|
||||
<small class="form-help">Приватный ключ для деплоя модуля (администратор платит газ)</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="etherscanApiKey">Etherscan API ключ:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="etherscanApiKey"
|
||||
v-model="moduleSettings.etherscanApiKey"
|
||||
class="form-control"
|
||||
placeholder="YourAPIKey..."
|
||||
>
|
||||
<small class="form-help">API ключ для автоматической верификации контрактов</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="simple-info">
|
||||
<h5>📋 Информация о DLEReader:</h5>
|
||||
<div class="info-text">
|
||||
<p><strong>DLEReader</strong> - это простой read-only модуль, который:</p>
|
||||
<ul>
|
||||
<li>✅ Только читает данные из DLE контракта</li>
|
||||
<li>✅ Не изменяет состояние блокчейна</li>
|
||||
<li>✅ Предоставляет API для получения информации</li>
|
||||
<li>✅ Безопасен для обновления</li>
|
||||
</ul>
|
||||
<p><strong>Конструктор принимает только один параметр:</strong> адрес DLE контракта</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка деплоя -->
|
||||
<div class="deploy-actions">
|
||||
<button
|
||||
class="btn btn-primary btn-large deploy-module"
|
||||
@click="deployDLEReader"
|
||||
:disabled="isDeploying || !dleAddress || !isFormValid"
|
||||
>
|
||||
<i class="fas fa-rocket" :class="{ 'fa-spin': isDeploying }"></i>
|
||||
{{ isDeploying ? 'Деплой модуля...' : 'Деплой DLEReader' }}
|
||||
</button>
|
||||
|
||||
<div v-if="!isFormValid && !isDeploying" class="form-validation-info">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span>Заполните приватный ключ и API ключ для деплоя</span>
|
||||
</div>
|
||||
|
||||
<div v-if="deploymentProgress" class="deployment-progress">
|
||||
<div class="progress-info">
|
||||
<span>{{ deploymentProgress.message }}</span>
|
||||
<span class="progress-percentage">{{ deploymentProgress.percentage }}%</span>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: deploymentProgress.percentage + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Сообщение для пользователей без прав доступа -->
|
||||
<div v-if="!canManageSettings" class="no-access-message">
|
||||
<div class="message-content">
|
||||
<h3>🔒 Нет прав доступа</h3>
|
||||
<p>У вас нет прав для деплоя смарт-контрактов. Только пользователи с ролью Editor могут выполнять деплой.</p>
|
||||
<button class="btn btn-secondary" @click="router.push('/management/modules')">
|
||||
← Вернуться к модулям
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
isAuthenticated: Boolean,
|
||||
identities: Array,
|
||||
tokenBalances: Object,
|
||||
isLoadingTokens: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { canEdit, canManageSettings } = usePermissions();
|
||||
|
||||
// Состояние
|
||||
const isLoading = ref(false);
|
||||
const dleAddress = ref(route.query.address || null);
|
||||
const isDeploying = ref(false);
|
||||
const deploymentProgress = ref(null);
|
||||
|
||||
// Настройки модуля
|
||||
const moduleSettings = ref({
|
||||
// Поля администратора
|
||||
adminPrivateKey: '',
|
||||
etherscanApiKey: ''
|
||||
});
|
||||
|
||||
// Проверка валидности формы
|
||||
const isFormValid = computed(() => {
|
||||
return moduleSettings.value.adminPrivateKey && moduleSettings.value.etherscanApiKey;
|
||||
});
|
||||
|
||||
// Функция деплоя DLEReader
|
||||
async function deployDLEReader() {
|
||||
if (!canManageSettings.value) {
|
||||
alert('У вас нет прав для деплоя смарт-контрактов');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isDeploying.value = true;
|
||||
deploymentProgress.value = {
|
||||
message: 'Инициализация деплоя...',
|
||||
percentage: 0
|
||||
};
|
||||
|
||||
console.log('[DLEReaderDeployView] Начинаем деплой DLEReader для DLE:', dleAddress.value);
|
||||
|
||||
// Вызываем API для деплоя модуля администратором
|
||||
const response = await fetch('/api/dle-modules/deploy-reader-admin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dleAddress: dleAddress.value,
|
||||
moduleType: 'reader',
|
||||
adminPrivateKey: moduleSettings.value.adminPrivateKey,
|
||||
etherscanApiKey: moduleSettings.value.etherscanApiKey,
|
||||
settings: {
|
||||
// Используем настройки по умолчанию
|
||||
useDefaultSettings: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
console.log('[DLEReaderDeployView] Деплой успешно запущен:', result);
|
||||
|
||||
// Обновляем прогресс
|
||||
deploymentProgress.value = {
|
||||
message: 'Деплой запущен успешно! Проверьте логи для отслеживания прогресса.',
|
||||
percentage: 100
|
||||
};
|
||||
|
||||
// Показываем детальную информацию о деплое
|
||||
const deployInfo = result.data || {};
|
||||
const deployedAddresses = deployInfo.addresses || [];
|
||||
|
||||
let successMessage = '✅ DLEReader успешно задеплоен!\n\n';
|
||||
successMessage += `📊 Детали деплоя:\n`;
|
||||
successMessage += `• DLE: ${dleAddress.value}\n`;
|
||||
successMessage += `• Тип модуля: DLEReader\n`;
|
||||
successMessage += `• Адрес модуля: ${deployInfo.moduleAddress || 'Не указан'}\n`;
|
||||
|
||||
if (deployedAddresses.length > 0) {
|
||||
successMessage += `\n🌐 Задеплоенные адреса:\n`;
|
||||
deployedAddresses.forEach((addr, index) => {
|
||||
successMessage += `${index + 1}. ${addr.network}: ${addr.address}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
successMessage += `\n📝 Следующий шаг: Создайте предложение для добавления модуля в DLE через governance.`;
|
||||
|
||||
alert(successMessage);
|
||||
|
||||
// Перенаправляем обратно к модулям
|
||||
setTimeout(() => {
|
||||
router.push(`/management/modules?address=${dleAddress.value}`);
|
||||
}, 3000);
|
||||
|
||||
} else {
|
||||
throw new Error(result.error || 'Неизвестная ошибка');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLEReaderDeployView] Ошибка деплоя:', error);
|
||||
alert('❌ Ошибка деплоя: ' + error.message);
|
||||
|
||||
deploymentProgress.value = {
|
||||
message: 'Ошибка деплоя: ' + error.message,
|
||||
percentage: 0
|
||||
};
|
||||
} finally {
|
||||
isDeploying.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
console.log('[DLEReaderDeployView] Страница загружена');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.reader-module-deploy {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: var(--radius-lg);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.header-content p {
|
||||
margin: 0 0 5px 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: 'Courier New', monospace;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Информация о модуле */
|
||||
.module-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: white;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.info-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border-left: 4px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.info-item strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Форма деплоя */
|
||||
.deploy-form {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.form-header h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.form-header p {
|
||||
margin: 0 0 20px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.networks-info,
|
||||
.module-settings {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Настройки отображения данных */
|
||||
.data-display-settings {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.data-display-settings h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.data-display-settings .form-row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.data-display-settings .form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.data-display-settings .form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Секция администратора */
|
||||
.admin-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: #fff3cd;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.admin-section h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #856404;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Простая информация */
|
||||
.simple-info {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.simple-info h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.info-text p {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.info-text ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.info-text li {
|
||||
margin: 5px 0;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.info-text strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.deploy-actions {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
padding: 16px 32px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.deployment-progress {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress-percentage {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Сети */
|
||||
.networks-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.network-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.network-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.network-chain-id {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* Сообщение об отсутствии прав доступа */
|
||||
.no-access-message {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message-content h3 {
|
||||
color: #856404;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.message-content p {
|
||||
color: #856404;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message-content .btn {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.message-content .btn:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
</style>
|
||||
@@ -1,663 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="module-deploy-page">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>🏛️ Деплой InheritanceModule</h1>
|
||||
<p>Модуль наследования токенов DLE - защита активов и автоматическая передача наследникам</p>
|
||||
<div v-if="selectedDle" class="dle-info">
|
||||
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
|
||||
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Описание модуля -->
|
||||
<div class="module-description">
|
||||
<div class="description-card">
|
||||
<h3>📋 Описание InheritanceModule</h3>
|
||||
<div class="description-content">
|
||||
<p><strong>InheritanceModule</strong> - это модуль для автоматической передачи токенов DLE наследникам в случае смерти или недееспособности токенхолдера.</p>
|
||||
|
||||
<h4>🔧 Основная функциональность:</h4>
|
||||
<ul>
|
||||
<li><strong>Назначение наследников:</strong> Токенхолдеры могут указать один или несколько наследников</li>
|
||||
<li><strong>Распределение долей:</strong> Настройка процентного распределения токенов между наследниками</li>
|
||||
<li><strong>Условия активации:</strong> Настройка условий для передачи токенов (смерть, недееспособность)</li>
|
||||
<li><strong>Временные ограничения:</strong> Установка минимального периода владения токенами</li>
|
||||
<li><strong>Множественные наследники:</strong> Поддержка сложных схем наследования</li>
|
||||
<li><strong>Отзыв и изменение:</strong> Возможность изменения наследников в любое время</li>
|
||||
</ul>
|
||||
|
||||
<h4>🏛️ Юридические аспекты:</h4>
|
||||
<ul>
|
||||
<li><strong>Соответствие законам:</strong> Интеграция с юридическими системами наследования</li>
|
||||
<li><strong>Документооборот:</strong> Автоматическое создание юридических документов</li>
|
||||
<li><strong>Подтверждение смерти:</strong> Интеграция с государственными реестрами</li>
|
||||
<li><strong>Споры и оспаривание:</strong> Механизмы разрешения споров о наследстве</li>
|
||||
<li><strong>Налоговые обязательства:</strong> Автоматический расчет налогов на наследство</li>
|
||||
</ul>
|
||||
|
||||
<h4>🔐 Безопасность и контроль:</h4>
|
||||
<ul>
|
||||
<li>Все изменения наследников требуют подтверждения через governance</li>
|
||||
<li>Криптографическая защита данных о наследниках</li>
|
||||
<li>Аудит всех операций наследования</li>
|
||||
<li>Возможность экстренной блокировки в случае споров</li>
|
||||
<li>Интеграция с системой идентификации для подтверждения личности</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Архитектура модуля -->
|
||||
<div class="module-architecture">
|
||||
<div class="architecture-card">
|
||||
<h3>🏗️ Архитектура InheritanceModule</h3>
|
||||
<div class="architecture-content">
|
||||
<div class="architecture-diagram">
|
||||
<div class="diagram-row">
|
||||
<div class="diagram-item tokenholder">
|
||||
<h5>👤 Токенхолдер</h5>
|
||||
<ul>
|
||||
<li>Назначает наследников</li>
|
||||
<li>Устанавливает доли</li>
|
||||
<li>Управляет условиями</li>
|
||||
<li>Может отозвать</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="diagram-arrow">→</div>
|
||||
<div class="diagram-item inheritance">
|
||||
<h5>🏛️ InheritanceModule</h5>
|
||||
<ul>
|
||||
<li>Хранит данные наследников</li>
|
||||
<li>Проверяет условия</li>
|
||||
<li>Выполняет передачу</li>
|
||||
<li>Ведет аудит</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="diagram-arrow">→</div>
|
||||
<div class="diagram-item heirs">
|
||||
<h5>👥 Наследники</h5>
|
||||
<ul>
|
||||
<li>Получают токены</li>
|
||||
<li>Подтверждают получение</li>
|
||||
<li>Управляют наследством</li>
|
||||
<li>Планируют налоги</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Типы наследования -->
|
||||
<div class="inheritance-types">
|
||||
<div class="types-card">
|
||||
<h3>📊 Типы наследования</h3>
|
||||
<div class="types-grid">
|
||||
<div class="type-item">
|
||||
<h4>👨👩👧👦 Семейное наследование</h4>
|
||||
<p>Передача токенов членам семьи согласно традиционным схемам</p>
|
||||
<ul>
|
||||
<li>Супруг/супруга (50%)</li>
|
||||
<li>Дети (равные доли)</li>
|
||||
<li>Родители (при отсутствии детей)</li>
|
||||
<li>Братья/сестры (при отсутствии родителей)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="type-item">
|
||||
<h4>🏢 Корпоративное наследование</h4>
|
||||
<p>Передача токенов в рамках бизнес-структур и организаций</p>
|
||||
<ul>
|
||||
<li>Партнеры по бизнесу</li>
|
||||
<li>Ключевые сотрудники</li>
|
||||
<li>Дочерние компании</li>
|
||||
<li>Благотворительные фонды</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="type-item">
|
||||
<h4>🎯 Целевое наследование</h4>
|
||||
<p>Передача токенов для достижения конкретных целей</p>
|
||||
<ul>
|
||||
<li>Образовательные учреждения</li>
|
||||
<li>Исследовательские проекты</li>
|
||||
<li>Экологические инициативы</li>
|
||||
<li>Социальные программы</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="type-item">
|
||||
<h4>⏰ Условное наследование</h4>
|
||||
<p>Передача токенов при выполнении определенных условий</p>
|
||||
<ul>
|
||||
<li>Достижение определенного возраста</li>
|
||||
<li>Завершение образования</li>
|
||||
<li>Создание семьи</li>
|
||||
<li>Достижение карьерных целей</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Примеры использования -->
|
||||
<div class="usage-examples">
|
||||
<div class="examples-card">
|
||||
<h3>💡 Примеры использования</h3>
|
||||
<div class="examples-content">
|
||||
<div class="example-item">
|
||||
<h4>👨👩👧👦 Семейное планирование</h4>
|
||||
<div class="example-code">
|
||||
<pre><code>// Назначение наследников для семьи
|
||||
function setFamilyInheritance() {
|
||||
setHeir(spouse, 50); // Супруг 50%
|
||||
setHeir(son, 25); // Сын 25%
|
||||
setHeir(daughter, 25); // Дочь 25%
|
||||
setActivationCondition("death");
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-item">
|
||||
<h4>🏢 Бизнес-преемственность</h4>
|
||||
<div class="example-code">
|
||||
<pre><code>// Передача бизнеса партнеру
|
||||
function setBusinessInheritance() {
|
||||
setHeir(businessPartner, 100); // Партнер 100%
|
||||
setActivationCondition("death");
|
||||
setTimeLock(365 days); // Минимум 1 год владения
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-item">
|
||||
<h4>🎯 Благотворительное наследование</h4>
|
||||
<div class="example-code">
|
||||
<pre><code>// Передача в благотворительный фонд
|
||||
function setCharityInheritance() {
|
||||
setHeir(environmentalFund, 70); // Экологический фонд 70%
|
||||
setHeir(educationFund, 30); // Образовательный фонд 30%
|
||||
setActivationCondition("death");
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Юридические аспекты -->
|
||||
<div class="legal-aspects">
|
||||
<div class="legal-card">
|
||||
<h3>⚖️ Юридические аспекты</h3>
|
||||
<div class="legal-content">
|
||||
<div class="legal-section">
|
||||
<h4>📜 Соответствие законодательству</h4>
|
||||
<ul>
|
||||
<li><strong>Гражданский кодекс:</strong> Соответствие нормам наследования</li>
|
||||
<li><strong>Налоговый кодекс:</strong> Правильный расчет налогов на наследство</li>
|
||||
<li><strong>Семейный кодекс:</strong> Учет семейных обязательств</li>
|
||||
<li><strong>Международное право:</strong> Наследование в разных юрисдикциях</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="legal-section">
|
||||
<h4>🔍 Процедура подтверждения</h4>
|
||||
<ul>
|
||||
<li><strong>Свидетельство о смерти:</strong> Официальное подтверждение</li>
|
||||
<li><strong>Медицинское заключение:</strong> При недееспособности</li>
|
||||
<li><strong>Судебное решение:</strong> При спорах о наследстве</li>
|
||||
<li><strong>Нотариальное заверение:</strong> Документов о наследниках</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="legal-section">
|
||||
<h4>💰 Налоговые обязательства</h4>
|
||||
<ul>
|
||||
<li><strong>Налог на наследство:</strong> Автоматический расчет</li>
|
||||
<li><strong>НДФЛ:</strong> При получении токенов</li>
|
||||
<li><strong>Отчетность:</strong> Автоматическая подача деклараций</li>
|
||||
<li><strong>Льготы:</strong> Учет налоговых льгот для наследников</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Статус разработки -->
|
||||
<div class="development-status">
|
||||
<div class="status-card">
|
||||
<h3>🚧 Статус разработки</h3>
|
||||
<div class="status-content">
|
||||
<p><strong>InheritanceModule находится в стадии планирования.</strong></p>
|
||||
<p>Модуль будет включать:</p>
|
||||
<ul>
|
||||
<li>✅ Систему назначения наследников</li>
|
||||
<li>✅ Управление долями и условиями</li>
|
||||
<li>✅ Интеграцию с юридическими системами</li>
|
||||
<li>✅ Автоматическую передачу токенов</li>
|
||||
<li>✅ Налоговые расчеты</li>
|
||||
<li>✅ Аудит и мониторинг</li>
|
||||
<li>✅ Разрешение споров</li>
|
||||
</ul>
|
||||
<p><em>Модуль будет доступен в следующих обновлениях DLE.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import api from '../../../api/axios';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
isAuthenticated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
identities: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
tokenBalances: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLoadingTokens: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
// Router
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// Состояние
|
||||
const selectedDle = ref(null);
|
||||
const isLoadingDle = ref(false);
|
||||
|
||||
// Получаем адрес DLE из URL
|
||||
const dleAddress = computed(() => route.query.address);
|
||||
|
||||
// Загрузка данных DLE
|
||||
const loadDleData = async () => {
|
||||
if (!dleAddress.value) return;
|
||||
|
||||
try {
|
||||
isLoadingDle.value = true;
|
||||
const response = await api.post('/blockchain/read-dle-info', {
|
||||
dleAddress: dleAddress.value
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
selectedDle.value = response.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных DLE:', error);
|
||||
} finally {
|
||||
isLoadingDle.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Загружаем данные при монтировании
|
||||
onMounted(() => {
|
||||
loadDleData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-deploy-page {
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2.5rem;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.header-content p {
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 1.1rem;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dle-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: monospace;
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: var(--color-grey-dark);
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.module-description,
|
||||
.module-architecture,
|
||||
.inheritance-types,
|
||||
.usage-examples,
|
||||
.legal-aspects,
|
||||
.development-status {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.description-card,
|
||||
.architecture-card,
|
||||
.types-card,
|
||||
.examples-card,
|
||||
.legal-card,
|
||||
.status-card {
|
||||
background: #f8f9fa;
|
||||
padding: 25px;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.description-card h3,
|
||||
.architecture-card h3,
|
||||
.types-card h3,
|
||||
.examples-card h3,
|
||||
.legal-card h3,
|
||||
.status-card h3 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.description-content h4 {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
|
||||
.description-content ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.description-content li {
|
||||
margin: 5px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Архитектурная диаграмма */
|
||||
.architecture-diagram {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.diagram-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.diagram-item {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
text-align: center;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.diagram-item.tokenholder {
|
||||
background: #e8f5e8;
|
||||
border: 2px solid #4caf50;
|
||||
}
|
||||
|
||||
.diagram-item.inheritance {
|
||||
background: #fff3e0;
|
||||
border: 2px solid #ff9800;
|
||||
}
|
||||
|
||||
.diagram-item.heirs {
|
||||
background: #f3e5f5;
|
||||
border: 2px solid #9c27b0;
|
||||
}
|
||||
|
||||
.diagram-item h5 {
|
||||
margin: 0 0 15px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.diagram-item ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.diagram-item li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.diagram-arrow {
|
||||
font-size: 2rem;
|
||||
color: var(--color-primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Типы наследования */
|
||||
.types-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.type-item {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.type-item h4 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.type-item p {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.type-item ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.type-item li {
|
||||
margin: 5px 0;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
/* Примеры использования */
|
||||
.examples-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.example-item {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.example-item h4 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.example-code {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 15px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.example-code pre {
|
||||
margin: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.example-code code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Юридические аспекты */
|
||||
.legal-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.legal-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.legal-section h4 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.legal-section ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.legal-section li {
|
||||
margin: 8px 0;
|
||||
line-height: 1.5;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
/* Статус разработки */
|
||||
.status-content {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.status-content p {
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.status-content ul {
|
||||
margin: 15px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.status-content li {
|
||||
margin: 5px 0;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
.status-content em {
|
||||
color: var(--color-primary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.diagram-row {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.diagram-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.types-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.legal-content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,485 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="module-deploy-page">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>🚀 Деплой MintModule</h1>
|
||||
<p>Модуль для выпуска новых токенов DLE через governance</p>
|
||||
<div v-if="selectedDle" class="dle-info">
|
||||
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
|
||||
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Описание модуля -->
|
||||
<div class="module-description">
|
||||
<div class="description-card">
|
||||
<h3>📋 Описание MintModule</h3>
|
||||
<div class="description-content">
|
||||
<p><strong>MintModule</strong> - это модуль для управления выпуском новых токенов DLE через систему governance.</p>
|
||||
|
||||
<h4>🔧 Функциональность:</h4>
|
||||
<ul>
|
||||
<li><strong>Выпуск токенов:</strong> Создание дополнительных токенов DLE</li>
|
||||
<li><strong>Governance:</strong> Все операции требуют голосования и кворума</li>
|
||||
<li><strong>Безопасность:</strong> Контролируемый выпуск через коллективные решения</li>
|
||||
<li><strong>Прозрачность:</strong> Все операции записываются в блокчейн</li>
|
||||
</ul>
|
||||
|
||||
<h4>⚠️ Важные особенности:</h4>
|
||||
<ul>
|
||||
<li>Выпуск токенов возможен только через предложения и голосование</li>
|
||||
<li>Новые токены могут быть распределены между участниками или добавлены в казну DLE</li>
|
||||
<li>Все операции требуют достижения кворума</li>
|
||||
<li>История всех выпусков сохраняется в блокчейне</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя -->
|
||||
<div class="deploy-form-section">
|
||||
<div class="form-header">
|
||||
<h3>⚙️ Настройки деплоя</h3>
|
||||
<p>Настройте параметры для деплоя MintModule</p>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="deployModule" class="deploy-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="moduleName">Название модуля:</label>
|
||||
<input
|
||||
id="moduleName"
|
||||
v-model="deployData.moduleName"
|
||||
type="text"
|
||||
placeholder="MintModule"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="moduleVersion">Версия модуля:</label>
|
||||
<input
|
||||
id="moduleVersion"
|
||||
v-model="deployData.moduleVersion"
|
||||
type="text"
|
||||
placeholder="1.0.0"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="moduleDescription">Описание модуля:</label>
|
||||
<textarea
|
||||
id="moduleDescription"
|
||||
v-model="deployData.moduleDescription"
|
||||
placeholder="Модуль для выпуска новых токенов DLE через governance..."
|
||||
rows="3"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="maxMintPerProposal">Максимальный выпуск за одно предложение:</label>
|
||||
<input
|
||||
id="maxMintPerProposal"
|
||||
v-model="deployData.maxMintPerProposal"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
placeholder="1000000"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Максимальное количество токенов, которое можно выпустить за одно предложение</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mintCooldown">Кулдаун между выпусками (часы):</label>
|
||||
<input
|
||||
id="mintCooldown"
|
||||
v-model="deployData.mintCooldown"
|
||||
type="number"
|
||||
min="0"
|
||||
step="1"
|
||||
placeholder="24"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Минимальное время между успешными выпусками токенов</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="deployDescription">Описание предложения для деплоя:</label>
|
||||
<textarea
|
||||
id="deployDescription"
|
||||
v-model="deployData.deployDescription"
|
||||
placeholder="Предложение о деплое MintModule для управления выпуском токенов DLE..."
|
||||
rows="3"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="votingDuration">Длительность голосования (часы):</label>
|
||||
<input
|
||||
id="votingDuration"
|
||||
v-model="deployData.votingDuration"
|
||||
type="number"
|
||||
min="1"
|
||||
max="168"
|
||||
placeholder="24"
|
||||
required
|
||||
/>
|
||||
<small class="form-help">Время для голосования по предложению деплоя (1-168 часов)</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary" :disabled="isDeploying">
|
||||
{{ isDeploying ? 'Деплой...' : 'Деплой MintModule' }}
|
||||
</button>
|
||||
|
||||
<!-- Статус деплоя -->
|
||||
<div v-if="deployStatus" class="deploy-status">
|
||||
<p class="status-message">{{ deployStatus }}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import api from '../../../api/axios';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
isAuthenticated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
identities: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
tokenBalances: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLoadingTokens: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
// Router
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// Состояние
|
||||
const isDeploying = ref(false);
|
||||
const deployStatus = ref('');
|
||||
const selectedDle = ref(null);
|
||||
const isLoadingDle = ref(false);
|
||||
|
||||
// Данные для деплоя
|
||||
const deployData = ref({
|
||||
moduleName: 'MintModule',
|
||||
moduleVersion: '1.0.0',
|
||||
moduleDescription: 'Модуль для выпуска новых токенов DLE через governance',
|
||||
maxMintPerProposal: 1000000,
|
||||
mintCooldown: 24,
|
||||
deployDescription: 'Предложение о деплое MintModule для управления выпуском токенов DLE',
|
||||
votingDuration: 24
|
||||
});
|
||||
|
||||
// Получаем адрес DLE из URL
|
||||
const dleAddress = computed(() => route.query.address);
|
||||
|
||||
// Загрузка данных DLE
|
||||
const loadDleData = async () => {
|
||||
if (!dleAddress.value) return;
|
||||
|
||||
try {
|
||||
isLoadingDle.value = true;
|
||||
const response = await api.post('/blockchain/read-dle-info', {
|
||||
dleAddress: dleAddress.value
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
selectedDle.value = response.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных DLE:', error);
|
||||
} finally {
|
||||
isLoadingDle.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Функция деплоя модуля
|
||||
const deployModule = async () => {
|
||||
if (isDeploying.value) return;
|
||||
|
||||
try {
|
||||
isDeploying.value = true;
|
||||
deployStatus.value = 'Подготовка к деплою...';
|
||||
|
||||
// Здесь будет логика деплоя модуля
|
||||
console.log('Деплой MintModule:', deployData.value);
|
||||
|
||||
// Временная заглушка
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
deployStatus.value = 'Модуль успешно развернут!';
|
||||
|
||||
// Очищаем статус через 3 секунды
|
||||
setTimeout(() => {
|
||||
deployStatus.value = '';
|
||||
}, 3000);
|
||||
|
||||
alert('MintModule успешно развернут!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка деплоя модуля:', error);
|
||||
deployStatus.value = 'Ошибка деплоя модуля';
|
||||
|
||||
setTimeout(() => {
|
||||
deployStatus.value = '';
|
||||
}, 3000);
|
||||
|
||||
alert('Ошибка при деплое модуля');
|
||||
} finally {
|
||||
isDeploying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Загружаем данные при монтировании
|
||||
onMounted(() => {
|
||||
loadDleData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-deploy-page {
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2.5rem;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.header-content p {
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 1.1rem;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dle-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: monospace;
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: var(--color-grey-dark);
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.module-description {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.description-card {
|
||||
background: #f8f9fa;
|
||||
padding: 25px;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.description-card h3 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.description-content h4 {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
|
||||
.description-content ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.description-content li {
|
||||
margin: 5px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.deploy-form-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-header {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-header h3 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.form-header p {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.deploy-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: 600;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group textarea {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.form-help {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-grey-dark);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.deploy-status {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #e8f5e8;
|
||||
border-radius: var(--radius-sm);
|
||||
border-left: 4px solid #28a745;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,205 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="module-deploy-form">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Деплой пользовательского модуля</h1>
|
||||
<p>Создание и деплой кастомного модуля для DLE</p>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management/modules')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Информация о пользовательских модулях -->
|
||||
<div class="module-info">
|
||||
<div class="info-card">
|
||||
<h3>🔧 Пользовательские модули</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<strong>Назначение:</strong> Расширение функциональности DLE
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Возможности:</strong> Любая кастомная логика, интеграции, API
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Безопасность:</strong> Проверка через голосование токен-холдеров
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя будет добавлена позже -->
|
||||
<div class="deploy-form-placeholder">
|
||||
<div class="placeholder-content">
|
||||
<h3>🚧 Форма деплоя в разработке</h3>
|
||||
<p>Здесь будет универсальная форма для деплоя пользовательских модулей</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, ref, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
isAuthenticated: { type: Boolean, default: false },
|
||||
identities: { type: Array, default: () => [] },
|
||||
tokenBalances: { type: Object, default: () => ({}) },
|
||||
isLoadingTokens: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
// Определяем emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// Состояние
|
||||
const isLoading = ref(false);
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
console.log('[ModuleDeployFormView] Страница загружена');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-deploy-form {
|
||||
padding: 20px;
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 10px 0 0 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Информация о модуле */
|
||||
.module-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.info-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.info-item strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Плейсхолдер для формы */
|
||||
.deploy-form-placeholder {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
border: 2px dashed #dee2e6;
|
||||
}
|
||||
|
||||
.placeholder-content h3 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.placeholder-content p {
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,584 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="module-deploy-page">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>🔗 Деплой OracleModule</h1>
|
||||
<p>Модуль для интеграции с внешними данными и автоматизации DLE</p>
|
||||
<div v-if="selectedDle" class="dle-info">
|
||||
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
|
||||
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Описание модуля -->
|
||||
<div class="module-description">
|
||||
<div class="description-card">
|
||||
<h3>📋 Описание OracleModule</h3>
|
||||
<div class="description-content">
|
||||
<p><strong>OracleModule</strong> - это модуль для интеграции DLE с внешними источниками данных и автоматизации процессов на основе реальных событий.</p>
|
||||
|
||||
<h4>🔧 Основная функциональность:</h4>
|
||||
<ul>
|
||||
<li><strong>Интеграция с IoT:</strong> Получение данных от датчиков, производственных линий, оборудования</li>
|
||||
<li><strong>API интеграция:</strong> Подключение к внешним системам, ERP, CRM, аналитическим платформам</li>
|
||||
<li><strong>Автоматические триггеры:</strong> Создание предложений на основе внешних событий</li>
|
||||
<li><strong>Валидация данных:</strong> Проверка достоверности полученной информации</li>
|
||||
<li><strong>Множественные оракулы:</strong> Подтверждение данных от нескольких источников</li>
|
||||
</ul>
|
||||
|
||||
<h4>🏭 Примеры применения:</h4>
|
||||
<ul>
|
||||
<li><strong>Производственные токены:</strong> Автоматический выпуск токенов за завершение партий продукции</li>
|
||||
<li><strong>Качественные бонусы:</strong> Токены за высокое качество продукции на основе данных датчиков</li>
|
||||
<li><strong>Экологические токены:</strong> Вознаграждения за снижение энергопотребления и выбросов</li>
|
||||
<li><strong>Инновационные токены:</strong> Токены за внедрение новых технологий и процессов</li>
|
||||
<li><strong>Рыночные корректировки:</strong> Автоматическая адаптация стратегии на основе рыночных данных</li>
|
||||
</ul>
|
||||
|
||||
<h4>🔐 Безопасность и контроль:</h4>
|
||||
<ul>
|
||||
<li>Все оракулы должны быть авторизованы через governance</li>
|
||||
<li>Данные валидируются перед обработкой</li>
|
||||
<li>Критические решения требуют множественного подтверждения</li>
|
||||
<li>История всех оракульных данных сохраняется в блокчейне</li>
|
||||
<li>Возможность отключения оракулов в экстренных случаях</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Архитектура модуля -->
|
||||
<div class="module-architecture">
|
||||
<div class="architecture-card">
|
||||
<h3>🏗️ Архитектура OracleModule</h3>
|
||||
<div class="architecture-content">
|
||||
<div class="architecture-diagram">
|
||||
<div class="diagram-row">
|
||||
<div class="diagram-item external">
|
||||
<h5>🌐 Внешние источники</h5>
|
||||
<ul>
|
||||
<li>IoT датчики</li>
|
||||
<li>Производственные линии</li>
|
||||
<li>ERP/CRM системы</li>
|
||||
<li>Аналитические платформы</li>
|
||||
<li>Рыночные данные</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="diagram-arrow">→</div>
|
||||
<div class="diagram-item oracle">
|
||||
<h5>🔗 OracleModule</h5>
|
||||
<ul>
|
||||
<li>Получение данных</li>
|
||||
<li>Валидация</li>
|
||||
<li>Обработка</li>
|
||||
<li>Создание предложений</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="diagram-arrow">→</div>
|
||||
<div class="diagram-item dle">
|
||||
<h5>🏛️ DLE Governance</h5>
|
||||
<ul>
|
||||
<li>Предложения</li>
|
||||
<li>Голосование</li>
|
||||
<li>Исполнение</li>
|
||||
<li>Выпуск токенов</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Типы оракулов -->
|
||||
<div class="oracle-types">
|
||||
<div class="types-card">
|
||||
<h3>📊 Типы оракулов</h3>
|
||||
<div class="types-grid">
|
||||
<div class="type-item">
|
||||
<h4>🏭 Производственные оракулы</h4>
|
||||
<p>Данные от производственных линий, оборудования, датчиков качества</p>
|
||||
<ul>
|
||||
<li>Завершение партий продукции</li>
|
||||
<li>Показатели качества</li>
|
||||
<li>Энергопотребление</li>
|
||||
<li>Время простоя</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="type-item">
|
||||
<h4>📈 Рыночные оракулы</h4>
|
||||
<p>Рыночные данные, цены, спрос, конкуренция</p>
|
||||
<ul>
|
||||
<li>Цены на сырье</li>
|
||||
<li>Спрос на продукцию</li>
|
||||
<li>Конкурентные данные</li>
|
||||
<li>Экономические индикаторы</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="type-item">
|
||||
<h4>🌱 Экологические оракулы</h4>
|
||||
<p>Данные об экологическом воздействии и устойчивости</p>
|
||||
<ul>
|
||||
<li>Выбросы CO2</li>
|
||||
<li>Потребление энергии</li>
|
||||
<li>Переработка отходов</li>
|
||||
<li>Использование возобновляемых ресурсов</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="type-item">
|
||||
<h4>💼 Бизнес-оракулы</h4>
|
||||
<p>Бизнес-метрики, продажи, удовлетворенность клиентов</p>
|
||||
<ul>
|
||||
<li>Объемы продаж</li>
|
||||
<li>Удовлетворенность клиентов</li>
|
||||
<li>Эффективность процессов</li>
|
||||
<li>Финансовые показатели</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Примеры использования -->
|
||||
<div class="usage-examples">
|
||||
<div class="examples-card">
|
||||
<h3>💡 Примеры использования</h3>
|
||||
<div class="examples-content">
|
||||
<div class="example-item">
|
||||
<h4>🏭 Автоматические производственные токены</h4>
|
||||
<div class="example-code">
|
||||
<pre><code>// При завершении партии продукции
|
||||
function onBatchCompleted(uint256 quantity, uint256 quality) {
|
||||
uint256 tokens = quantity * quality * 10;
|
||||
createMintProposal(tokens, "Production reward");
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-item">
|
||||
<h4>🌱 Экологические бонусы</h4>
|
||||
<div class="example-code">
|
||||
<pre><code>// При снижении энергопотребления
|
||||
function onEnergyReduction(uint256 savedEnergy) {
|
||||
uint256 tokens = savedEnergy * 5;
|
||||
createMintProposal(tokens, "Energy efficiency bonus");
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-item">
|
||||
<h4>📈 Рыночная адаптация</h4>
|
||||
<div class="example-code">
|
||||
<pre><code>// При изменении рыночных условий
|
||||
function onMarketChange(uint256 priceChange) {
|
||||
if (priceChange > 10) {
|
||||
createStrategyProposal("Increase production");
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Статус разработки -->
|
||||
<div class="development-status">
|
||||
<div class="status-card">
|
||||
<h3>🚧 Статус разработки</h3>
|
||||
<div class="status-content">
|
||||
<p><strong>OracleModule находится в стадии планирования.</strong></p>
|
||||
<p>Модуль будет включать:</p>
|
||||
<ul>
|
||||
<li>✅ Систему авторизации оракулов</li>
|
||||
<li>✅ Валидацию и обработку данных</li>
|
||||
<li>✅ Интеграцию с IoT и API</li>
|
||||
<li>✅ Автоматические триггеры</li>
|
||||
<li>✅ Множественное подтверждение данных</li>
|
||||
<li>✅ Аудит и мониторинг</li>
|
||||
</ul>
|
||||
<p><em>Модуль будет доступен в следующих обновлениях DLE.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import api from '../../../api/axios';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
isAuthenticated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
identities: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
tokenBalances: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLoadingTokens: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
// Router
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// Состояние
|
||||
const selectedDle = ref(null);
|
||||
const isLoadingDle = ref(false);
|
||||
|
||||
// Получаем адрес DLE из URL
|
||||
const dleAddress = computed(() => route.query.address);
|
||||
|
||||
// Загрузка данных DLE
|
||||
const loadDleData = async () => {
|
||||
if (!dleAddress.value) return;
|
||||
|
||||
try {
|
||||
isLoadingDle.value = true;
|
||||
const response = await api.post('/blockchain/read-dle-info', {
|
||||
dleAddress: dleAddress.value
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
selectedDle.value = response.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных DLE:', error);
|
||||
} finally {
|
||||
isLoadingDle.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Загружаем данные при монтировании
|
||||
onMounted(() => {
|
||||
loadDleData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.module-deploy-page {
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2.5rem;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.header-content p {
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 1.1rem;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dle-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: monospace;
|
||||
color: var(--color-grey-dark);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: var(--color-grey-dark);
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.module-description,
|
||||
.module-architecture,
|
||||
.oracle-types,
|
||||
.usage-examples,
|
||||
.development-status {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.description-card,
|
||||
.architecture-card,
|
||||
.types-card,
|
||||
.examples-card,
|
||||
.status-card {
|
||||
background: #f8f9fa;
|
||||
padding: 25px;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.description-card h3,
|
||||
.architecture-card h3,
|
||||
.types-card h3,
|
||||
.examples-card h3,
|
||||
.status-card h3 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.description-content h4 {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
|
||||
.description-content ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.description-content li {
|
||||
margin: 5px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Архитектурная диаграмма */
|
||||
.architecture-diagram {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.diagram-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.diagram-item {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
text-align: center;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.diagram-item.external {
|
||||
background: #e3f2fd;
|
||||
border: 2px solid #2196f3;
|
||||
}
|
||||
|
||||
.diagram-item.oracle {
|
||||
background: #f3e5f5;
|
||||
border: 2px solid #9c27b0;
|
||||
}
|
||||
|
||||
.diagram-item.dle {
|
||||
background: #e8f5e8;
|
||||
border: 2px solid #4caf50;
|
||||
}
|
||||
|
||||
.diagram-item h5 {
|
||||
margin: 0 0 15px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.diagram-item ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.diagram-item li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.diagram-arrow {
|
||||
font-size: 2rem;
|
||||
color: var(--color-primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Типы оракулов */
|
||||
.types-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.type-item {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.type-item h4 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.type-item p {
|
||||
color: var(--color-grey-dark);
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.type-item ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.type-item li {
|
||||
margin: 5px 0;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
/* Примеры использования */
|
||||
.examples-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.example-item {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.example-item h4 {
|
||||
color: var(--color-primary);
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.example-code {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 15px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.example-code pre {
|
||||
margin: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.example-code code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Статус разработки */
|
||||
.status-content {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.status-content p {
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.status-content ul {
|
||||
margin: 15px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.status-content li {
|
||||
margin: 5px 0;
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
.status-content em {
|
||||
color: var(--color-primary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.diagram-row {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.diagram-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.types-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,673 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="timelock-module-deploy">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Деплой TimelockModule</h1>
|
||||
<p>Задержки исполнения - безопасность критических операций через таймлоки</p>
|
||||
<p v-if="dleAddress" class="dle-address">
|
||||
<strong>DLE:</strong> {{ dleAddress }}
|
||||
</p>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management/modules')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Информация о модуле -->
|
||||
<div class="module-info">
|
||||
<div class="info-card">
|
||||
<h3>⏰ TimelockModule</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<strong>Назначение:</strong> Безопасность критических операций
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Функции:</strong> Настраиваемые таймлоки, отмена предложений, аудит
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Безопасность:</strong> Задержки исполнения для защиты от атак
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя модуля администратором -->
|
||||
<div v-if="canManageSettings" class="deploy-form">
|
||||
<div class="form-header">
|
||||
<h3>🔧 Деплой TimelockModule администратором</h3>
|
||||
<p>Администратор деплоит модуль, затем создает предложение для добавления в DLE</p>
|
||||
</div>
|
||||
|
||||
<div class="form-content">
|
||||
<!-- Информация о сетях -->
|
||||
<div class="networks-info">
|
||||
<h4>📡 Сети для деплоя:</h4>
|
||||
<div class="networks-list">
|
||||
<div class="network-item">
|
||||
<span class="network-name">Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 11155111</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Holesky</span>
|
||||
<span class="network-chain-id">Chain ID: 17000</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Arbitrum Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 421614</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Base Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 84532</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Настройки модуля -->
|
||||
<div class="module-settings">
|
||||
<h4>⚙️ Настройки TimelockModule:</h4>
|
||||
|
||||
<div class="settings-form">
|
||||
<!-- Поля администратора -->
|
||||
<div class="admin-section">
|
||||
<h5>🔐 Настройки администратора:</h5>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="adminPrivateKey">Приватный ключ администратора:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="adminPrivateKey"
|
||||
v-model="moduleSettings.adminPrivateKey"
|
||||
class="form-control"
|
||||
placeholder="0x..."
|
||||
required
|
||||
>
|
||||
<small class="form-help">Приватный ключ для деплоя модуля (администратор платит газ)</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="etherscanApiKey">Etherscan API ключ:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="etherscanApiKey"
|
||||
v-model="moduleSettings.etherscanApiKey"
|
||||
class="form-control"
|
||||
placeholder="YourAPIKey..."
|
||||
>
|
||||
<small class="form-help">API ключ для автоматической верификации контрактов</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="simple-info">
|
||||
<h5>📋 Информация о TimelockModule:</h5>
|
||||
<div class="info-text">
|
||||
<p><strong>TimelockModule</strong> будет задеплоен с настройками по умолчанию:</p>
|
||||
<ul>
|
||||
<li>✅ Стандартная задержка: 2 дня</li>
|
||||
<li>✅ Экстренная задержка: 30 минут</li>
|
||||
<li>✅ Автоматическое исполнение операций</li>
|
||||
<li>✅ Готовые настройки безопасности</li>
|
||||
</ul>
|
||||
<p><em>После деплоя настройки можно будет изменить через governance.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка деплоя -->
|
||||
<div class="deploy-actions">
|
||||
<button
|
||||
class="btn btn-primary btn-large deploy-module"
|
||||
@click="deployTimelockModule"
|
||||
:disabled="isDeploying || !dleAddress || !isFormValid"
|
||||
>
|
||||
<i class="fas fa-rocket" :class="{ 'fa-spin': isDeploying }"></i>
|
||||
{{ isDeploying ? 'Деплой модуля...' : 'Деплой TimelockModule' }}
|
||||
</button>
|
||||
|
||||
<div v-if="!isFormValid && !isDeploying" class="form-validation-info">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span>Заполните приватный ключ и API ключ для деплоя</span>
|
||||
</div>
|
||||
|
||||
<div v-if="deploymentProgress" class="deployment-progress">
|
||||
<div class="progress-info">
|
||||
<span>{{ deploymentProgress.message }}</span>
|
||||
<span class="progress-percentage">{{ deploymentProgress.percentage }}%</span>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: deploymentProgress.percentage + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Сообщение для пользователей без прав доступа -->
|
||||
<div v-if="!canManageSettings" class="no-access-message">
|
||||
<div class="message-content">
|
||||
<h3>🔒 Нет прав доступа</h3>
|
||||
<p>У вас нет прав для деплоя смарт-контрактов. Только пользователи с ролью Editor могут выполнять деплой.</p>
|
||||
<button class="btn btn-secondary" @click="router.push('/management/modules')">
|
||||
← Вернуться к модулям
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, ref, computed, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
isAuthenticated: { type: Boolean, default: false },
|
||||
identities: { type: Array, default: () => [] },
|
||||
tokenBalances: { type: Object, default: () => ({}) },
|
||||
isLoadingTokens: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
// Определяем emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { canEdit, canManageSettings } = usePermissions();
|
||||
|
||||
// Состояние
|
||||
const isLoading = ref(false);
|
||||
const dleAddress = ref(route.query.address || null);
|
||||
const isDeploying = ref(false);
|
||||
const deploymentProgress = ref(null);
|
||||
|
||||
// Настройки модуля
|
||||
const moduleSettings = ref({
|
||||
// Поля администратора
|
||||
adminPrivateKey: '',
|
||||
etherscanApiKey: ''
|
||||
});
|
||||
|
||||
// Проверка валидности формы
|
||||
const isFormValid = computed(() => {
|
||||
return moduleSettings.value.adminPrivateKey && moduleSettings.value.etherscanApiKey;
|
||||
});
|
||||
|
||||
// Функция деплоя TimelockModule
|
||||
async function deployTimelockModule() {
|
||||
if (!canManageSettings.value) {
|
||||
alert('У вас нет прав для деплоя смарт-контрактов');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isDeploying.value = true;
|
||||
deploymentProgress.value = {
|
||||
message: 'Инициализация деплоя...',
|
||||
percentage: 0
|
||||
};
|
||||
|
||||
console.log('[TimelockModuleDeployView] Начинаем деплой TimelockModule для DLE:', dleAddress.value);
|
||||
|
||||
// Вызываем API для деплоя модуля администратором
|
||||
const response = await fetch('/api/dle-modules/deploy-timelock-admin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dleAddress: dleAddress.value,
|
||||
moduleType: 'timelock',
|
||||
adminPrivateKey: moduleSettings.value.adminPrivateKey,
|
||||
etherscanApiKey: moduleSettings.value.etherscanApiKey,
|
||||
settings: {
|
||||
// Используем настройки по умолчанию
|
||||
useDefaultSettings: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
console.log('[TimelockModuleDeployView] Деплой успешно запущен:', result);
|
||||
|
||||
// Обновляем прогресс
|
||||
deploymentProgress.value = {
|
||||
message: 'Деплой запущен успешно! Проверьте логи для отслеживания прогресса.',
|
||||
percentage: 100
|
||||
};
|
||||
|
||||
// Показываем детальную информацию о деплое
|
||||
const deployInfo = result.data || {};
|
||||
const deployedAddresses = deployInfo.addresses || [];
|
||||
|
||||
let successMessage = '✅ TimelockModule успешно задеплоен!\n\n';
|
||||
successMessage += `📊 Детали деплоя:\n`;
|
||||
successMessage += `• DLE: ${dleAddress.value}\n`;
|
||||
successMessage += `• Тип модуля: TimelockModule\n`;
|
||||
successMessage += `• Адрес модуля: ${deployInfo.moduleAddress || 'Не указан'}\n`;
|
||||
|
||||
if (deployedAddresses.length > 0) {
|
||||
successMessage += `\n🌐 Задеплоенные адреса:\n`;
|
||||
deployedAddresses.forEach((addr, index) => {
|
||||
successMessage += `${index + 1}. ${addr.network}: ${addr.address}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
successMessage += `\n📝 Следующий шаг: Создайте предложение для добавления модуля в DLE через governance.`;
|
||||
|
||||
alert(successMessage);
|
||||
|
||||
// Перенаправляем обратно к модулям
|
||||
setTimeout(() => {
|
||||
router.push(`/management/modules?address=${dleAddress.value}`);
|
||||
}, 3000);
|
||||
|
||||
} else {
|
||||
throw new Error(result.error || 'Неизвестная ошибка');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[TimelockModuleDeployView] Ошибка деплоя:', error);
|
||||
alert('❌ Ошибка деплоя: ' + error.message);
|
||||
|
||||
deploymentProgress.value = {
|
||||
message: 'Ошибка деплоя: ' + error.message,
|
||||
percentage: 0
|
||||
};
|
||||
} finally {
|
||||
isDeploying.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
console.log('[TimelockModuleDeployView] Страница загружена');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.timelock-module-deploy {
|
||||
padding: 20px;
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 10px 0 0 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
margin-top: 10px !important;
|
||||
font-family: monospace;
|
||||
background: #f8f9fa;
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Форма деплоя */
|
||||
.deploy-form {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.form-header h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.form-header p {
|
||||
margin: 0 0 20px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.networks-info,
|
||||
.module-settings {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Секция администратора */
|
||||
.admin-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: #fff3cd;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.admin-section h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #856404;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Дополнительные настройки */
|
||||
.advanced-settings {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.advanced-settings h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.advanced-settings .form-row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.advanced-settings .form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.advanced-settings .form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.deploy-actions {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
padding: 16px 32px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.deployment-progress {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress-percentage {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Информация о модуле */
|
||||
.module-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.info-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.info-item strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Плейсхолдер для формы */
|
||||
.deploy-form-placeholder {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
border: 2px dashed #dee2e6;
|
||||
}
|
||||
|
||||
.placeholder-content h3 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.placeholder-content p {
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Сообщение об отсутствии прав доступа */
|
||||
.no-access-message {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message-content h3 {
|
||||
color: #856404;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.message-content p {
|
||||
color: #856404;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message-content .btn {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.message-content .btn:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,886 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
All rights reserved.
|
||||
|
||||
This software is proprietary and confidential.
|
||||
Unauthorized copying, modification, or distribution is prohibited.
|
||||
|
||||
For licensing inquiries: info@hb3-accelerator.com
|
||||
Website: https://hb3-accelerator.com
|
||||
GitHub: https://github.com/HB3-ACCELERATOR
|
||||
-->
|
||||
|
||||
<template>
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="treasury-module-deploy">
|
||||
<!-- Заголовок -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Деплой TreasuryModule</h1>
|
||||
<p>Казначейство DLE - управление финансами, депозиты, выводы, дивиденды</p>
|
||||
<p v-if="dleAddress" class="dle-address">
|
||||
<strong>DLE:</strong> {{ dleAddress }}
|
||||
</p>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management/modules')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Информация о модуле -->
|
||||
<div class="module-info">
|
||||
<div class="info-card">
|
||||
<h3>💰 TreasuryModule</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<strong>Назначение:</strong> Управление финансами DLE
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Функции:</strong> Депозиты, выводы, дивиденды, бюджетирование
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Безопасность:</strong> Все операции через голосование
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Форма деплоя модуля администратором -->
|
||||
<div v-if="canManageSettings" class="deploy-form">
|
||||
<div class="form-header">
|
||||
<h3>🌐 Деплой TreasuryModule во всех сетях</h3>
|
||||
<p>Администратор деплоит модуль во всех 4 сетях одновременно, затем создает предложение для добавления в DLE</p>
|
||||
</div>
|
||||
|
||||
<div class="form-content">
|
||||
<!-- Информация о сетях -->
|
||||
<div class="networks-info">
|
||||
<h4>📡 Сети для деплоя:</h4>
|
||||
<div class="networks-list">
|
||||
<div class="network-item">
|
||||
<span class="network-name">Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 11155111</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Holesky</span>
|
||||
<span class="network-chain-id">Chain ID: 17000</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Arbitrum Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 421614</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-name">Base Sepolia</span>
|
||||
<span class="network-chain-id">Chain ID: 84532</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Настройки модуля -->
|
||||
<div class="module-settings">
|
||||
<h4>⚙️ Настройки TreasuryModule:</h4>
|
||||
|
||||
<div class="settings-form">
|
||||
<!-- Поля администратора -->
|
||||
<div class="admin-section">
|
||||
<h5>🔐 Настройки администратора:</h5>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="adminPrivateKey">Приватный ключ администратора:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="adminPrivateKey"
|
||||
v-model="moduleSettings.adminPrivateKey"
|
||||
class="form-control"
|
||||
placeholder="0x..."
|
||||
required
|
||||
>
|
||||
<small class="form-help">Приватный ключ для деплоя модуля (администратор платит газ)</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="etherscanApiKey">Etherscan API ключ:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="etherscanApiKey"
|
||||
v-model="moduleSettings.etherscanApiKey"
|
||||
class="form-control"
|
||||
placeholder="YourAPIKey..."
|
||||
>
|
||||
<small class="form-help">API ключ для автоматической верификации контрактов</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="simple-info">
|
||||
<h5>📋 Информация о TreasuryModule:</h5>
|
||||
<div class="info-text">
|
||||
<p><strong>TreasuryModule</strong> будет задеплоен с настройками по умолчанию:</p>
|
||||
<ul>
|
||||
<li>✅ Поддержка ETH и основных ERC20 токенов</li>
|
||||
<li>✅ Стандартные задержки для безопасности</li>
|
||||
<li>✅ Автоматическая настройка для всех сетей</li>
|
||||
<li>✅ Готовые настройки безопасности</li>
|
||||
</ul>
|
||||
<p><em>После деплоя настройки можно будет изменить через governance.</em></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка деплоя -->
|
||||
<div class="deploy-actions">
|
||||
<button
|
||||
class="btn btn-primary btn-large deploy-module"
|
||||
@click="deployTreasuryModule"
|
||||
:disabled="isDeploying || !dleAddress || !isFormValid"
|
||||
>
|
||||
<i class="fas fa-rocket" :class="{ 'fa-spin': isDeploying }"></i>
|
||||
{{ isDeploying ? 'Деплой во всех сетях...' : 'Деплой TreasuryModule во всех сетях' }}
|
||||
</button>
|
||||
|
||||
<div v-if="!isFormValid && !isDeploying" class="form-validation-info">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span>Заполните приватный ключ и API ключ для деплоя</span>
|
||||
</div>
|
||||
|
||||
<div v-if="deploymentProgress" class="deployment-progress">
|
||||
<div class="progress-info">
|
||||
<span>{{ deploymentProgress.message }}</span>
|
||||
<span class="progress-percentage">{{ deploymentProgress.percentage }}%</span>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: deploymentProgress.percentage + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Сообщение для пользователей без прав доступа -->
|
||||
<div v-if="!canManageSettings" class="no-access-message">
|
||||
<div class="message-content">
|
||||
<h3>🔒 Нет прав доступа</h3>
|
||||
<p>У вас нет прав для деплоя смарт-контрактов. Только пользователи с ролью Editor могут выполнять деплой.</p>
|
||||
<button class="btn btn-secondary" @click="router.push('/management/modules')">
|
||||
← Вернуться к модулям
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, ref, computed, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../../components/BaseLayout.vue';
|
||||
import { usePermissions } from '@/composables/usePermissions';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
isAuthenticated: { type: Boolean, default: false },
|
||||
identities: { type: Array, default: () => [] },
|
||||
tokenBalances: { type: Object, default: () => ({}) },
|
||||
isLoadingTokens: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
// Определяем emits
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { canEdit, canManageSettings } = usePermissions();
|
||||
|
||||
// Состояние
|
||||
const isLoading = ref(false);
|
||||
const dleAddress = ref(route.query.address || null);
|
||||
const isDeploying = ref(false);
|
||||
const deploymentProgress = ref(null);
|
||||
|
||||
// Настройки модуля
|
||||
const moduleSettings = ref({
|
||||
// Поля администратора
|
||||
adminPrivateKey: '',
|
||||
etherscanApiKey: ''
|
||||
});
|
||||
|
||||
// Проверка валидности формы
|
||||
const isFormValid = computed(() => {
|
||||
return moduleSettings.value.adminPrivateKey && moduleSettings.value.etherscanApiKey;
|
||||
});
|
||||
|
||||
// Функция деплоя TreasuryModule
|
||||
async function deployTreasuryModule() {
|
||||
if (!canManageSettings.value) {
|
||||
alert('У вас нет прав для деплоя смарт-контрактов');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isDeploying.value = true;
|
||||
deploymentProgress.value = {
|
||||
message: 'Инициализация деплоя...',
|
||||
percentage: 0
|
||||
};
|
||||
|
||||
console.log('[TreasuryModuleDeployView] Начинаем деплой TreasuryModule для DLE:', dleAddress.value);
|
||||
|
||||
// Вызываем API для деплоя модуля администратором
|
||||
const response = await fetch('/api/dle-modules/deploy-treasury-admin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dleAddress: dleAddress.value,
|
||||
moduleType: 'treasury',
|
||||
adminPrivateKey: moduleSettings.value.adminPrivateKey,
|
||||
etherscanApiKey: moduleSettings.value.etherscanApiKey,
|
||||
settings: {
|
||||
// Используем настройки по умолчанию
|
||||
useDefaultSettings: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
console.log('[TreasuryModuleDeployView] Деплой успешно запущен:', result);
|
||||
|
||||
// Обновляем прогресс
|
||||
deploymentProgress.value = {
|
||||
message: 'Деплой запущен успешно! Проверьте логи для отслеживания прогресса.',
|
||||
percentage: 100
|
||||
};
|
||||
|
||||
// Показываем детальную информацию о деплое
|
||||
const deployInfo = result.data || {};
|
||||
const deployedAddresses = deployInfo.addresses || [];
|
||||
|
||||
let successMessage = '✅ TreasuryModule успешно задеплоен во всех сетях!\n\n';
|
||||
successMessage += `📊 Детали деплоя:\n`;
|
||||
successMessage += `• DLE: ${dleAddress.value}\n`;
|
||||
successMessage += `• Тип модуля: TreasuryModule\n`;
|
||||
successMessage += `• Сети: Sepolia, Holesky, Arbitrum Sepolia, Base Sepolia\n`;
|
||||
|
||||
if (deployedAddresses.length > 0) {
|
||||
successMessage += `\n🌐 Задеплоенные адреса:\n`;
|
||||
deployedAddresses.forEach((addr, index) => {
|
||||
successMessage += `${index + 1}. ${addr.network}: ${addr.address}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
successMessage += `\n📝 Следующий шаг: Создайте предложение для добавления модуля в DLE через governance.`;
|
||||
|
||||
alert(successMessage);
|
||||
|
||||
// Перенаправляем обратно к модулям
|
||||
setTimeout(() => {
|
||||
router.push(`/management/modules?address=${dleAddress.value}`);
|
||||
}, 3000);
|
||||
|
||||
} else {
|
||||
throw new Error(result.error || 'Неизвестная ошибка');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[TreasuryModuleDeployView] Ошибка деплоя:', error);
|
||||
alert('❌ Ошибка деплоя: ' + error.message);
|
||||
|
||||
deploymentProgress.value = {
|
||||
message: 'Ошибка деплоя: ' + error.message,
|
||||
percentage: 0
|
||||
};
|
||||
} finally {
|
||||
isDeploying.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
console.log('[TreasuryModuleDeployView] Страница загружена');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.treasury-module-deploy {
|
||||
padding: 20px;
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 10px 0 0 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
margin-top: 10px !important;
|
||||
font-family: monospace;
|
||||
background: #f8f9fa;
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Форма деплоя */
|
||||
.deploy-form {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.form-header h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.form-header p {
|
||||
margin: 0 0 20px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.networks-info,
|
||||
.module-settings {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Секция администратора */
|
||||
.admin-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: #fff3cd;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.admin-section h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #856404;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Простая информация */
|
||||
.simple-info {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.simple-info h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.info-text ul {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.info-text li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.token-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.token-input-wrapper {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.token-input {
|
||||
width: 100%;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.token-input.is-valid {
|
||||
border-color: #28a745;
|
||||
box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.1);
|
||||
}
|
||||
|
||||
.token-input.is-invalid {
|
||||
border-color: #dc3545;
|
||||
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.1);
|
||||
}
|
||||
|
||||
.validation-icon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.validation-icon.valid {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.validation-icon.invalid {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.validation-icon.loading {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.remove-token {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #dc3545;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.remove-token:hover {
|
||||
background: #c82333;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.remove-token:disabled {
|
||||
background: #6c757d;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.add-token {
|
||||
margin-top: 15px;
|
||||
width: 100%;
|
||||
padding: 12px 20px;
|
||||
border: 2px dashed #28a745;
|
||||
background: #f8f9fa;
|
||||
color: #28a745;
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.add-token:hover {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border-color: #28a745;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(40, 167, 69, 0.2);
|
||||
}
|
||||
|
||||
/* Сообщение валидации */
|
||||
.form-validation-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 15px;
|
||||
padding: 10px 15px;
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: var(--radius-sm);
|
||||
color: #856404;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-validation-info i {
|
||||
color: #f39c12;
|
||||
}
|
||||
|
||||
/* Дополнительные настройки */
|
||||
.advanced-settings {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.advanced-settings h5 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.advanced-settings .form-row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.advanced-settings .form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.advanced-settings .form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.networks-info h4,
|
||||
.deploy-parameters h4 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.networks-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.network-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.network-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.network-chain-id {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.parameter-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.parameter-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.parameter-item label {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.parameter-value {
|
||||
font-family: monospace;
|
||||
color: var(--color-primary);
|
||||
background: #f8f9fa;
|
||||
padding: 4px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.deploy-actions {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
padding: 16px 32px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.deployment-progress {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress-percentage {
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Информация о модуле */
|
||||
.module-info {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.info-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.info-item strong {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Плейсхолдер для формы */
|
||||
.deploy-form-placeholder {
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
border: 2px dashed #dee2e6;
|
||||
}
|
||||
|
||||
.placeholder-content h3 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.placeholder-content p {
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Сообщение об отсутствии прав доступа */
|
||||
.no-access-message {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message-content h3 {
|
||||
color: #856404;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.message-content p {
|
||||
color: #856404;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message-content .btn {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.message-content .btn:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user