mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-01-13 01:02:46 +00:00
feat: New structure panel, new theme
This commit is contained in:
parent
beac0cdf67
commit
fa7759280b
9 changed files with 157 additions and 12 deletions
2
web/assets/css/custom.min.css
vendored
2
web/assets/css/custom.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -7,7 +7,8 @@
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content :style="{ padding: '24px 16px' }">
|
<a-layout-content :style="{ padding: '24px 16px' }">
|
||||||
<a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
<a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-if="loadingStates.fetched">
|
<transition name="list" appear>
|
||||||
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-if="loadingStates.fetched">
|
||||||
<a-col>
|
<a-col>
|
||||||
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
||||||
<h2>{{ i18n "pages.clients.title" }}</h2>
|
<h2>{{ i18n "pages.clients.title" }}</h2>
|
||||||
|
|
@ -133,6 +134,7 @@
|
||||||
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
</transition>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<a-theme-switch></a-theme-switch>
|
<a-theme-switch></a-theme-switch>
|
||||||
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
|
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
|
||||||
@click="({key}) => openLink(key)">
|
@click="({key}) => openLink(key)">
|
||||||
<a-menu-item v-for="tab in tabs" :key="tab.key">
|
<a-menu-item v-for="tab in tabs" :key="tab.key" :data-menu-key="tab.key">
|
||||||
<a-icon :type="tab.icon"></a-icon>
|
<a-icon :type="tab.icon"></a-icon>
|
||||||
<span v-text="tab.title"></span>
|
<span v-text="tab.title"></span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<a-theme-switch></a-theme-switch>
|
<a-theme-switch></a-theme-switch>
|
||||||
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
|
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
|
||||||
@click="({key}) => openLink(key)">
|
@click="({key}) => openLink(key)">
|
||||||
<a-menu-item v-for="tab in tabs" :key="tab.key">
|
<a-menu-item v-for="tab in tabs" :key="tab.key" :data-menu-key="tab.key">
|
||||||
<a-icon :type="tab.icon"></a-icon>
|
<a-icon :type="tab.icon"></a-icon>
|
||||||
<span v-text="tab.title"></span>
|
<span v-text="tab.title"></span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
|
<a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
|
||||||
<span>{{ i18n "menu.dark" }}</span>
|
<span>{{ i18n "menu.dark" }}</span>
|
||||||
<a-switch :style="{ marginLeft: '2px' }" size="small" :default-checked="themeSwitcher.isDarkTheme"
|
<a-switch :style="{ marginLeft: '2px' }" size="small" :default-checked="themeSwitcher.isDarkTheme"
|
||||||
@change="themeSwitcher.toggleTheme()"></a-switch>
|
:disabled="themeSwitcher.isGlassMorphism" @change="themeSwitcher.toggleTheme()"></a-switch>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
|
<a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
|
||||||
@mousedown="themeSwitcher.animationsOffUltra()">
|
@mousedown="themeSwitcher.animationsOffUltra()">
|
||||||
|
|
@ -17,6 +17,12 @@
|
||||||
<a-checkbox :style="{ marginLeft: '2px' }" :checked="themeSwitcher.isUltra"
|
<a-checkbox :style="{ marginLeft: '2px' }" :checked="themeSwitcher.isUltra"
|
||||||
@click="themeSwitcher.toggleUltra()"></a-checkbox>
|
@click="themeSwitcher.toggleUltra()"></a-checkbox>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
<a-menu-item id="change-theme-glass" class="ant-menu-theme-switch"
|
||||||
|
@mousedown="themeSwitcher.animationsOffGlass()">
|
||||||
|
<span>{{ i18n "menu.glassMorphism" }}</span>
|
||||||
|
<a-switch :style="{ marginLeft: '2px' }" size="small" :default-checked="themeSwitcher.isGlassMorphism"
|
||||||
|
@change="themeSwitcher.toggleGlassMorphism()"></a-switch>
|
||||||
|
</a-menu-item>
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -26,13 +32,17 @@
|
||||||
<template>
|
<template>
|
||||||
<a-space @mousedown="themeSwitcher.animationsOff()" id="change-theme" direction="vertical" :size="10" :style="{ width: '100%' }">
|
<a-space @mousedown="themeSwitcher.animationsOff()" id="change-theme" direction="vertical" :size="10" :style="{ width: '100%' }">
|
||||||
<a-space direction="horizontal" size="small">
|
<a-space direction="horizontal" size="small">
|
||||||
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
|
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme" :disabled="themeSwitcher.isGlassMorphism" @change="themeSwitcher.toggleTheme()"></a-switch>
|
||||||
<span>{{ i18n "menu.dark" }}</span>
|
<span>{{ i18n "menu.dark" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-space v-if="themeSwitcher.isDarkTheme" direction="horizontal" size="small">
|
<a-space v-if="themeSwitcher.isDarkTheme" direction="horizontal" size="small">
|
||||||
<a-checkbox :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
|
<a-checkbox :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
|
||||||
<span>{{ i18n "menu.ultraDark" }}</span>
|
<span>{{ i18n "menu.ultraDark" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
<a-space direction="horizontal" size="small">
|
||||||
|
<a-switch size="small" :default-checked="themeSwitcher.isGlassMorphism" @change="themeSwitcher.toggleGlassMorphism()"></a-switch>
|
||||||
|
<span>{{ i18n "menu.glassMorphism" }}</span>
|
||||||
|
</a-space>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
@ -40,10 +50,34 @@
|
||||||
{{define "component/aThemeSwitch"}}
|
{{define "component/aThemeSwitch"}}
|
||||||
<script>
|
<script>
|
||||||
function createThemeSwitcher() {
|
function createThemeSwitcher() {
|
||||||
const isDarkTheme = localStorage.getItem('dark-mode') === 'true';
|
let isDarkTheme = localStorage.getItem('dark-mode') === 'true';
|
||||||
const isUltra = localStorage.getItem('isUltraDarkThemeEnabled') === 'true';
|
let isUltra = localStorage.getItem('isUltraDarkThemeEnabled') === 'true';
|
||||||
if (isUltra) {
|
// Glass Morphism включен по умолчанию, если не установлено явно
|
||||||
document.documentElement.setAttribute('data-theme', 'ultra-dark');
|
let isGlassMorphism = localStorage.getItem('isGlassMorphismEnabled');
|
||||||
|
if (isGlassMorphism === null) {
|
||||||
|
isGlassMorphism = true; // По умолчанию включен
|
||||||
|
localStorage.setItem('isGlassMorphismEnabled', 'true');
|
||||||
|
} else {
|
||||||
|
isGlassMorphism = isGlassMorphism === 'true';
|
||||||
|
}
|
||||||
|
// Если включен Glass Morphism, отключаем темную тему
|
||||||
|
if (isGlassMorphism) {
|
||||||
|
isDarkTheme = false;
|
||||||
|
isUltra = false;
|
||||||
|
localStorage.setItem('dark-mode', 'false');
|
||||||
|
localStorage.setItem('isUltraDarkThemeEnabled', 'false');
|
||||||
|
document.documentElement.setAttribute('data-glass-morphism', 'true');
|
||||||
|
document.documentElement.removeAttribute('data-theme');
|
||||||
|
} else {
|
||||||
|
// Если включена темная тема, отключаем Glass Morphism
|
||||||
|
if (isDarkTheme) {
|
||||||
|
isGlassMorphism = false;
|
||||||
|
localStorage.setItem('isGlassMorphismEnabled', 'false');
|
||||||
|
document.documentElement.removeAttribute('data-glass-morphism');
|
||||||
|
}
|
||||||
|
if (isUltra) {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'ultra-dark');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const theme = isDarkTheme ? 'dark' : 'light';
|
const theme = isDarkTheme ? 'dark' : 'light';
|
||||||
document.querySelector('body').setAttribute('class', theme);
|
document.querySelector('body').setAttribute('class', theme);
|
||||||
|
|
@ -68,13 +102,33 @@
|
||||||
document.documentElement.removeAttribute('data-theme-animations');
|
document.documentElement.removeAttribute('data-theme-animations');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
animationsOffGlass() {
|
||||||
|
document.documentElement.setAttribute('data-theme-animations', 'off');
|
||||||
|
const themeAnimationsGlass = document.querySelector('#change-theme-glass');
|
||||||
|
themeAnimationsGlass.addEventListener('mouseleave', () => {
|
||||||
|
document.documentElement.removeAttribute('data-theme-animations');
|
||||||
|
});
|
||||||
|
themeAnimationsGlass.addEventListener('touchend', () => {
|
||||||
|
document.documentElement.removeAttribute('data-theme-animations');
|
||||||
|
});
|
||||||
|
},
|
||||||
isDarkTheme,
|
isDarkTheme,
|
||||||
isUltra,
|
isUltra,
|
||||||
|
isGlassMorphism,
|
||||||
get currentTheme() {
|
get currentTheme() {
|
||||||
return this.isDarkTheme ? 'dark' : 'light';
|
return this.isDarkTheme ? 'dark' : 'light';
|
||||||
},
|
},
|
||||||
toggleTheme() {
|
toggleTheme() {
|
||||||
|
if (this.isGlassMorphism) {
|
||||||
|
return; // Не позволяем включать темную тему когда включен Glass Morphism
|
||||||
|
}
|
||||||
this.isDarkTheme = !this.isDarkTheme;
|
this.isDarkTheme = !this.isDarkTheme;
|
||||||
|
if (this.isDarkTheme) {
|
||||||
|
// Если включаем темную тему, отключаем Glass Morphism
|
||||||
|
this.isGlassMorphism = false;
|
||||||
|
document.documentElement.removeAttribute('data-glass-morphism');
|
||||||
|
localStorage.setItem('isGlassMorphismEnabled', 'false');
|
||||||
|
}
|
||||||
localStorage.setItem('dark-mode', this.isDarkTheme);
|
localStorage.setItem('dark-mode', this.isDarkTheme);
|
||||||
document.querySelector('body').setAttribute('class', this.isDarkTheme ? 'dark' : 'light');
|
document.querySelector('body').setAttribute('class', this.isDarkTheme ? 'dark' : 'light');
|
||||||
document.getElementById('message').className = themeSwitcher.currentTheme;
|
document.getElementById('message').className = themeSwitcher.currentTheme;
|
||||||
|
|
@ -87,6 +141,23 @@
|
||||||
document.documentElement.removeAttribute('data-theme');
|
document.documentElement.removeAttribute('data-theme');
|
||||||
}
|
}
|
||||||
localStorage.setItem('isUltraDarkThemeEnabled', this.isUltra.toString());
|
localStorage.setItem('isUltraDarkThemeEnabled', this.isUltra.toString());
|
||||||
|
},
|
||||||
|
toggleGlassMorphism() {
|
||||||
|
this.isGlassMorphism = !this.isGlassMorphism;
|
||||||
|
if (this.isGlassMorphism) {
|
||||||
|
// Если включаем Glass Morphism, отключаем темную тему
|
||||||
|
this.isDarkTheme = false;
|
||||||
|
document.querySelector('body').setAttribute('class', 'light');
|
||||||
|
document.documentElement.removeAttribute('data-theme');
|
||||||
|
this.isUltra = false;
|
||||||
|
localStorage.setItem('dark-mode', 'false');
|
||||||
|
localStorage.setItem('isUltraDarkThemeEnabled', 'false');
|
||||||
|
document.documentElement.setAttribute('data-glass-morphism', 'true');
|
||||||
|
document.getElementById('message').className = 'light';
|
||||||
|
} else {
|
||||||
|
document.documentElement.removeAttribute('data-glass-morphism');
|
||||||
|
}
|
||||||
|
localStorage.setItem('isGlassMorphismEnabled', this.isGlassMorphism.toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content :style="{ padding: '24px 16px' }">
|
<a-layout-content :style="{ padding: '24px 16px' }">
|
||||||
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-if="loadingStates.fetched && multiNodeMode">
|
<transition name="list" appear>
|
||||||
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-if="loadingStates.fetched && multiNodeMode">
|
||||||
<a-col>
|
<a-col>
|
||||||
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
||||||
<h2>{{ i18n "pages.hosts.title" }}</h2>
|
<h2>{{ i18n "pages.hosts.title" }}</h2>
|
||||||
|
|
@ -69,6 +70,7 @@
|
||||||
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
</transition>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,8 @@
|
||||||
this.loadingStates.spinning = true;
|
this.loadingStates.spinning = true;
|
||||||
const msg = await HttpUtil.post('/login', this.user);
|
const msg = await HttpUtil.post('/login', this.user);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
|
// Устанавливаем флаг для показа popup "Что нового?" после логина
|
||||||
|
sessionStorage.setItem('showWhatsNew', 'true');
|
||||||
location.href = basePath + 'panel/';
|
location.href = basePath + 'panel/';
|
||||||
}
|
}
|
||||||
this.loadingStates.spinning = false;
|
this.loadingStates.spinning = false;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content :style="{ padding: '24px 16px' }">
|
<a-layout-content :style="{ padding: '24px 16px' }">
|
||||||
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-if="loadingStates.fetched">
|
<transition name="list" appear>
|
||||||
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-if="loadingStates.fetched">
|
||||||
<a-col>
|
<a-col>
|
||||||
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
||||||
<h2>{{ i18n "pages.nodes.title" }}</h2>
|
<h2>{{ i18n "pages.nodes.title" }}</h2>
|
||||||
|
|
@ -95,6 +96,7 @@
|
||||||
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
</transition>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
|
|
|
||||||
|
|
@ -740,6 +740,7 @@
|
||||||
"theme" = "Theme"
|
"theme" = "Theme"
|
||||||
"dark" = "Dark"
|
"dark" = "Dark"
|
||||||
"ultraDark" = "Ultra Dark"
|
"ultraDark" = "Ultra Dark"
|
||||||
|
"glassMorphism" = "Glass Morphism"
|
||||||
"dashboard" = "Overview"
|
"dashboard" = "Overview"
|
||||||
"inbounds" = "Inbounds"
|
"inbounds" = "Inbounds"
|
||||||
"clients" = "Clients"
|
"clients" = "Clients"
|
||||||
|
|
@ -749,6 +750,38 @@
|
||||||
"hosts" = "Hosts"
|
"hosts" = "Hosts"
|
||||||
"logout" = "Log Out"
|
"logout" = "Log Out"
|
||||||
"link" = "Manage"
|
"link" = "Manage"
|
||||||
|
"tutorial" = "Tutorial"
|
||||||
|
"restartTutorial" = "Restart Tutorial"
|
||||||
|
|
||||||
|
[tutorial]
|
||||||
|
"title" = "Web Panel Menu Guide"
|
||||||
|
"next" = "Next"
|
||||||
|
"prev" = "Previous"
|
||||||
|
"skip" = "Skip"
|
||||||
|
"finish" = "Finish"
|
||||||
|
"step" = "Step"
|
||||||
|
"of" = "of"
|
||||||
|
"dashboardTitle" = "1. Panel"
|
||||||
|
"dashboardDesc" = "Main interface for managing the entire system. Through the panel we:\n\n• Configure and control nodes (servers with xray)\n• Manage clients\n• Configure inbounds"
|
||||||
|
"dashboardHint" = "Panel is the control center. Here you create nodes, inbounds and assign clients."
|
||||||
|
"nodeTitle" = "2. Node"
|
||||||
|
"nodeDesc" = "Separate Xray core with API for communication with the panel. The node handles connections and serves as the 'brain' for inbounds."
|
||||||
|
"nodeHint" = "Node is the server core. The panel communicates with it via API to manage configs and connections."
|
||||||
|
"inboundTitle" = "3. Inbound"
|
||||||
|
"inboundDesc" = "Configuration or profile for a node.\n\n• Creates a connection to a node\n• Subscribes to one or more nodes"
|
||||||
|
"inboundHint" = "Inbound is a connection profile. Through it, clients get access to the required nodes."
|
||||||
|
"clientTitle" = "4. Client"
|
||||||
|
"clientDesc" = "System user who can be assigned one or more inbounds.\n\nFor example:\n• Inbound 1 → whitelist node\n• Inbound 2 → regular foreign server\n\nClient can use any or all inbounds assigned to them"
|
||||||
|
"clientHint" = "Client is a user. Assign inbounds to them so they can connect to the required nodes."
|
||||||
|
"hostTitle" = "5. Hosts"
|
||||||
|
"hostDesc" = "External addresses for connection.\n\n• Proxy balancer that hides direct node addresses\n• Can distribute load between multiple nodes\n• Replace node address with the required host in inbound\n\nExample:\nInbound connects to a balancer host, which then distributes the connection to real nodes"
|
||||||
|
"hostHint" = "Hosts are virtual addresses. Use them for load balancing and hiding real servers."
|
||||||
|
"settingsTitle" = "6. Panel Settings"
|
||||||
|
"settingsDesc" = "Section for general panel configuration.\n\n• Enable/disable various features\n• Configure panel appearance and behavior"
|
||||||
|
"settingsHint" = "Panel Settings — here you can manage features and panel configuration."
|
||||||
|
"xrayTitle" = "7. Xray Configuration"
|
||||||
|
"xrayDesc" = "Section for fine-tuning Xray core.\n\n• Routing\n• Connection parameters and traffic routing\n• Additional advanced node configs"
|
||||||
|
"xrayHint" = "Xray Configuration — for advanced core configuration, routing management and other node parameters."
|
||||||
|
|
||||||
[pages.settings.toasts]
|
[pages.settings.toasts]
|
||||||
"modifySettings" = "The parameters have been changed."
|
"modifySettings" = "The parameters have been changed."
|
||||||
|
|
|
||||||
|
|
@ -740,6 +740,7 @@
|
||||||
"theme" = "Тема"
|
"theme" = "Тема"
|
||||||
"dark" = "Темная"
|
"dark" = "Темная"
|
||||||
"ultraDark" = "Очень темная"
|
"ultraDark" = "Очень темная"
|
||||||
|
"glassMorphism" = "Glass Morphism"
|
||||||
"dashboard" = "Обзор"
|
"dashboard" = "Обзор"
|
||||||
"inbounds" = "Подключения"
|
"inbounds" = "Подключения"
|
||||||
"clients" = "Клиенты"
|
"clients" = "Клиенты"
|
||||||
|
|
@ -749,6 +750,38 @@
|
||||||
"hosts" = "Хосты"
|
"hosts" = "Хосты"
|
||||||
"logout" = "Выйти"
|
"logout" = "Выйти"
|
||||||
"link" = "Управление"
|
"link" = "Управление"
|
||||||
|
"tutorial" = "Обучалка"
|
||||||
|
"restartTutorial" = "Повторить обучение"
|
||||||
|
|
||||||
|
[tutorial]
|
||||||
|
"title" = "Инструкция по меню веб-панели"
|
||||||
|
"next" = "Далее"
|
||||||
|
"prev" = "Назад"
|
||||||
|
"skip" = "Пропустить"
|
||||||
|
"finish" = "Завершить"
|
||||||
|
"step" = "Шаг"
|
||||||
|
"of" = "из"
|
||||||
|
"dashboardTitle" = "1. Панель"
|
||||||
|
"dashboardDesc" = "Главный интерфейс для управления всей системой. Через панель мы:\n\n• Настраиваем и контролируем ноды (серверы с xray)\n• Управляем клиентами\n• Настраиваем инбаунды"
|
||||||
|
"dashboardHint" = "Панель — это центр управления. Здесь создаются ноды, инбаунды и назначаются клиенты."
|
||||||
|
"nodeTitle" = "2. Нода"
|
||||||
|
"nodeDesc" = "Отдельное ядро Xray с API для связи с панелью. Нода выполняет работу по обработке подключений и служит «мозгом» для инбаундов."
|
||||||
|
"nodeHint" = "Нода — это серверное ядро. Панель взаимодействует с ним через API, чтобы управлять конфигами и подключениями."
|
||||||
|
"inboundTitle" = "3. Инбаунд"
|
||||||
|
"inboundDesc" = "Конфигурация или профиль для ноды.\n\n• Создаётся подключение к ноде\n• Подписывается на одну или несколько нод"
|
||||||
|
"inboundHint" = "Инбаунд — это профиль подключения. Через него клиенты получают доступ к нужным нодам."
|
||||||
|
"clientTitle" = "4. Клиент"
|
||||||
|
"clientDesc" = "Пользователь системы, которому можно назначать один или несколько инбаундов.\n\nНапример:\n• Инбаунд 1 → нода из белого списка\n• Инбаунд 2 → обычный забугорный сервер\n\nКлиент может использовать любой или все инбаунды, которые ему назначены"
|
||||||
|
"clientHint" = "Клиент — это пользователь. Назначайте ему инбаунды, чтобы он мог подключаться к нужным нодам."
|
||||||
|
"hostTitle" = "5. Хосты"
|
||||||
|
"hostDesc" = "Внешние адреса для подключения.\n\n• Прокси-балансир, скрывающий прямые адреса нод\n• Можно распределять нагрузку между несколькими нодами\n• Подменяем адрес ноды на нужный хост в инбаунде\n\nПример:\nИнбаунд подключается к хосту-балансиру, а тот уже распределяет подключение на реальные ноды"
|
||||||
|
"hostHint" = "Хосты — это виртуальные адреса. Используйте их для балансировки нагрузки и скрытия реальных серверов."
|
||||||
|
"settingsTitle" = "6. Настройки панели"
|
||||||
|
"settingsDesc" = "Раздел для общей конфигурации панели.\n\n• Включение/отключение различных функций\n• Настройка внешнего вида и поведения панели"
|
||||||
|
"settingsHint" = "Настройки панели — здесь вы можете управлять функциями и конфигурацией самой панели."
|
||||||
|
"xrayTitle" = "7. Конфигурация Xray"
|
||||||
|
"xrayDesc" = "Раздел для тонкой настройки ядра Xray.\n\n• Роутинг\n• Параметры соединений и маршрутизации трафика\n• Дополнительные продвинутые конфиги ноды"
|
||||||
|
"xrayHint" = "Конфигурация Xray — для продвинутой конфигурации ядра, управления роутингом и другими параметрами ноды."
|
||||||
|
|
||||||
[pages.settings.toasts]
|
[pages.settings.toasts]
|
||||||
"modifySettings" = "Настройки изменены"
|
"modifySettings" = "Настройки изменены"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue